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

davidcole1340 / ext-php-rs / 16323306954

16 Jul 2025 03:10PM UTC coverage: 22.222% (+0.6%) from 21.654%
16323306954

Pull #482

github

web-flow
Merge de76d2402 into 1166e2910
Pull Request #482: feat(cargo-php)!: escalate privilege and to copy extension and edit ini file

0 of 48 new or added lines in 1 file covered. (0.0%)

193 existing lines in 10 files now uncovered.

870 of 3915 relevant lines covered (22.22%)

3.63 hits per line

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

96.67
/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::{
10
    ffi::{c_char, c_int, c_void, CString},
11
    ptr,
12
};
13

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

278
    /// Set the `ini_entries` for this SAPI
279
    ///
280
    /// # Parameters
281
    ///
282
    /// * `entries` - A pointer to the ini entries.
283
    pub fn ini_entries<E: Into<String>>(mut self, entries: E) -> Self {
1✔
284
        self.ini_entries = Some(entries.into());
2✔
285
        self
1✔
286
    }
287

288
    /// Sets the php ini path override for this SAPI
289
    ///
290
    /// # Parameters
291
    ///
292
    /// * `path` - The path to the php ini file.
293
    pub fn php_ini_path_override<S: Into<String>>(mut self, path: S) -> Self {
2✔
294
        self.php_ini_path_override = Some(path.into());
4✔
295
        self
2✔
296
    }
297

298
    /// Sets the php ini ignore for this SAPI
299
    ///
300
    /// # Parameters
301
    ///
302
    /// * `ignore` - The value to set php ini ignore to.
303
    pub fn php_ini_ignore(mut self, ignore: i32) -> Self {
2✔
304
        self.module.php_ini_ignore = ignore as c_int;
2✔
305
        self
2✔
306
    }
307

308
    /// Sets the php ini ignore cwd for this SAPI
309
    ///
310
    /// # Parameters
311
    ///
312
    /// * `ignore` - The value to set php ini ignore cwd to.
313
    pub fn php_ini_ignore_cwd(mut self, ignore: i32) -> Self {
1✔
314
        self.module.php_ini_ignore_cwd = ignore as c_int;
1✔
315
        self
1✔
316
    }
317

318
    /// Sets the executable location for this SAPI
319
    ///
320
    /// # Parameters
321
    ///
322
    /// * `location` - The location of the executable.
323
    pub fn executable_location<S: Into<String>>(mut self, location: S) -> Self {
2✔
324
        self.executable_location = Some(location.into());
4✔
325
        self
2✔
326
    }
327

328
    /// Builds the extension and returns a `SapiModule`.
329
    ///
330
    /// Returns a result containing the sapi module if successful.
331
    ///
332
    /// # Errors
333
    ///
334
    /// * If name or property name contain null bytes
335
    pub fn build(mut self) -> Result<SapiModule> {
25✔
336
        self.module.name = CString::new(self.name)?.into_raw();
75✔
337
        self.module.pretty_name = CString::new(self.pretty_name)?.into_raw();
25✔
338

339
        if let Some(path) = self.executable_location {
2✔
340
            self.module.executable_location = CString::new(path)?.into_raw();
2✔
341
        }
342

343
        if let Some(entries) = self.ini_entries {
26✔
344
            self.module.ini_entries = CString::new(entries)?.into_raw();
1✔
345
        }
346

347
        if let Some(path) = self.php_ini_path_override {
27✔
348
            self.module.php_ini_path_override = CString::new(path)?.into_raw();
2✔
349
        }
350

351
        if self.module.send_header.is_none() {
49✔
352
            self.module.send_header = Some(dummy_send_header);
24✔
353
        }
354

355
        if self.module.sapi_error.is_none() {
25✔
356
            self.module.sapi_error = Some(ext_php_rs_php_error);
25✔
357
        }
358

359
        if self.module.default_post_reader.is_none() {
25✔
360
            self.module.default_post_reader = Some(php_default_post_reader);
25✔
361
        }
362

363
        if self.module.treat_data.is_none() {
25✔
364
            self.module.treat_data = Some(php_default_treat_data);
25✔
365
        }
366

367
        if self.module.input_filter.is_none() {
25✔
368
            self.module.input_filter = Some(php_default_input_filter);
25✔
369
        }
370

371
        Ok(self.module)
372
    }
373
}
374

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

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

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

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

387
/// A function to be called when PHP send a header
388
pub type SapiSendHeaderFunc =
389
    extern "C" fn(header: *mut sapi_header_struct, server_context: *mut c_void);
