• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidcole1340 / ext-php-rs / 16006197694

01 Jul 2025 05:33PM UTC coverage: 21.654% (-0.3%) from 21.978%
16006197694

Pull #471

github

web-flow
Merge 7af465c1e into 68e218f9b
Pull Request #471: feat(sapi): expand `SapiBuilder`

66 of 69 new or added lines in 1 file covered. (95.65%)

748 existing lines in 21 files now uncovered.

830 of 3833 relevant lines covered (21.65%)

3.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.47
/src/builders/sapi.rs
1
use crate::embed::ext_php_rs_php_error;
2
use crate::ffi::{
3
    gid_t, php_default_input_filter, php_default_post_reader, php_default_treat_data,
4
    sapi_header_struct, uid_t,
5
};
6
use crate::types::Zval;
7
use crate::{embed::SapiModule, error::Result};
8

9
use std::ffi::{c_char, c_int, c_void};
10
use std::{ffi::CString, ptr};
11

12
/// Builder for `SapiModule`s
13
///
14
/// # Example
15
///
16
/// ```rust,no_run
17
/// use std::ffi::{c_char, c_int, c_void};
18
/// use ext_php_rs::{
19
///     builders::SapiBuilder,
20
///     ffi::sapi_header_struct
21
/// };
22
///
23
/// #[no_mangle]
24
/// pub extern "C" fn ub_write(str: *const i8, str_length: usize) -> usize {
25
///     println!("PHP wrote: {:?}", str);
26
///     str_length
27
/// }
28
///
29
/// #[no_mangle]
30
/// pub extern "C" fn send_header(header: *mut sapi_header_struct, server_context: *mut c_void) {
31
///     println!("PHP sent a header: {:?}", header);
32
/// }
33
///
34
/// let sapi = SapiBuilder::new("ext_php_rs", "Ext PHP RS")
35
///   .ub_write_function(ub_write)
36
///   .send_header_function(send_header)
37
///   .build();
38
/// ```
39
#[must_use]
40
pub struct SapiBuilder {
41
    name: String,
42
    pretty_name: String,
43
    module: SapiModule,
44
    executable_location: Option<String>,
45
    php_ini_path_override: Option<String>,
46
}
47

