Skip to content

Commit 64f5509

Browse files
committed
feat(php): Add PHP 8.5 support
1 parent 935a2d2 commit 64f5509

File tree

10 files changed

+320
-166
lines changed

10 files changed

+320
-166
lines changed

.github/action/musl/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ARG PHP_VERSION=8.4
1+
ARG PHP_VERSION=8.5
22
ARG TS=-zts
33

44
FROM php:${PHP_VERSION}${TS}-alpine

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
strategy:
8888
matrix:
8989
os: [ubuntu-latest, macos-latest, windows-latest]
90-
php: ["8.1", "8.2", "8.3", "8.4"]
90+
php: ["8.1", "8.2", "8.3", "8.4", "8.5"]
9191
rust: [stable, nightly]
9292
clang: ["15", "17"]
9393
phpts: [ts, nts]
@@ -231,7 +231,7 @@ jobs:
231231
runs-on: ubuntu-latest
232232
strategy:
233233
matrix:
234-
php: ["8.1", "8.2", "8.3", "8.4"]
234+
php: ["8.1", "8.2", "8.3", "8.4", "8.5"]
235235
phpts: [["-zts", "TS"], ["", "NTS"]]
236236
env:
237237
CARGO_TERM_COLOR: always

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
FROM rust:latest AS base
2-
ARG PHP_VERSION=8.4
2+
ARG PHP_VERSION=8.5
33
WORKDIR /tmp
44
RUN <<EOF
55
set -e