390

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

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

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

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

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

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

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

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

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

418
/// A function to be called when PHP terminates the process
419
pub type SapiTerminateProcessFunc = extern "C" fn();
420

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

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

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

429
#[cfg(test)]
430
mod test {
431
    use super::*;
432
    use std::ffi::CStr;
433

434
    extern "C" fn test_startup(_sapi: *mut SapiModule) -> c_int {
435
        0
436
    }
437
    extern "C" fn test_shutdown(_sapi: *mut SapiModule) -> c_int {
438
        0
439
    }
440
    extern "C" fn test_activate() -> c_int {
441
        0
442
    }
443
    extern "C" fn test_deactivate() -> c_int {
444
        0
445
    }
446
    extern "C" fn test_ub_write(_str: *const c_char, _str_length: usize) -> usize {
447
        0
448
    }
449
    extern "C" fn test_flush(_server_context: *mut c_void) {}
450
    extern "C" fn test_getenv(_name: *const c_char, _name_length: usize) -> *mut c_char {
451
        ptr::null_mut()
452
    }
453
    // Note: C-variadic functions are unstable in Rust, so we can't test this properly
454
    // extern "C" fn test_sapi_error(_type: c_int, _error_msg: *const c_char, _args: ...) {}
455
    extern "C" fn test_send_header(_header: *mut sapi_header_struct, _server_context: *mut c_void) {
456
    }
457
    extern "C" fn test_read_post(_buffer: *mut c_char, _length: usize) -> usize {
458
        0
459
    }
460
    extern "C" fn test_read_cookies() -> *mut c_char {
461
        ptr::null_mut()
462
    }
463
    extern "C" fn test_register_server_variables(_vars: *mut Zval) {}
464
    extern "C" fn test_log_message(_message: *const c_char, _syslog_type_int: c_int) {}
465
    extern "C" fn test_get_request_time(_time: *mut f64) -> c_int {
466
        0
467
    }
468
    extern "C" fn test_terminate_process() {}
469
    extern "C" fn test_get_target_uid(_uid: *mut uid_t) -> c_int {
470
        0
471
    }
472
    extern "C" fn test_get_target_gid(_gid: *mut gid_t) -> c_int {
473
        0
474
    }
475

476
    #[test]
477
    fn test_basic_sapi_builder() {
478
        let sapi = SapiBuilder::new("test_sapi", "Test SAPI")
479
            .build()
480
            .expect("should build sapi module");
481

482
        assert_eq!(
483
            unsafe { CStr::from_ptr(sapi.name) }
484
                .to_str()
485
                .expect("should convert CStr to str"),
486
            "test_sapi"
487
        );
488
        assert_eq!(
489
            unsafe { CStr::from_ptr(sapi.pretty_name) }
490
                .to_str()
491
                .expect("should convert CStr to str"),
492
            "Test SAPI"
493
        );
494
    }
495

496
    #[test]
497
    fn test_startup_function() {
498
        let sapi = SapiBuilder::new("test", "Test")
499
            .startup_function(test_startup)
500
            .build()
501
            .expect("should build sapi module");
502

503
        assert!(sapi.startup.is_some());
504
        assert_eq!(
505
            sapi.startup.expect("should have startup function") as usize,
506
            test_startup as usize
507
        );
508
    }
509

510
    #[test]
511
    fn test_shutdown_function() {
512
        let sapi = SapiBuilder::new("test", "Test")
513
            .shutdown_function(test_shutdown)
514
            .build()
515
            .expect("should build sapi module");
516

517
        assert!(sapi.shutdown.is_some());
518
        assert_eq!(
519
            sapi.shutdown.expect("should have shutdown function") as usize,
520
            test_shutdown as usize
521
        );
522
    }
523

524
    #[test]
525
    fn test_activate_function() {
526
        let sapi = SapiBuilder::new("test", "Test")
527
            .activate_function(test_activate)
528
            .build()
529
            .expect("should build sapi module");
530

531
        assert!(sapi.activate.is_some());
532
        assert_eq!(
533
            sapi.activate.expect("should have activate function") as usize,
534
            test_activate as usize
535
        );
536
    }
537

538
    #[test]
539
    fn test_deactivate_function() {
540
        let sapi = SapiBuilder::new("test", "Test")
541
            .deactivate_function(test_deactivate)
542
            .build()
543
            .expect("should build sapi module");
544

545
        assert!(sapi.deactivate.is_some());
546
        assert_eq!(
547
            sapi.deactivate.expect("should have deactivate function") as usize,
548
            test_deactivate as usize
549
        );
550
    }
551

552
    #[test]
553
    fn test_ub_write_function() {
554
        let sapi = SapiBuilder::new("test", "Test")
555
            .ub_write_function(test_ub_write)
556
            .build()
557
            .expect("should build sapi module");
558

559
        assert!(sapi.ub_write.is_some());
560
        assert_eq!(
561
            sapi.ub_write.expect("should have ub_write function") as usize,
562
            test_ub_write as usize
563
        );
564
    }
565

566
    #[test]
567
    fn test_flush_function() {
568
        let sapi = SapiBuilder::new("test", "Test")
569
            .flush_function(test_flush)
570
            .build()
571
            .expect("should build sapi module");
572

573
        assert!(sapi.flush.is_some());
574
        assert_eq!(
575
            sapi.flush.expect("should have flush function") as usize,
576
            test_flush as usize
577
        );
578
    }
579

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

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

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

598
    #[test]
599
    fn test_send_header_function() {
600
        let sapi = SapiBuilder::new("test", "Test")
601
            .send_header_function(test_send_header)
602
            .build()
603
            .expect("should build sapi module");
604

605
        assert!(sapi.send_header.is_some());
606
        assert_eq!(
607
            sapi.send_header.expect("should have send_header function") as usize,
608
            test_send_header as usize
609
        );
610
    }
611

612
    #[test]
613
    fn test_read_post_function() {
614
        let sapi = SapiBuilder::new("test", "Test")
615
            .read_post_function(test_read_post)
616
            .build()
617
            .expect("should build sapi module");
618

619
        assert!(sapi.read_post.is_some());
620
        assert_eq!(
621
            sapi.read_post.expect("should have read_post function") as usize,
622
            test_read_post as usize
623
        );
624
    }
625

626
    #[test]
627
    fn test_read_cookies_function() {
628
        let sapi = SapiBuilder::new("test", "Test")
629
            .read_cookies_function(test_read_cookies)
630
            .build()
631
            .expect("should build sapi module");
632

633
        assert!(sapi.read_cookies.is_some());
634
        assert_eq!(
635
            sapi.read_cookies
636
                .expect("should have read_cookies function") as usize,
637
            test_read_cookies as usize
638
        );
639
    }
640

641
    #[test]
642
    fn test_register_server_variables_function() {
643
        let sapi = SapiBuilder::new("test", "Test")
644
            .register_server_variables_function(test_register_server_variables)
645
            .build()
646
            .expect("should build sapi module");
647

648
        assert!(sapi.register_server_variables.is_some());
649
        assert_eq!(
650
            sapi.register_server_variables
651
                .expect("should have register_server_variables function") as usize,
652
            test_register_server_variables as usize
653
        );
654
    }
655

656
    #[test]
657
    fn test_log_message_function() {
658
        let sapi = SapiBuilder::new("test", "Test")
659
            .log_message_function(test_log_message)
660
            .build()
661
            .expect("should build sapi module");
662

663
        assert!(sapi.log_message.is_some());
664
        assert_eq!(
665
            sapi.log_message.expect("should have log_message function") as usize,
666
            test_log_message as usize
667
        );
668
    }
669

670
    #[test]
671
    fn test_get_request_time_function() {
672
        let sapi = SapiBuilder::new("test", "Test")
673
            .get_request_time_function(test_get_request_time)
674
            .build()
675
            .expect("should build sapi module");
676

677
        assert!(sapi.get_request_time.is_some());
678
        assert_eq!(
679
            sapi.get_request_time
680
                .expect("should have request_time function") as usize,
681
            test_get_request_time as usize
682
        );
683
    }
684

685
    #[test]
686
    fn test_terminate_process_function() {
687
        let sapi = SapiBuilder::new("test", "Test")
688
            .terminate_process_function(test_terminate_process)
689
            .build()
690
            .expect("should build sapi module");
691

692
        assert!(sapi.terminate_process.is_some());
693
        assert_eq!(
694
            sapi.terminate_process
695
                .expect("should have terminate_process function") as usize,
696
            test_terminate_process as usize
697
        );
698
    }
699

700
    #[test]
701
    fn test_get_target_uid_function() {
702
        let sapi = SapiBuilder::new("test", "Test")
703
            .get_target_uid_function(test_get_target_uid)
704
            .build()
705
            .expect("should build sapi module");
706

707
        assert!(sapi.get_target_uid.is_some());
708
        assert_eq!(
709
            sapi.get_target_uid
710
                .expect("should have get_target_uid function") as usize,
711
            test_get_target_uid as usize
712
        );
713
    }
714

715
    #[test]
716
    fn test_get_target_gid_function() {
717
        let sapi = SapiBuilder::new("test", "Test")
718
            .get_target_gid_function(test_get_target_gid)
719
            .build()
720
            .expect("should build sapi module");
721

722
        assert!(sapi.get_target_gid.is_some());
723
        assert_eq!(
724
            sapi.get_target_gid
725
                .expect("should have get_target_gid function") as usize,
726
            test_get_target_gid as usize
727
        );
728
    }
729

730
    #[cfg(php82)]
731
    #[test]
732
    fn test_sapi_ini_entries() {
733
        let mut ini = crate::builders::IniBuilder::new();
734
        ini.define("foo=bar").expect("should define ini entry");
735
        ini.quoted("memory_limit", "128M")
736
            .expect("should add quoted ini entry");
737

738
        let sapi = SapiBuilder::new("test", "Test")
739
            .ini_entries(ini)
740
            .build()
741
            .expect("should build sapi module");
742

743
        assert!(!sapi.ini_entries.is_null());
744
        assert_eq!(
745
            unsafe { CStr::from_ptr(sapi.ini_entries) },
746
            c"foo=bar\nmemory_limit=\"128M\"\n"
747
        );
748
    }
749

750
    #[test]
751
    fn test_php_ini_path_override() {
752
        let sapi = SapiBuilder::new("test", "Test")
753
            .php_ini_path_override("/custom/path/php.ini")
754
            .build()
755
            .expect("should build sapi module");
756

757
        assert!(!sapi.php_ini_path_override.is_null());
758
        assert_eq!(
759
            unsafe { CStr::from_ptr(sapi.php_ini_path_override) },
760
            c"/custom/path/php.ini"
761
        );
762
    }
763

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

771
        assert_eq!(sapi.php_ini_ignore, 1);
772
    }