48
impl SapiBuilder {
49
    /// Creates a new [`SapiBuilder`] instance
50
    ///
51
    /// # Parameters
52
    ///
53
    /// * `name` - The name of the SAPI module.
54
    pub fn new<T: Into<String>, U: Into<String>>(name: T, pretty_name: U) -> Self {
24✔
55
        Self {
56
            name: name.into(),
72✔
57
            pretty_name: pretty_name.into(),
72✔
58
            module: SapiModule {
48✔
59
                name: ptr::null_mut(),
60
                pretty_name: ptr::null_mut(),
61
                startup: None,
62
                shutdown: None,
63
                activate: None,
64
                deactivate: None,
65
                ub_write: None,
66
                flush: None,
67
                get_stat: None,
68
                getenv: None,
69
                sapi_error: None,
70
                header_handler: None,
71
                send_headers: None,
72
                send_header: None,
73
                read_post: None,
74
                read_cookies: None,
75
                register_server_variables: None,
76
                log_message: None,
77
                get_request_time: None,
78
                terminate_process: None,
79
                php_ini_path_override: ptr::null_mut(),
80
                default_post_reader: None,
81
                treat_data: None,
82
                executable_location: ptr::null_mut(),
83
                php_ini_ignore: 0,
84
                php_ini_ignore_cwd: 0,
85
                get_fd: None,
86
                force_http_10: None,
87
                get_target_uid: None,
88
                get_target_gid: None,
89
                input_filter: None,
90
                ini_defaults: None,
91
                phpinfo_as_text: 0,
92
                ini_entries: ptr::null_mut(),
93
                additional_functions: ptr::null(),
94
                input_filter_init: None,
95
            },
96
            executable_location: None,
97
            php_ini_path_override: None,
98
        }
99
    }
100

101
    /// Sets the startup function for this SAPI
102
    ///
103
    /// # Parameters
104
    ///
105
    /// * `func` - The function to be called on startup.
106
    pub fn startup_function(mut self, func: SapiStartupFunc) -> Self {
2✔
107
        self.module.startup = Some(func);
2✔
108
        self
2✔
109
    }
110

111
    /// Sets the shutdown function for this SAPI
112
    ///
113
    /// # Parameters
114
    ///
115
    /// * `func` - The function to be called on shutdown.
116
    pub fn shutdown_function(mut self, func: SapiShutdownFunc) -> Self {
2✔
117
        self.module.shutdown = Some(func);
2✔
118
        self
2✔
119
    }
120

121
    /// Sets the activate function for this SAPI
122
    ///
123
    /// # Parameters
124
    ///
125
    /// * `func` - The function to be called on activation.
126
    pub fn activate_function(mut self, func: SapiActivateFunc) -> Self {
2✔
127
        self.module.activate = Some(func);
2✔
128
        self
2✔
129
    }
130

131
    /// Sets the deactivate function for this SAPI
132
    ///
133
    /// # Parameters
134
    ///
135
    /// * `func` - The function to be called on deactivation.
136
    pub fn deactivate_function(mut self, func: SapiDeactivateFunc) -> Self {
2✔
137
        self.module.deactivate = Some(func);
2✔
138
        self
2✔
139
    }
140

141
    /// Sets the `ub_write` function for this SAPI
142
    ///
143
    /// # Parameters
144
    ///
145
    /// * `func` - The function to be called on write.
146
    pub fn ub_write_function(mut self, func: SapiUbWriteFunc) -> Self {
3✔
147
        self.module.ub_write = Some(func);
3✔
148
        self
3✔
149
    }
150

151
    /// Set the flush function for this SAPI
152
    ///
153
    /// # Parameters
154
    ///
155
    /// * `func` - The function to be called on flush.
156
    pub fn flush_function(mut self, func: SapiFlushFunc) -> Self {
2✔
157
        self.module.flush = Some(func);
2✔
158
        self
2✔
159
    }
160

161
    /// Sets the get env function for this SAPI
162
    ///
163
    /// # Parameters
164
    ///
165
    /// * `func` - The function to be called when PHP gets an environment variable.
166
    pub fn getenv_function(mut self, func: SapiGetEnvFunc) -> Self {
1✔
167
        self.module.getenv = Some(func);
1✔
168
        self
1✔
169
    }
170

171
    /// Sets the sapi error function for this SAPI
172
    ///
173
    /// # Parameters
174
    ///
175
    /// * `func` - The function to be called when PHP encounters an error.
NEW
176
    pub fn sapi_error_function(mut self, func: SapiErrorFunc) -> Self {
×
NEW
177
        self.module.sapi_error = Some(func);
×
NEW
178
        self
×
179
    }
180

181
    /// Sets the send header function for this SAPI
182
    ///
183
    /// # Arguments
184
    ///
185
    /// * `func` - The function to be called on shutdown.
186
    pub fn send_header_function(mut self, func: SapiSendHeaderFunc) -> Self {
1✔
187
        self.module.send_header = Some(func);
1✔
188
        self
1✔
189
    }
190

191
    /// Sets the read post function for this SAPI
192
    ///
193
    /// # Parameters
194
    ///
195
    /// * `func` - The function to be called when PHP reads the POST data.
196
    pub fn read_post_function(mut self, func: SapiReadPostFunc) -> Self {
1✔
197
        self.module.read_post = Some(func);
1✔
198
        self
1✔
199
    }
200

201
    /// Sets the read cookies function for this SAPI
202
    ///
203
    /// # Parameters
204
    ///
205
    /// * `func` - The function to be called when PHP reads the cookies.
206
    pub fn read_cookies_function(mut self, func: SapiReadCookiesFunc) -> Self {
1✔
207
        self.module.read_cookies = Some(func);
1✔
208
        self
1✔
209
    }
210

211
    /// Sets the register server variables function for this SAPI
212
    ///
213
    /// # Parameters
214
    ///
215
    /// * `func` - The function to be called when PHP registers server variables.
216
    pub fn register_server_variables_function(
1✔
217
        mut self,
218
        func: SapiRegisterServerVariablesFunc,
219
    ) -> Self {
220
        self.module.register_server_variables = Some(func);
1✔
221
        self
1✔
222
    }
223

224
    /// Sets the log message function for this SAPI
225
    ///
226
    /// # Parameters
227
    ///
228
    /// * `func` - The function to be called when PHP logs a message.
229
    pub fn log_message_function(mut self, func: SapiLogMessageFunc) -> Self {
1✔
230
        self.module.log_message = Some(func);
1✔
231
        self
1✔
232
    }
233

234
    /// Sets the request time function for this SAPI
235
    ///
236
    /// # Parameters
237
    ///
238
    /// * `func` - The function to be called when PHP gets the request time.
239
    pub fn get_request_time_function(mut self, func: SapiRequestTimeFunc) -> Self {
1✔
240
        self.module.get_request_time = Some(func);
1✔
241
        self
1✔
242
    }
243

244
    /// Sets the terminate process function for this SAPI
245
    ///
246
    /// # Parameters
247
    ///
248
    /// * `func` - The function to be called when PHP terminates the process.
249
    pub fn terminate_process_function(mut self, func: SapiTerminateProcessFunc) -> Self {
1✔
250
        self.module.terminate_process = Some(func);
1✔
251
        self
1✔
252
    }
253

254
    /// Sets the get uid function for this SAPI
255
    ///
256
    /// # Parameters
257
    ///
258
    /// * `func` - The function to be called when PHP gets the uid.
259
    pub fn get_target_uid_function(mut self, func: SapiGetUidFunc) -> Self {
1✔
260
        self.module.get_target_uid = Some(func);
1✔
261
        self
1✔
262
    }
263

264
    /// Sets the get gid function for this SAPI
265
    ///
266
    /// # Parameters
267
    ///
268
    /// * `func` - The function to be called when PHP gets the gid.
269
    pub fn get_target_gid_function(mut self, func: SapiGetGidFunc) -> Self {
1✔
270
        self.module.get_target_gid = Some(func);
1✔
271
        self
1✔
272
    }
273

274
    /// Sets the php ini path override for this SAPI
275
    ///
276
    /// # Parameters
277
    ///
278
    /// * `path` - The path to the php ini file.
279
    pub fn php_ini_path_override<S: Into<String>>(mut self, path: S) -> Self {
2✔
280
        self.php_ini_path_override = Some(path.into());
4✔
281
        self
2✔
282
    }
283

284
    /// Sets the php ini ignore for this SAPI
285
    ///
286
    /// # Parameters
287
    ///
288
    /// * `ignore` - The value to set php ini ignore to.
289
    pub fn php_ini_ignore(mut self, ignore: i32) -> Self {
2✔
290
        self.module.php_ini_ignore = ignore as c_int;
2✔
291
        self
2✔
292
    }
293

294
    /// Sets the php ini ignore cwd for this SAPI
295
    ///
296
    /// # Parameters
297
    ///
298
    /// * `ignore` - The value to set php ini ignore cwd to.
299
    pub fn php_ini_ignore_cwd(mut self, ignore: i32) -> Self {
1✔
300
        self.module.php_ini_ignore_cwd = ignore as c_int;
1✔
301
        self
1✔
302
    }
303

304
    /// Sets the executable location for this SAPI
305
    ///
306
    /// # Parameters
307
    ///
308
    /// * `location` - The location of the executable.
309
    pub fn executable_location<S: Into<String>>(mut self, location: S) -> Self {
2✔
310
        self.executable_location = Some(location.into());
4✔
311
        self
2✔
312
    }
313

314
    /// Builds the extension and returns a `SapiModule`.
315
    ///
316
    /// Returns a result containing the sapi module if successful.
317
    ///
318
    /// # Errors
319
    ///
320
    /// * If name or property name contain null bytes
321
    pub fn build(mut self) -> Result<SapiModule> {
24✔
322
        self.module.name = CString::new(self.name)?.into_raw();
72✔
323
        self.module.pretty_name = CString::new(self.pretty_name)?.into_raw();
24✔
324

325
        if let Some(path) = self.executable_location {
2✔
326
            self.module.executable_location = CString::new(path)?.into_raw();
2✔
327
        }
328

329
        if let Some(path) = self.php_ini_path_override {
26✔
330
            self.module.php_ini_path_override = CString::new(path)?.into_raw();
2✔
331
        }
332

333
        if self.module.send_header.is_none() {
47✔
334
            self.module.send_header = Some(dummy_send_header);
23✔
335
        }
336

337
        if self.module.sapi_error.is_none() {
24✔
338
            self.module.sapi_error = Some(ext_php_rs_php_error);
24✔
339
        }
340

341
        if self.module.default_post_reader.is_none() {
24✔
342
            self.module.default_post_reader = Some(php_default_post_reader);
24✔
343
        }
344

345
        if self.module.treat_data.is_none() {
24✔
346
            self.module.treat_data = Some(php_default_treat_data);
24✔
347
        }
348

349
        if self.module.input_filter.is_none() {
24✔
350
            self.module.input_filter = Some(php_default_input_filter);
24✔
351
        }
352

353
        Ok(self.module)
354
    }
355
}
356