allowed_bindings.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ bind! {
271271
zend_hash_get_current_key_zval_ex,
272272
zend_hash_get_current_data_ex,
273273
zend_hash_move_backwards_ex,
274+
zend_hash_key_type,
274275
zend_array_count,
275276
gc_possible_root,
276277
ZEND_ACC_NOT_SERIALIZABLE,

build.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ enum ApiVersion {
268268
Php82 = 2022_08_29,
269269
Php83 = 2023_08_31,
270270
Php84 = 2024_09_24,
271+
Php85 = 2025_09_25,
271272
}
272273

273274
impl ApiVersion {
@@ -285,7 +286,7 @@ impl ApiVersion {
285286

286287
/// Returns the maximum API version supported by ext-php-rs.
287288
pub const fn max() -> Self {
288-
ApiVersion::Php84
289+
ApiVersion::Php85
289290
}
290291

291292
pub fn versions() -> Vec<Self> {
@@ -295,6 +296,7 @@ impl ApiVersion {
295296
ApiVersion::Php82,
296297
ApiVersion::Php83,
297298
ApiVersion::Php84,
299+
ApiVersion::Php85,
298300
]
299301
}
300302

@@ -313,6 +315,7 @@ impl ApiVersion {
313315
ApiVersion::Php82 => "php82",
314316
ApiVersion::Php83 => "php83",
315317
ApiVersion::Php84 => "php84",
318+
ApiVersion::Php85 => "php85",
316319
}
317320
}
318321

@@ -323,6 +326,7 @@ impl ApiVersion {
323326
ApiVersion::Php82 => "EXT_PHP_RS_PHP_82",
324327
ApiVersion::Php83 => "EXT_PHP_RS_PHP_83",
325328
ApiVersion::Php84 => "EXT_PHP_RS_PHP_84",
329+
ApiVersion::Php85 => "EXT_PHP_RS_PHP_85",
326330
}
327331
}
328332
}
@@ -344,7 +348,10 @@ impl TryFrom<u32> for ApiVersion {
344348
x if ((ApiVersion::Php83 as u32)..(ApiVersion::Php84 as u32)).contains(&x) => {
345349
Ok(ApiVersion::Php83)
346350
}
347-
x if (ApiVersion::Php84 as u32) == x => Ok(ApiVersion::Php84),
351+
x if ((ApiVersion::Php84 as u32)..(ApiVersion::Php85 as u32)).contains(&x) => {
352+
Ok(ApiVersion::Php84)
353+
}
354+
x if (ApiVersion::Php85 as u32) == x => Ok(ApiVersion::Php85),
348355
version => Err(anyhow!(
349356
"The current version of PHP is not supported. Current PHP API version: {}, requires a version between {} and {}",
350357
version,
@@ -371,7 +378,7 @@ fn check_php_version(info: &PHPInfo) -> Result<()> {
371378
// The PHP version cfg flags should also stack - if you compile on PHP 8.2 you
372379
// should get both the `php81` and `php82` flags.
373380
println!(
374-
"cargo::rustc-check-cfg=cfg(php80, php81, php82, php83, php84, php_zts, php_debug, docs)"
381+
"cargo::rustc-check-cfg=cfg(php80, php81, php82, php83, php84, php85, php_zts, php_debug, docs)"
375382
);
376383

377384
if version == ApiVersion::Php80 {
@@ -416,6 +423,7 @@ fn main() -> Result<()> {
416423
println!("cargo:rustc-cfg=php82");
417424
println!("cargo:rustc-cfg=php83");
418425
println!("cargo:rustc-cfg=php84");
426+
println!("cargo:rustc-cfg=php85");
419427
std::fs::copy("docsrs_bindings.rs", out_path)
420428
.expect("failed to copy docs.rs stub bindings to out directory");
421429
return Ok(());

docsrs_bindings.rs

Lines changed: 173 additions & 129 deletions
Large diffs are not rendered by default.

src/builders/sapi.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ impl SapiBuilder {
9595
ini_entries: ptr::null_mut(),
9696
additional_functions: ptr::null(),
9797
input_filter_init: None,
98+
#[cfg(php85)]
99+
pre_request_init: None,
98100
},
99101
executable_location: None,
100102
php_ini_path_override: None,
@@ -287,6 +289,20 @@ impl SapiBuilder {
287289
self
288290
}
289291

292+
/// Sets the pre-request init function for this SAPI
293+
///
294+
/// This function is called before request activation and before POST data is read.
295+
/// It is typically used for .user.ini processing.
296+
///
297+
/// # Parameters
298+
///
299+
/// * `func` - The function to be called before request initialization.
300+
#[cfg(php85)]
301+
pub fn pre_request_init_function(mut self, func: SapiPreRequestInitFunc) -> Self {
302+
self.module.pre_request_init = Some(func);
303+
self
304+
}
305+
290306
/// Set the `ini_entries` for this SAPI
291307
///
292308
/// # Parameters
@@ -439,6 +455,10 @@ pub type SapiGetUidFunc = extern "C" fn(uid: *mut uid_t) -> c_int;
439455
/// A function to be called when PHP gets the gid
440456
pub type SapiGetGidFunc = extern "C" fn(gid: *mut gid_t) -> c_int;
441457

458+
/// A function to be called before request activation (used for .user.ini processing)
459+
#[cfg(php85)]
460+
pub type SapiPreRequestInitFunc = extern "C" fn() -> c_int;
461+
442462
extern "C" fn dummy_send_header(_header: *mut sapi_header_struct, _server_context: *mut c_void) {}
443463

444464
#[cfg(test)]
@@ -490,6 +510,10 @@ mod test {
490510
extern "C" fn test_get_target_gid(_gid: *mut gid_t) -> c_int {
491511
0
492512
}
513+
#[cfg(php85)]
514+
extern "C" fn test_pre_request_init() -> c_int {
515+
0
516+
}
493517

494518
#[test]
495519
fn test_basic_sapi_builder() {
@@ -760,6 +784,22 @@ mod test {
760784
);
761785
}
762786

787+
#[cfg(php85)]
788+
#[test]
789+
fn test_pre_request_init_function() {
790+
let sapi = SapiBuilder::new("test", "Test")
791+
.pre_request_init_function(test_pre_request_init)
792+
.build()
793+
.expect("should build sapi module");
794+
795+
assert!(sapi.pre_request_init.is_some());
796+
assert_eq!(
797+
sapi.pre_request_init
798+
.expect("should have pre_request_init function") as usize,
799+
test_pre_request_init as usize
800+
);
801+
}
802+
763803
#[cfg(php82)]
764804
#[test]
765805
fn test_sapi_ini_entries() {

src/constant.rs

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,26 @@ impl IntoConst for &str {
107107
flags: GlobalConstantFlags,
108108
) -> Result<()> {
109109
unsafe {
110-
zend_register_string_constant(
111-
CString::new(name)?.as_ptr(),
112-
name.len() as _,
113-
CString::new(*self)?.as_ptr(),
114-
flags.bits().try_into()?,
115-
module_number,
116-
);
110+
#[cfg(php85)]
111+
{
112+
let _ = zend_register_string_constant(
113+
CString::new(name)?.as_ptr(),
114+
name.len() as _,
115+
CString::new(*self)?.as_ptr(),
116+
flags.bits().try_into()?,
117+
module_number,
118+
);
119+
}
120+
#[cfg(not(php85))]
121+
{
122+
zend_register_string_constant(
123+
CString::new(name)?.as_ptr(),
124+
name.len() as _,
125+
CString::new(*self)?.as_ptr(),
126+
flags.bits().try_into()?,
127+
module_number,
128+
);
129+
}
117130
};
118131
Ok(())
119132
}
@@ -127,13 +140,26 @@ impl IntoConst for bool {
127140
flags: GlobalConstantFlags,
128141
) -> Result<()> {
129142
unsafe {
130-
zend_register_bool_constant(
131-
CString::new(name)?.as_ptr(),
132-
name.len() as _,
133-
*self,
134-
flags.bits().try_into()?,
135-
module_number,
136-
);
143+
#[cfg(php85)]
144+
{
145+
let _ = zend_register_bool_constant(
146+
CString::new(name)?.as_ptr(),
147+
name.len() as _,
148+
*self,
149+
flags.bits().try_into()?,
150+
module_number,
151+
);
152+
}
153+
#[cfg(not(php85))]
154+
{
155+
zend_register_bool_constant(
156+
CString::new(name)?.as_ptr(),
157+
name.len() as _,
158+
*self,
159+
flags.bits().try_into()?,
160+
module_number,
161+
);
162+
}
137163
};
138164
Ok(())
139165
}
@@ -150,15 +176,29 @@ macro_rules! into_const_num {
150176
module_number: i32,
151177
flags: GlobalConstantFlags,
152178
) -> Result<()> {
153-
Ok(unsafe {
154-
$fn(
155-
CString::new(name)?.as_ptr(),
156-
name.len() as _,
157-
(*self).into(),
158-
flags.bits().try_into()?,
159-
module_number,
160-
)
161-
})
179+
unsafe {
180+
#[cfg(php85)]
181+
{
182+
let _ = $fn(
183+
CString::new(name)?.as_ptr(),
184+
name.len() as _,
185+
(*self).into(),
186+
flags.bits().try_into()?,
187+
module_number,
188+
);
189+
}
190+
#[cfg(not(php85))]
191+
{
192+
$fn(
193+
CString::new(name)?.as_ptr(),
194+
name.len() as _,
195+
(*self).into(),
196+
flags.bits().try_into()?,
197+
module_number,
198+
);
199+
}
200+
};
201+
Ok(())
162202
}
163203
}
164204
};

src/types/array/iterators.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ use crate::{
1515
types::Zval,
1616
};
1717

18+
#[cfg(php85)]
19+
use crate::ffi::zend_hash_key_type_HASH_KEY_NON_EXISTENT;
20+
1821
/// Immutable iterator upon a reference to a hashtable.
1922
pub struct Iter<'a> {
2023
ht: &'a ZendHashTable,
@@ -59,13 +62,22 @@ impl<'a> Iter<'a> {
5962
zend_hash_get_current_key_type_ex(ptr::from_ref(self.ht).cast_mut(), &raw mut self.pos)
6063
};
6164

62-
// Key type `-1` is ???
63-
// Key type `1` is string
64-
// Key type `2` is long
65-
// Key type `3` is null meaning the end of the array
66-
if key_type == -1 || key_type == 3 {
65+
// Key type `1` is string (HASH_KEY_IS_STRING)
66+
// Key type `2` is long (HASH_KEY_IS_LONG)
67+
// Key type `3` is null/non-existent (HASH_KEY_NON_EXISTENT)
68+
// Pre-PHP 8.5 returns int, PHP 8.5+ returns enum u32
69+
#[cfg(php85)]
70+
if key_type == zend_hash_key_type_HASH_KEY_NON_EXISTENT {
6771
return None;
6872
}
73+
#[cfg(not(php85))]
74+
{
75+
// Pre-PHP 8.5: function returns signed int (i32)
76+
// Check for -1 (defensive) and 3 (HASH_KEY_NON_EXISTENT)
77+
if key_type == -1 || key_type == 3 {
78+
return None;
79+
}
80+
}
6981

7082
let mut key = Zval::new();
7183

@@ -157,9 +169,18 @@ impl DoubleEndedIterator for Iter<'_> {
157169
zend_hash_get_current_key_type_ex(ptr::from_ref(self.ht).cast_mut(), &raw mut self.pos)
158170
};
159171

160-
if key_type == -1 {
172+
#[cfg(php85)]
173+
if key_type == zend_hash_key_type_HASH_KEY_NON_EXISTENT {
161174
return None;
162175
}
176+
#[cfg(not(php85))]
177+
{
178+
// Pre-PHP 8.5: function returns signed int (i32)
179+
// Check for -1 (defensive) and 3 (HASH_KEY_NON_EXISTENT)
180+
if key_type == -1 || key_type == 3 {
181+
return None;
182+
}
183+
}
163184

164185
let key = Zval::new();
165186

tools/update_bindings.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ docker buildx build \
99
--platform linux/amd64 \
1010
--target docsrs_bindings \
1111
-o type=local,dest=. \
12-
--build-arg PHP_VERSION=8.4 \
12+
--build-arg PHP_VERSION=8.5 \
1313
.

0 commit comments

Comments
 (0)