Skip to content

Commit

Permalink
Class Create thru generated register_class*
Browse files Browse the repository at this point in the history
Signed-off-by: Dusan Malusev <dusan@dusanmalusev.dev>
  • Loading branch information
CodeLieutenant committed Jun 20, 2024
1 parent 03f3412 commit e15940e
Show file tree
Hide file tree
Showing 18 changed files with 263 additions and 219 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/complex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ name = "complex"
crate-type = ["lib", "cdylib"]

[dependencies]
paste = "^1.0.15"
phper = { workspace = true }

[dev-dependencies]
Expand Down
1 change: 0 additions & 1 deletion examples/complex/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ fn main() {
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
let stubs = current_dir.join("stubs");


phper_build::generate_php_function_args(&out_path, &[&stubs], None).unwrap();
}
2 changes: 1 addition & 1 deletion examples/complex/src/args_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
#![allow(non_snake_case)]
#![allow(deref_nullptr)]
#![allow(clippy::all)]
include!(concat!(env!("OUT_DIR"), "/php_args_bindings.rs"));
include!(concat!(env!("OUT_DIR"), "/php_args_bindings.rs"));
8 changes: 5 additions & 3 deletions examples/complex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ use std::ffi::CStr;

use args_bindings::{
arginfo_Complex_get_all_ini, arginfo_Complex_say_hello, arginfo_Complex_throw_exception,
register_class_Complex_Foo,
};

use phper::arrays::ZArray;
use phper::classes::ClassEntity;
use phper::ini::{ini_get, Policy};
use phper::{modules::Module, php_get_module, values::ZVal, zend_args};
use phper::{modules::Module, php_get_module, values::ZVal, zend_args, zend_create_fn};

fn say_hello(arguments: &mut [ZVal]) -> phper::Result<String> {
let name = &mut arguments[0];
name.convert_to_string();
let name = name.as_z_str().unwrap().to_str()?;
Ok(format!("Hello, {}!\n", name))
Ok(format!("Hello, {name}!\n"))
}