357
/// A function to be called when PHP starts the SAPI
358
pub type SapiStartupFunc = extern "C" fn(sapi: *mut SapiModule) -> c_int;
359

360
/// A function to be called when PHP stops the SAPI
361
pub type SapiShutdownFunc = extern "C" fn(sapi: *mut SapiModule) -> c_int;
362

363
/// A function to be called when PHP activates the SAPI
364
pub type SapiActivateFunc = extern "C" fn() -> c_int;
365

366
/// A function to be called when PHP deactivates the SAPI
367
pub type SapiDeactivateFunc = extern "C" fn() -> c_int;
368

369
/// A function to be called when PHP send a header
370
pub type SapiSendHeaderFunc =
371
    extern "C" fn(header: *mut sapi_header_struct, server_context: *mut c_void);
372

373
/// A function to be called when PHP write to the output buffer
374
pub type SapiUbWriteFunc = extern "C" fn(str: *const c_char, str_length: usize) -> usize;
375

376
/// A function to be called when PHP flush the output buffer
377
pub type SapiFlushFunc = extern "C" fn(*mut c_void);
378

379
/// A function to be called when PHP gets an environment variable
380
pub type SapiGetEnvFunc = extern "C" fn(name: *const c_char, name_length: usize) -> *mut c_char;
381

