Skip to content

Commit 6793b4b

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

File tree

13 files changed

+347
-173
lines changed

13 files changed

+347
-173
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: 6 additions & 6 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]
@@ -173,7 +173,7 @@ jobs:
173173
runs-on: ubuntu-latest
174174
env:
175175
clang: "17"
176-
php_version: "8.4"
176+
php_version: "8.5"
177177
steps:
178178
- name: Checkout code
179179
uses: actions/checkout@v5
@@ -185,15 +185,15 @@ jobs:
185185
env:
186186
debug: true
187187

188-
- name: Install libphp-embed
189-
run: sudo apt update -y && sudo apt install -y libphp8.4-embed
190-
191188
- name: Setup Rust
192189
uses: dtolnay/rust-toolchain@master
193190
with:
194191
toolchain: stable
195192
components: rustfmt, clippy
196193

194+
- name: Install libphp-embed
195+
run: sudo apt update -y && sudo apt install -y libphp${{ env.php_version }}-embed-dbgsym
196+
197197
- name: Install cargo-expand
198198
uses: dtolnay/install@cargo-expand
199199

@@ -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: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Types and traits for registering constants in PHP.
22
3+
use cfg_if::cfg_if;
34
use std::ffi::CString;
45
use std::fmt::Debug;
56

@@ -107,13 +108,25 @@ impl IntoConst for &str {
107108
flags: GlobalConstantFlags,
108109
) -> Result<()> {
109110
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-
);
111+
cfg_if! {
112+
if #[cfg(php85)] {
113+
let _ = zend_register_string_constant(
114+
CString::new(name)?.as_ptr(),
115+
name.len() as _,
116+
CString::new(*self)?.as_ptr(),
117+
flags.bits().try_into()?,
118+
module_number,
119+
);
120+
} else {
121+
zend_register_string_constant(
122+
CString::new(name)?.as_ptr(),
123+
name.len() as _,
124+
CString::new(*self)?.as_ptr(),
125+
flags.bits().try_into()?,
126+
module_number,
127+
);
128+
}
129+
}
117130
};
118131
Ok(())
119132
}
@@ -127,13 +140,25 @@ 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_if! {
144+
if #[cfg(php85)] {
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+
} else {
153+
zend_register_bool_constant(
154+
CString::new(name)?.as_ptr(),
155+
name.len() as _,
156+
*self,
157+
flags.bits().try_into()?,
158+
module_number,
159+
);
160+
}
161+
}
137162
};
138163
Ok(())
139164
}
@@ -150,15 +175,28 @@ macro_rules! into_const_num {
150175
module_number: i32,
151176
flags: GlobalConstantFlags,
152177
) -> 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-
})
178+
unsafe {
179+
cfg_if! {
180+
if #[cfg(php85)] {
181+
let _ = $fn(
182+
CString::new(name)?.as_ptr(),
183+
name.len() as _,
184+
(*self).into(),
185+
flags.bits().try_into()?,
186+
module_number,
187+
);
188+
} else {
189+
$fn(
190+
CString::new(name)?.as_ptr(),
191+
name.len() as _,
192+
(*self).into(),
193+
flags.bits().try_into()?,
194+
module_number,
195+
);
196+
}
197+
}
198+
};
199+
Ok(())
162200
}
163201
}
164202
};

src/types/array/iterators.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use cfg_if::cfg_if;
12
use std::{
23
convert::TryInto,
34
iter::{DoubleEndedIterator, ExactSizeIterator, Iterator},
@@ -15,6 +16,9 @@ use crate::{
1516
types::Zval,
1617
};
1718

19+
#[cfg(php85)]
20+
use crate::ffi::zend_hash_key_type_HASH_KEY_NON_EXISTENT;
21+
1822
/// Immutable iterator upon a reference to a hashtable.
1923
pub struct Iter<'a> {
2024
ht: &'a ZendHashTable,
@@ -59,12 +63,22 @@ impl<'a> Iter<'a> {
5963
zend_hash_get_current_key_type_ex(ptr::from_ref(self.ht).cast_mut(), &raw mut self.pos)
6064
};
6165

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 {
67-
return None;
66+
// Key type `1` is string (HASH_KEY_IS_STRING)
67+
// Key type `2` is long (HASH_KEY_IS_LONG)
68+
// Key type `3` is null/non-existent (HASH_KEY_NON_EXISTENT)
69+
// Pre-PHP 8.5 returns int, PHP 8.5+ returns enum u32
70+
cfg_if! {
71+
if #[cfg(php85)] {
72+
if key_type == zend_hash_key_type_HASH_KEY_NON_EXISTENT {
73+
return None;
74+
}
75+
} else {
76+
// Pre-PHP 8.5: function returns signed int (i32)
77+
// Check for -1 (defensive) and 3 (HASH_KEY_NON_EXISTENT)
78+
if key_type == -1 || key_type == 3 {
79+
return None;
80+
}
81+
}
6882
}
6983

7084
let mut key = Zval::new();
@@ -157,8 +171,18 @@ impl DoubleEndedIterator for Iter<'_> {
157171
zend_hash_get_current_key_type_ex(ptr::from_ref(self.ht).cast_mut(), &raw mut self.pos)
158172
};
159173

160-
if key_type == -1 {
161-
return None;
174+
cfg_if! {
175+
if #[cfg(php85)] {
176+
if key_type == zend_hash_key_type_HASH_KEY_NON_EXISTENT {
177+
return None;
178+
}
179+
} else {
180+
// Pre-PHP 8.5: function returns signed int (i32)
181+
// Check for -1 (defensive) and 3 (HASH_KEY_NON_EXISTENT)
182+
if key_type == -1 || key_type == 3 {
183+
return None;
184+
}
185+
}
162186
}
163187

164188
let key = Zval::new();

0 commit comments

Comments
 (0)