fn throw_exception(_: &mut [ZVal]) -> phper::Result<()> {
Expand Down Expand Up @@ -85,7 +87,7 @@ pub fn get_module() -> Module {

//
// // register classes
// let mut foo_class = ClassEntity::new("FooClass");
let mut foo_class = ClassEntity::new(zend_create_fn!(register_class_Complex_Foo));
// foo_class.add_property("foo", Visibility::Private, 100);
// foo_class.add_method(
// "getFoo",
Expand Down
13 changes: 13 additions & 0 deletions examples/complex/stubs/say_hello.stub.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

/** @generate-class-entries */

namespace Complex {
function say_hello(string $name): string {}

Expand All @@ -10,4 +12,15 @@ function throw_exception(): void
function get_all_ini(): array
{
}


/**
* @strict-properties
*/
class Foo {
private int|\JsonSerializable|\ArrayAccess $foo = 100;
public function getFoo(): int {}

public function setFoo(int $foo): void {}
}
}
3 changes: 2 additions & 1 deletion phper-build/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ fn main() {
std::fs::copy(
current_dir().unwrap().join("gen_stub.php"),
out_path.join("gen_stub.php"),
).unwrap();
)
.unwrap();
}
163 changes: 76 additions & 87 deletions phper-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,11 @@ fn create_builder() -> Result<(Build, Builder), Box<dyn std::error::Error>> {
.generate_inline_functions(true)
.generate_block(true)
.generate_comments(true)
.allowlist_type("zend_internal_arg_info")
.allowlist_type("zend_type")
.allowlist_var("arginfo_*")
.wrap_unsafe_ops(true)
.array_pointers_in_arguments(true)
.generate_cstr(true);

let mut cc = cc::Build::new();
let mut cc = Build::new();

for dir in includes.iter() {
cc.flag(dir);
Expand Down Expand Up @@ -122,18 +119,15 @@ pub fn generate_php_function_args<P: AsRef<Path>, Q: AsRef<Path>>(
let gen_stub_php = gen_stub_php.as_os_str().to_str().unwrap();

for dir in dirs {
let dir_name = dir.as_ref().to_str().unwrap();

std::process::Command::new(php_exec.unwrap_or("php"))
.args([gen_stub_php, dir_name])
Command::new(php_exec.unwrap_or("php"))
.args([gen_stub_php, dir.as_ref().to_str().unwrap()])
.output()?;
}

let mut header = String::with_capacity(64 * 1024);
let mut c_file = String::with_capacity(64 * 1024);

header.push_str("#pragma once\n\n#include <php.h>\n\n",);
c_file.push_str("#include <php.h>\n\nBEGIN_EXTERN_C()\n\n");
header.push_str("#pragma once\n\n#include <php.h>\n\n");
c_file.push_str("#include <php.h>\n\nBEGIN_EXTERN_C()\n#define static\n\n");

for dir in dirs {
let dir = dir.as_ref();
Expand All @@ -154,23 +148,21 @@ pub fn generate_php_function_args<P: AsRef<Path>, Q: AsRef<Path>>(

let contents = std::fs::read_to_string(path)?;

match extract_arginfo_size_and_name(&contents) {
Some(vals) => write_header(&mut header, &vals),
None => continue,
if extract_headers_and_c_file(&mut header, &mut c_file, contents).is_none() {
continue;
}

c_file.push_str(&contents);
std::fs::remove_file(path)?;
}
}

c_file.push_str("END_EXTERN_C()\n\n");
c_file.push_str("#undef static\nEND_EXTERN_C()\n");

let php_args_binding_h_path = output_dir.join("php_args_bindings.h");
std::fs::write(&php_args_binding_h_path, &header)?;

let php_args_binding_c_path = output_dir.join("php_args_bindings.c");
std::fs::write(&php_args_binding_c_path, format!("\n\n{}\n\n", c_file))?;
std::fs::write(&php_args_binding_c_path, c_file)?;

let (mut cc, builder) = create_builder()?;

Expand All @@ -187,43 +179,80 @@ pub fn generate_php_function_args<P: AsRef<Path>, Q: AsRef<Path>>(
Ok(())
}

fn write_header(header: &mut String, content: &[(&str, usize)]) {
content.iter().for_each(|(name, count)| {
header.push_str("extern zend_internal_arg_info ");
header.push_str(name);
header.push('[');
header.push_str(count.to_string().as_str());
header.push_str("];\n\n");
})
}

fn extract_arginfo_size_and_name(input: &str) -> Option<Vec<(&str, usize)>> {
fn extract_headers_and_c_file(
header: &mut String,
c_file: &mut String,
contents: String,
) -> Option<()> {
let mut result = Vec::new();

let mut name = "";
let mut counter = 0;

for line in input.lines() {
let line = line.trim();
if line.starts_with("ZEND_BEGIN_") {
for line in contents.lines() {
let trimmed_line = line.trim();

if trimmed_line.starts_with("ZEND_FUNCTION")
|| trimmed_line.starts_with("ZEND_METHOD")
|| trimmed_line.starts_with("static const zend_function_entry ext_functions[]")
|| trimmed_line.starts_with("static const zend_function_entry class_")
|| trimmed_line.starts_with("ZEND_ME")
|| trimmed_line.starts_with("ZEND_FE_END")
|| trimmed_line.starts_with("ZEND_FE")
|| trimmed_line.starts_with("ZEND_NS_")
|| trimmed_line.starts_with("};")
{
continue;
}

if trimmed_line.contains("zend_class_entry *register_") {
header.push_str("extern ");
header.push_str(&trimmed_line["static ".len()..]);
header.push(';');
header.push('\n');
}

if trimmed_line.contains("_methods);") {
let last_comma = trimmed_line.rfind(',').unwrap();
c_file.push_str(&trimmed_line[..last_comma]);
c_file.push_str(", NULL);");
} else if trimmed_line.starts_with("static ") {
c_file.push_str(trimmed_line.strip_prefix("static ").unwrap());
} else {
c_file.push_str(trimmed_line);
}

c_file.push('\n');

if trimmed_line.starts_with("ZEND_BEGIN_") {
let start = line.find("arginfo_")?;
let end = line.find(',')?;

name = &line[start..end];
}

if line.starts_with("ZEND_ARG_") {
if trimmed_line.starts_with("ZEND_ARG_") {
counter += 1;
}

if line.starts_with("ZEND_END_ARG_INFO") {
if trimmed_line.starts_with("ZEND_END_ARG_INFO") {
result.push((name, counter + 1));
counter = 0;
name = "";
}
}

Some(result)
if !result.is_empty() {
result.iter().for_each(|(name, count)| {
header.push_str("extern const zend_internal_arg_info ");
header.push_str(name);
header.push('[');
header.push_str(count.to_string().as_str());
header.push_str("];\n");
});
}

Some(())
}

#[cfg(test)]
Expand All @@ -232,59 +261,19 @@ mod tests {
use super::*;

#[test]
fn test_write_header() {
fn test_extract_content_and_header() {
let mut header = String::new();
let content = vec![
("arginfo_Complex_say_hello", 1),
("arginfo_Complex_say_hello2", 5),
];

write_header(&mut header, &content);

assert_eq!(
header,
r#"extern zend_internal_arg_info arginfo_Complex_say_hello[1];
extern zend_internal_arg_info arginfo_Complex_say_hello2[5];
"#
);
}

#[test]
fn test_extract_arginfo_size_and_name() {
let input = r#"
#pragma once
#include <php.h>
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Complex_say_hello, 0, 1,
IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Complex_say_hello2, 0, 1,
IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_END_ARG_INFO()
"#;

let result = extract_arginfo_size_and_name(input);

assert!(result.is_some());

let result = result.unwrap();

assert_eq!(result.len(), 2);

assert_eq!("arginfo_Complex_say_hello", result[0].0);
assert_eq!(2, result[0].1);

assert_eq!("arginfo_Complex_say_hello2", result[1].0);
assert_eq!(5, result[1].1)
let mut c_file = String::new();

const INPUT: &str =
include_str!("../tests/test_extract_content_and_header/say_hello_arginfo.h");
const EXPECTED_HEADER: &str =
include_str!("../tests/test_extract_content_and_header/expected_header");
const EXPECTED_C_FILE: &str =
include_str!("../tests/test_extract_content_and_header/expected_c_file");

assert!(extract_headers_and_c_file(&mut header, &mut c_file, INPUT.into()).is_some());
assert_eq!(EXPECTED_HEADER, header.as_str());
assert_eq!(EXPECTED_C_FILE, c_file.as_str());
}
}
2 changes: 2 additions & 0 deletions phper-build/tests/stubs/test.stub.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<?php
/** @generate-class-entries */
/** @generate-function-entries */

function hello(string $name): string {}
30 changes: 30 additions & 0 deletions phper-build/tests/test_extract_content_and_header/expected_c_file
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Complex_say_hello, 0, 1, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_END_ARG_INFO()




zend_class_entry *register_class_Complex_Foo(void)
{
zend_class_entry ce, *class_entry;

INIT_NS_CLASS_ENTRY(ce, "Complex", "Foo", NULL);
class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES;

zval property_foo_default_value;
ZVAL_LONG(&property_foo_default_value, 100);
zend_string *property_foo_name = zend_string_init("foo", sizeof("foo") - 1, 1);
zend_string *property_foo_class_JsonSerializable = zend_string_init("JsonSerializable", sizeof("JsonSerializable") - 1, 1);
zend_string *property_foo_class_ArrayAccess = zend_string_init("ArrayAccess", sizeof("ArrayAccess") - 1, 1);
zend_type_list *property_foo_type_list = malloc(ZEND_TYPE_LIST_SIZE(2));
property_foo_type_list->num_types = 2;
property_foo_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_foo_class_JsonSerializable, 0, 0);
property_foo_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_foo_class_ArrayAccess, 0, 0);
zend_type property_foo_type = ZEND_TYPE_INIT_UNION(property_foo_type_list, MAY_BE_LONG);
zend_declare_typed_property(class_entry, property_foo_name, &property_foo_default_value, ZEND_ACC_PRIVATE, NULL, property_foo_type);
zend_string_release(property_foo_name);

return class_entry;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
extern zend_class_entry *register_class_Complex_Foo(void);
extern const zend_internal_arg_info arginfo_Complex_say_hello[2];
Loading

0 comments on commit e15940e

Please sign in to comment.