382
/// A function to be called when PHP encounters an error
383
pub type SapiErrorFunc = extern "C" fn(type_: c_int, error_msg: *const c_char, args: ...);
384

385
/// A function to be called when PHP read the POST data
386
pub type SapiReadPostFunc = extern "C" fn(buffer: *mut c_char, length: usize) -> usize;
387

388
/// A function to be called when PHP read the cookies
389
pub type SapiReadCookiesFunc = extern "C" fn() -> *mut c_char;
390

391
/// A function to be called when PHP register server variables
392
pub type SapiRegisterServerVariablesFunc = extern "C" fn(vars: *mut Zval);
393

394
/// A function to be called when PHP logs a message
395
pub type SapiLogMessageFunc = extern "C" fn(message: *const c_char, syslog_type_int: c_int);
396

397
/// A function to be called when PHP gets the request time
398
pub type SapiRequestTimeFunc = extern "C" fn(time: *mut f64) -> c_int;
399

400
/// A function to be called when PHP terminates the process
401
pub type SapiTerminateProcessFunc = extern "C" fn();
402

403
/// A function to be called when PHP gets the uid
404
pub type SapiGetUidFunc = extern "C" fn(uid: *mut uid_t) -> c_int;
405

406
/// A function to be called when PHP gets the gid
407
pub type SapiGetGidFunc = extern "C" fn(gid: *mut gid_t) -> c_int;
408

409
extern "C" fn dummy_send_header(_header: *mut sapi_header_struct, _server_context: *mut c_void) {}
6✔
410