773

774
    #[test]
775
    fn test_php_ini_ignore_cwd() {
776
        let sapi = SapiBuilder::new("test", "Test")
777
            .php_ini_ignore_cwd(1)
778
            .build()
779
            .expect("should build sapi module");
780

781
        assert_eq!(sapi.php_ini_ignore_cwd, 1);
782
    }
783

784
    #[test]
785
    fn test_executable_location() {
786
        let sapi = SapiBuilder::new("test", "Test")
787
            .executable_location("/usr/bin/php")
788
            .build()
789
            .expect("should build sapi module");
790

791
        assert!(!sapi.executable_location.is_null());
792
        assert_eq!(
793
            unsafe { CStr::from_ptr(sapi.executable_location) },
794
            c"/usr/bin/php"
795
        );
796
    }
797

798
    #[test]
799
    fn test_default_functions_set() {
800
        let sapi = SapiBuilder::new("test", "Test")
801
            .build()
802
            .expect("should build sapi module");
803

804
        // Test that default functions are set
805
        assert!(sapi.send_header.is_some());
806
        assert!(sapi.sapi_error.is_some());
807
        assert!(sapi.default_post_reader.is_some());
808
        assert!(sapi.treat_data.is_some());
809
        assert!(sapi.input_filter.is_some());
810
    }
811

812
    #[test]
813
    fn test_chained_builder() {
814
        let sapi = SapiBuilder::new("chained", "Chained SAPI")
815
            .startup_function(test_startup)
816
            .shutdown_function(test_shutdown)
817
            .activate_function(test_activate)
818
            .deactivate_function(test_deactivate)
819
            .ub_write_function(test_ub_write)
820
            .flush_function(test_flush)
821
            .php_ini_path_override("/test/php.ini")
822
            .php_ini_ignore(1)
823
            .executable_location("/test/php")
824
            .build()
825
            .expect("should build sapi module");
826

827
        assert_eq!(unsafe { CStr::from_ptr(sapi.name) }, c"chained");
828
        assert_eq!(unsafe { CStr::from_ptr(sapi.pretty_name) }, c"Chained SAPI");
829
        assert!(sapi.startup.is_some());
830
        assert!(sapi.shutdown.is_some());
831
        assert!(sapi.activate.is_some());
832
        assert!(sapi.deactivate.is_some());
833
        assert!(sapi.ub_write.is_some());
834
        assert!(sapi.flush.is_some());
835
        assert_eq!(sapi.php_ini_ignore, 1);
836
        assert!(!sapi.php_ini_path_override.is_null());
837
        assert!(!sapi.executable_location.is_null());
838
    }
839
}
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