Skip to content

Add IniBuilder #442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion allowed_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,5 +313,10 @@ bind! {
php_module_shutdown,
php_request_startup,
php_request_shutdown,
instanceof_function_slow
instanceof_function_slow,
php_ini_builder,
php_ini_builder_prepend,
php_ini_builder_unquoted,
php_ini_builder_quoted,
php_ini_builder_define
}
132 changes: 101 additions & 31 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@ use std::{
str::FromStr,
};

use anyhow::{anyhow, bail, Context, Result};
use anyhow::{anyhow, bail, Context, Error, Result};
use bindgen::RustTarget;
use impl_::Provider;

const MIN_PHP_API_VER: u32 = 2020_09_30;
const MAX_PHP_API_VER: u32 = 2024_09_24;

/// Provides information about the PHP installation.
pub trait PHPProvider<'a>: Sized {
/// Create a new PHP provider.
Expand Down Expand Up @@ -170,6 +167,20 @@ impl PHPInfo {
}
}

fn add_php_version_defines(
defines: &mut Vec<(&'static str, &'static str)>,
info: &PHPInfo,
) -> Result<()> {
let version = info.zend_version()?;
let supported_version: ApiVersion = version.try_into()?;

for supported_api in supported_version.supported_apis() {
defines.push((supported_api.define_name(), "1"));
}

Ok(())
}

/// Builds the wrapper library.
fn build_wrapper(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result<()> {
let mut build = cc::Build::new();
Expand Down Expand Up @@ -249,19 +260,90 @@ fn generate_bindings(defines: &[(&str, &str)], includes: &[PathBuf]) -> Result<S
Ok(bindings)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)]
enum ApiVersion {
Php80 = 2020_09_30,
Php81 = 2021_09_02,
Php82 = 2022_08_29,
Php83 = 2023_08_31,
Php84 = 2024_09_24,
}

impl ApiVersion {
/// Returns the minimum API version supported by ext-php-rs.
pub const fn min() -> Self {
ApiVersion::Php80
}

/// Returns the maximum API version supported by ext-php-rs.
pub const fn max() -> Self {
ApiVersion::Php84
}

pub fn versions() -> Vec<Self> {
vec![
ApiVersion::Php80,
ApiVersion::Php81,
ApiVersion::Php82,
ApiVersion::Php83,
ApiVersion::Php84,
]
}

/// Returns the API versions that are supported by this version.
pub fn supported_apis(self) -> Vec<ApiVersion> {
ApiVersion::versions()
.into_iter()
.filter(|&v| v <= self)
.collect()
}

pub fn cfg_name(self) -> &'static str {
match self {
ApiVersion::Php80 => "php80",
ApiVersion::Php81 => "php81",
ApiVersion::Php82 => "php82",
ApiVersion::Php83 => "php83",
ApiVersion::Php84 => "php84",
}
}

pub fn define_name(self) -> &'static str {
match self {
ApiVersion::Php80 => "EXT_PHP_RS_PHP_80",
ApiVersion::Php81 => "EXT_PHP_RS_PHP_81",
ApiVersion::Php82 => "EXT_PHP_RS_PHP_82",
ApiVersion::Php83 => "EXT_PHP_RS_PHP_83",
ApiVersion::Php84 => "EXT_PHP_RS_PHP_84",
}
}
}

impl TryFrom<u32> for ApiVersion {
type Error = Error;

fn try_from(version: u32) -> Result<Self, Self::Error> {
match version {
x if (2020_09_30..2021_09_02).contains(&x) => Ok(ApiVersion::Php80),
x if (2021_09_02..2022_08_29).contains(&x) => Ok(ApiVersion::Php81),
x if (2022_08_29..2023_08_31).contains(&x) => Ok(ApiVersion::Php82),
x if (2023_08_31..2024_09_24).contains(&x) => Ok(ApiVersion::Php83),
2024_09_24 => Ok(ApiVersion::Php84),
version => Err(anyhow!(
"The current version of PHP is not supported. Current PHP API version: {}, requires a version between {} and {}",
version,
ApiVersion::min() as u32,
ApiVersion::max() as u32
))
}
}
}

/// Checks the PHP Zend API version for compatibility with ext-php-rs, setting
/// any configuration flags required.
fn check_php_version(info: &PHPInfo) -> Result<()> {
const PHP_81_API_VER: u32 = 2021_09_02;
const PHP_82_API_VER: u32 = 2022_08_29;
const PHP_83_API_VER: u32 = 2023_08_31;
const PHP_84_API_VER: u32 = 2024_09_24;

let version = info.zend_version()?;

if !(MIN_PHP_API_VER..=MAX_PHP_API_VER).contains(&version) {
bail!("The current version of PHP is not supported. Current PHP API version: {}, requires a version between {} and {}", version, MIN_PHP_API_VER, MAX_PHP_API_VER);
}
let version: ApiVersion = version.try_into()?;

// Infra cfg flags - use these for things that change in the Zend API that don't
// rely on a feature and the crate user won't care about (e.g. struct field
Expand All @@ -275,26 +357,13 @@ fn check_php_version(info: &PHPInfo) -> Result<()> {
println!(
"cargo::rustc-check-cfg=cfg(php80, php81, php82, php83, php84, php_zts, php_debug, docs)"
);
println!("cargo:rustc-cfg=php80");

if (MIN_PHP_API_VER..PHP_81_API_VER).contains(&version) {
println!("cargo:warning=PHP version 8.0 is EOL and will no longer be supported in a future release. Please upgrade to a supported version of PHP. See https://www.php.net/supported-versions.php for information on version support timelines.");
}

if version >= PHP_81_API_VER {
println!("cargo:rustc-cfg=php81");
}

if version >= PHP_82_API_VER {
println!("cargo:rustc-cfg=php82");
}

if version >= PHP_83_API_VER {
println!("cargo:rustc-cfg=php83");
if version == ApiVersion::Php80 {
println!("cargo:warning=PHP 8.0 is EOL and will no longer be supported in a future release. Please upgrade to a supported version of PHP. See https://www.php.net/supported-versions.php for information on version support timelines.");
}

if version >= PHP_84_API_VER {
println!("cargo:rustc-cfg=php84");
for supported_version in version.supported_apis() {
println!("cargo:rustc-cfg={}", supported_version.cfg_name());
}

Ok(())
Expand Down Expand Up @@ -337,7 +406,8 @@ fn main() -> Result<()> {
let provider = Provider::new(&info)?;

let includes = provider.get_includes()?;
let defines = provider.get_defines()?;
let mut defines = provider.get_defines()?;
add_php_version_defines(&mut defines, &info)?;

check_php_version(&info)?;
build_wrapper(&defines, &includes)?;
Expand Down
39 changes: 39 additions & 0 deletions docsrs_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2732,3 +2732,42 @@ pub struct _sapi_post_entry {
),
>,
}
#[doc = " A class which helps with constructing INI entries from the command\n line."]
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct php_ini_builder {
pub value: *mut ::std::os::raw::c_char,
pub length: usize,
}
extern "C" {
#[doc = " Prepend a string.\n\n @param src the source string\n @param length the size of the source string"]
pub fn php_ini_builder_prepend(
b: *mut php_ini_builder,
src: *const ::std::os::raw::c_char,
length: usize,
);
}
extern "C" {
#[doc = " Append an unquoted name/value pair."]
pub fn php_ini_builder_unquoted(
b: *mut php_ini_builder,
name: *const ::std::os::raw::c_char,
name_length: usize,
value: *const ::std::os::raw::c_char,
value_length: usize,
);
}
extern "C" {
#[doc = " Append a quoted name/value pair."]
pub fn php_ini_builder_quoted(
b: *mut php_ini_builder,
name: *const ::std::os::raw::c_char,
name_length: usize,
value: *const ::std::os::raw::c_char,
value_length: usize,
);
}
extern "C" {
#[doc = " Parse an INI entry from the command-line option \"--define\"."]
pub fn php_ini_builder_define(b: *mut php_ini_builder, arg: *const ::std::os::raw::c_char);
}
38 changes: 38 additions & 0 deletions guide/src/ini-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# INI Builder

When configuring a SAPI you may use `IniBuilder` to load INI settings as text.
This is useful for setting up configurations required by the SAPI capabilities.

INI settings applied to a SAPI through `sapi.ini_entries` will be immutable,
meaning they cannot be changed at runtime. This is useful for applying settings
to match hard requirements of the way your SAPI works.

To apply _configurable_ defaults it is recommended to use a `sapi.ini_defaults`
callback instead, which will allow settings to be changed at runtime.

```rust,no_run,ignore
use ext_php_rs::builder::{IniBuilder, SapiBuilder};

# fn main() {
// Create a new IniBuilder instance.
let mut builder = IniBuilder::new();

// Append a single key/value pair to the INIT buffer with an unquoted value.
builder.unquoted("log_errors", "1");

// Append a single key/value pair to the INI buffer with a quoted value.
builder.quoted("default_mimetype", "text/html");

// Append INI line text as-is. A line break will be automatically appended.
builder.define("memory_limit=128MB");

// Prepend INI line text as-is. No line break insertion will occur.
builder.prepend("error_reporting=0\ndisplay_errors=1\n");

// Construct a SAPI.
let mut sapi = SapiBuilder::new("name", "pretty_name").build()
.expect("should build SAPI");

// Dump INI entries from the builder into the SAPI.
sapi.ini_entries = builder.finish();
# }
Loading
Loading