411
#[cfg(test)]
412
mod test {
413
    use super::*;
414
    use std::ffi::CStr;
415

416
    extern "C" fn test_startup(_sapi: *mut SapiModule) -> c_int {
417
        0
418
    }
419
    extern "C" fn test_shutdown(_sapi: *mut SapiModule) -> c_int {
420
        0
421
    }
422
    extern "C" fn test_activate() -> c_int {
423
        0
424
    }
425
    extern "C" fn test_deactivate() -> c_int {
426
        0
427
    }
428
    extern "C" fn test_ub_write(_str: *const c_char, _str_length: usize) -> usize {
429
        0
430
    }
431
    extern "C" fn test_flush(_server_context: *mut c_void) {}
432
    extern "C" fn test_getenv(_name: *const c_char, _name_length: usize) -> *mut c_char {
433
        ptr::null_mut()
434
    }
435
    // Note: C-variadic functions are unstable in Rust, so we can't test this properly
436
    // extern "C" fn test_sapi_error(_type: c_int, _error_msg: *const c_char, _args: ...) {}
437
    extern "C" fn test_send_header(_header: *mut sapi_header_struct, _server_context: *mut c_void) {
438
    }
439
    extern "C" fn test_read_post(_buffer: *mut c_char, _length: usize) -> usize {
440
        0
441
    }
442
    extern "C" fn test_read_cookies() -> *mut c_char {
443
        ptr::null_mut()
444
    }
445
    extern "C" fn test_register_server_variables(_vars: *mut Zval) {}
446
    extern "C" fn test_log_message(_message: *const c_char, _syslog_type_int: c_int) {}
447
    extern "C" fn test_get_request_time(_time: *mut f64) -> c_int {
448
        0
449
    }
450
    extern "C" fn test_terminate_process() {}
451
    extern "C" fn test_get_target_uid(_uid: *mut uid_t) -> c_int {
452
        0
453
    }
454
    extern "C" fn test_get_target_gid(_gid: *mut gid_t) -> c_int {
455
        0
456
    }
457

458
    #[test]
459
    fn test_basic_sapi_builder() {
460
        let sapi = SapiBuilder::new("test_sapi", "Test SAPI")
461
            .build()
462
            .expect("should build sapi module");
463

464
        assert_eq!(
465
            unsafe { CStr::from_ptr(sapi.name) }
466
                .to_str()
467
                .expect("should convert CStr to str"),
468
            "test_sapi"
469
        );
470
        assert_eq!(
471
            unsafe { CStr::from_ptr(sapi.pretty_name) }
472
                .to_str()
473
                .expect("should convert CStr to str"),
474
            "Test SAPI"
475
        );
476
    }
477

478
    #[test]
479
    fn test_startup_function() {
480
        let sapi = SapiBuilder::new("test", "Test")
481
            .startup_function(test_startup)
482
            .build()
483
            .expect("should build sapi module");
484

485
        assert!(sapi.startup.is_some());
486
        assert_eq!(
487
            sapi.startup.expect("should have startup function") as usize,
488
            test_startup as usize
489
        );
490
    }
491

492
    #[test]
493
    fn test_shutdown_function() {
494
        let sapi = SapiBuilder::new("test", "Test")
495
            .shutdown_function(test_shutdown)
496
            .build()
497
            .expect("should build sapi module");
498

499
        assert!(sapi.shutdown.is_some());
500
        assert_eq!(
501
            sapi.shutdown.expect("should have shutdown function") as usize,
502
            test_shutdown as usize
503
        );
504
    }
505

506
    #[test]
507
    fn test_activate_function() {
508
        let sapi = SapiBuilder::new("test", "Test")
509
            .activate_function(test_activate)
510
            .build()
511
            .expect("should build sapi module");
512

513
        assert!(sapi.activate.is_some());
514
        assert_eq!(
515
            sapi.activate.expect("should have activate function") as usize,
516
            test_activate as usize
517
        );
518
    }
519

520
    #[test]
521
    fn test_deactivate_function() {
522
        let sapi = SapiBuilder::new("test", "Test")
523
            .deactivate_function(test_deactivate)
524
            .build()
525
            .expect("should build sapi module");
526

527
        assert!(sapi.deactivate.is_some());
528
        assert_eq!(
529
            sapi.deactivate.expect("should have deactivate function") as usize,
530
            test_deactivate as usize
531
        );
532
    }
533

534
    #[test]
535
    fn test_ub_write_function() {
536
        let sapi = SapiBuilder::new("test", "Test")
537
            .ub_write_function(test_ub_write)
538
            .build()
539
            .expect("should build sapi module");
540

541
        assert!(sapi.ub_write.is_some());
542
        assert_eq!(
543
            sapi.ub_write.expect("should have ub_write function") as usize,
544
            test_ub_write as usize
545
        );
546
    }
547

548
    #[test]
549
    fn test_flush_function() {
550
        let sapi = SapiBuilder::new("test", "Test")
551
            .flush_function(test_flush)
552
            .build()
553
            .expect("should build sapi module");
554

555
        assert!(sapi.flush.is_some());
556
        assert_eq!(
557
            sapi.flush.expect("should have flush function") as usize,
558
            test_flush as usize
559
        );
560
    }
561

562
    #[test]
563
    fn test_getenv_function() {
564
        let sapi = SapiBuilder::new("test", "Test")
565
            .getenv_function(test_getenv)
566
            .build()
567
            .expect("should build sapi module");
568

569
        assert!(sapi.getenv.is_some());
570
        assert_eq!(
571
            sapi.getenv.expect("should have getenv function") as usize,
572
            test_getenv as usize
573
        );
574
    }
575

576
    // Note: Cannot test sapi_error_function because C-variadic functions are unstable in Rust
577
    // The sapi_error field accepts a function with variadic arguments which cannot be
578
    // created in stable Rust. However, the builder method itself works correctly.
579

580
    #[test]
581
    fn test_send_header_function() {
582
        let sapi = SapiBuilder::new("test", "Test")
583
            .send_header_function(test_send_header)
584
            .build()
585
            .expect("should build sapi module");
586

587
        assert!(sapi.send_header.is_some());
588
        assert_eq!(
589
            sapi.send_header.expect("should have send_header function") as usize,
590
            test_send_header as usize
591
        );
592
    }
593

594
    #[test]
595
    fn test_read_post_function() {
596
        let sapi = SapiBuilder::new("test", "Test")
597
            .read_post_function(test_read_post)
598
            .build()
599
            .expect("should build sapi module");
600

601
        assert!(sapi.read_post.is_some());
602
        assert_eq!(
603
            sapi.read_post.expect("should have read_post function") as usize,
604
            test_read_post as usize
605
        );
606
    }
607

608
    #[test]
609
    fn test_read_cookies_function() {
610
        let sapi = SapiBuilder::new("test", "Test")
611
            .read_cookies_function(test_read_cookies)
612
            .build()
613
            .expect("should build sapi module");
614

615
        assert!(sapi.read_cookies.is_some());
616
        assert_eq!(
617
            sapi.read_cookies
618
                .expect("should have read_cookies function") as usize,
619
            test_read_cookies as usize
620
        );
621
    }
622

623
    #[test]
624
    fn test_register_server_variables_function() {
625
        let sapi = SapiBuilder::new("test", "Test")
626
            .register_server_variables_function(test_register_server_variables)
627
            .build()
628
            .expect("should build sapi module");
629

630
        assert!(sapi.register_server_variables.is_some());
631
        assert_eq!(
632
            sapi.register_server_variables
633
                .expect("should have register_server_variables function") as usize,
634
            test_register_server_variables as usize
635
        );
636
    }
637

638
    #[test]
639
    fn test_log_message_function() {
640
        let sapi = SapiBuilder::new("test", "Test")
641
            .log_message_function(test_log_message)
642
            .build()
643
            .expect("should build sapi module");
644

645
        assert!(sapi.log_message.is_some());
646
        assert_eq!(
647
            sapi.log_message.expect("should have log_message function") as usize,
648
            test_log_message as usize
649
        );
650
    }
651

652
    #[test]
653
    fn test_get_request_time_function() {
654
        let sapi = SapiBuilder::new("test", "Test")
655
            .get_request_time_function(test_get_request_time)
656
            .build()
657
            .expect("should build sapi module");
658

659
        assert!(sapi.get_request_time.is_some());
660
        assert_eq!(
661
            sapi.get_request_time
662
                .expect("should have request_time function") as usize,
663
            test_get_request_time as usize
664
        );
665
    }
666

667
    #[test]
668
    fn test_terminate_process_function() {
669
        let sapi = SapiBuilder::new("test", "Test")
670
            .terminate_process_function(test_terminate_process)
671
            .build()
672
            .expect("should build sapi module");
673

674
        assert!(sapi.terminate_process.is_some());
675
        assert_eq!(
676
            sapi.terminate_process
677
                .expect("should have terminate_process function") as usize,
678
            test_terminate_process as usize
679
        );
680
    }
681

682
    #[test]
683
    fn test_get_target_uid_function() {
684
        let sapi = SapiBuilder::new("test", "Test")
685
            .get_target_uid_function(test_get_target_uid)
686
            .build()
687
            .expect("should build sapi module");
688

689
        assert!(sapi.get_target_uid.is_some());
690
        assert_eq!(
691
            sapi.get_target_uid
692
                .expect("should have get_target_uid function") as usize,
693
            test_get_target_uid as usize
694
        );
695
    }
696

697
    #[test]
698
    fn test_get_target_gid_function() {
699
        let sapi = SapiBuilder::new("test", "Test")
700
            .get_target_gid_function(test_get_target_gid)
701
            .build()
702
            .expect("should build sapi module");
703

704
        assert!(sapi.get_target_gid.is_some());
705
        assert_eq!(
706
            sapi.get_target_gid
707
                .expect("should have get_target_gid function") as usize,
708
            test_get_target_gid as usize
709
        );
710
    }
711

712
    #[test]
713
    fn test_php_ini_path_override() {
714
        let sapi = SapiBuilder::new("test", "Test")
715
            .php_ini_path_override("/custom/path/php.ini")
716
            .build()
717
            .expect("should build sapi module");
718

719
        assert!(!sapi.php_ini_path_override.is_null());
720
        assert_eq!(
721
            unsafe { CStr::from_ptr(sapi.php_ini_path_override) }
722
                .to_str()
723
                .expect("should convert CStr to str"),
724
            "/custom/path/php.ini"
725
        );
726
    }
727

728
    #[test]
729
    fn test_php_ini_ignore() {
730
        let sapi = SapiBuilder::new("test", "Test")
731
            .php_ini_ignore(1)
732
            .build()
733
            .expect("should build sapi module");
734

735
        assert_eq!(sapi.php_ini_ignore, 1);
736
    }
737

738
    #[test]
739
    fn test_php_ini_ignore_cwd() {
740
        let sapi = SapiBuilder::new("test", "Test")
741
            .php_ini_ignore_cwd(1)
742
            .build()
743
            .expect("should build sapi module");
744

745
        assert_eq!(sapi.php_ini_ignore_cwd, 1);
746
    }
747

748
    #[test]
749
    fn test_executable_location() {
750
        let sapi = SapiBuilder::new("test", "Test")
751
            .executable_location("/usr/bin/php")
752
            .build()
753
            .expect("should build sapi module");
754

755
        assert!(!sapi.executable_location.is_null());
756
        assert_eq!(
757
            unsafe { CStr::from_ptr(sapi.executable_location) }
758
                .to_str()
759
                .expect("should convert CStr to str"),
760
            "/usr/bin/php"
761
        );
762
    }
763

764
    #[test]
765
    fn test_default_functions_set() {
766
        let sapi = SapiBuilder::new("test", "Test")
767
            .build()
768
            .expect("should build sapi module");
769

770
        // Test that default functions are set
771
        assert!(sapi.send_header.is_some());
772
        assert!(sapi.sapi_error.is_some());
773
        assert!(sapi.default_post_reader.is_some());
774
        assert!(sapi.treat_data.is_some());
775
        assert!(sapi.input_filter.is_some());
776
    }
777

778
    #[test]
779
    fn test_chained_builder() {
780
        let sapi = SapiBuilder::new("chained", "Chained SAPI")
781
            .startup_function(test_startup)
782
            .shutdown_function(test_shutdown)
783
            .activate_function(test_activate)
784
            .deactivate_function(test_deactivate)
785
            .ub_write_function(test_ub_write)
786
            .flush_function(test_flush)
787
            .php_ini_path_override("/test/php.ini")
788
            .php_ini_ignore(1)
789
            .executable_location("/test/php")
790
            .build()
791
            .expect("should build sapi module");
792

793
        assert_eq!(
794
            unsafe { CStr::from_ptr(sapi.name) }
795
                .to_str()
796
                .expect("should convert CStr to str"),
797
            "chained"
798
        );
799
        assert_eq!(
800
            unsafe { CStr::from_ptr(sapi.pretty_name) }
801
                .to_str()
802
                .expect("should convert CStr to str"),
803
            "Chained SAPI"
804
        );
805
        assert!(sapi.startup.is_some());
806
        assert!(sapi.shutdown.is_some());
807
        assert!(sapi.activate.is_some());
808
        assert!(sapi.deactivate.is_some());
809
        assert!(sapi.ub_write.is_some());
810
        assert!(sapi.flush.is_some());
811
        assert_eq!(sapi.php_ini_ignore, 1);
812
        assert!(!sapi.php_ini_path_override.is_null());
813
        assert!(!sapi.executable_location.is_null());
814
    }
815
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc