-
Notifications
You must be signed in to change notification settings - Fork 64
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
chore(macro): Deprecate global state in macro system #174
base: master
Are you sure you want to change the base?
Conversation
3ebc440
to
5898aaf
Compare
@davidcole1340 what is the status here? anything i can do to help? :) |
Testing testing and testing haha. From the author, this is mainly finished |
I have actually tested it a bit today :D seems good to me |
annotated with the `#[this]` attribute. This can also be used to return a | ||
reference to `$this`. | ||
`ZendClassObject<T>` in place of the self parameter, where the parameter must | ||
be named `self_`. This can also be used to return a reference to `$this`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not requiring this
as a name?
Makes it both idiomatic and obvious to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My future self is still sad about using self_
instead of the idiomatic and less typo-prone this
... 😢
## Startup Function | ||
|
||
The `#[php_startup]` macro has been deprecated. Instead, define a function with | ||
the signature `fn(ty: i32, mod_num: i32) -> i32` and provide the function name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe explain what those numbers mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done a small explanation here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off the top of my head the module type can only be one of MODULE_PERSISTENT
or MODULE_TEMPORARY
, so it would be nicer to have a :
pub enum ModuleType {
Persistent,
Temporary,
}
impl From<i32> for ModuleType {
fn from(value: i32) -> Self {
match value => {
ffi::MODULE_PERSISTENT => Persistent,
ffi::MODULE_TEMPORARY => Temporary,
_ => panic!("onoes!")
}
}
}
// TODO: impl From<ModuleType> for i32
and then:
fn startup(ty: ModuleType, id: i32) -> Result<(), SomeErrorType> {
Ok(())
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary remains a rare case, and then the default case is totally fine for now.
Let's open another PR, after merging it
@@ -16,11 +16,10 @@ There are also additional macros that modify the class. These macros **must** be | |||
placed underneath the `#[php_class]` attribute. | |||
|
|||
- `#[extends(ce)]` - Sets the parent class of the class. Can only be used once. | |||
`ce` must be a valid Rust expression when it is called inside the | |||
`#[php_module]` function. | |||
`ce` must be a function with the signature `fn() -> &'static ClassEntry`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good move! 👍🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this change but it's not clear how you'd retrieve the ClassEntry of one of your registered classes.
There should be an example of how to do that in the documentation, i.e:
#[php_class]
pub struct Parent;
#[php_class]
#[extends(get_parent_class)]
pub struct Child;
fn get_parent_class() -> &'static ClassEntry {
// ????????
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answering to my past self:
#[php_class]
pub struct Parent;
fn get_parent_class() -> &'static ClassEntry {
<Parent as RegisteredClass>::get_metadata().ce()
}
@@ -119,6 +119,9 @@ pub trait IntoZval: Sized { | |||
/// The corresponding type of the implemented value in PHP. | |||
const TYPE: DataType; | |||
|
|||
/// Whether converting into a [`Zval`] may result in null. | |||
const NULLABLE: bool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the rationale behind these NULLABLE
s in conversion traits?
I did a bit of basic tests and so far your PR solves one of the big pain points I have with the current macro system: With this PR, now it is possible to do this: // lib.rs
mod sub;
#[php_module]
pub fn get_module(module: ModuleBuilder) -> ModuleBuilder {
sub::register(module)
} // sub/mod.rs
#[php_function]
fn hello_sub(name: &str) -> String {
format!("Hello sub, {}!", name)
}
pub fn register(module: ModuleBuilder) -> ModuleBuilder {
module.function(wrap_function!(hello_sub))
} without having to import all the things into the main module, so big 👍🏻 on this ! |
@@ -136,6 +136,7 @@ impl DropLifetimes for syn::TypePath { | |||
if let Some(qself) = &mut self.qself { | |||
qself.ty.drop_lifetimes(); | |||
} | |||
self.path.segments.drop_lifetimes(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tell me if I'm wrong but if this is the part that removes lifetimes from items, wouldn't it be better to throw an error here ?
Because currently if you try to add a lifetime to a registered class, this is the compiler message:
error[E0726]: implicit elided lifetime not allowed here
--> src/lib.rs:4:8
|
4 | struct Foo<'a> {
| ^^^ expected lifetime parameter
|
= note: assuming a `'static` lifetime...
help: indicate the anonymous lifetime
|
4 | struct Foo<'_><'a> {
| ++++
Which is rather misleading.
Given that the restrictions on lifetime and generic parameters are easily overlooked by newcomers, I think that an error stating explicitely that this is not allowed by the library would be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answering my past self: self.path.segments.drop_lifetimes()
drops lifetimes of function and method arguments.
Since I'm not fond of type conversion magic at function boundaries, I have no further opinion on whether dropping lifetime for arguments is a good thing or not.
But I still think that #[php_class]
should fail to compile with an error message like:
Lifetimes and generic parameters are not allowed on classes and class members
instead of the message seen in the previous comment (but this can be done in a later PR).
The build fails when the
|
0b8996c
to
ab0bbe9
Compare
Green CI here! Maybe have a second round of review @ju1ius @joehoyle @joelwurtz @danog |
This comment was marked as off-topic.
This comment was marked as off-topic.
@joelwurtz I'm not sure I understand... I have the feeling that you're confusing Modules are loaded from ini entries ( Extensions are loaded from ini entries ( So to me it sounds like what you'd like to implement is the hybrid extension pattern described in the book. Or maybe I'm just missing something, in which case would you mind clarifying? 😉 |
I was correctly talking about a PHP extension (zend_module_entry) but i misunderstood how sapi (and so embed) loaded themself as a PHP extension (so you could build a php binary with extra functions) But in fact this is done when using the As an example this is done by php-fpm to register it's own functions (like it's shutdown one) https://github.com/php/php-src/blob/master/sapi/fpm/fpm/fpm_main.c#L774 So disregard my comment it's off topic for this PR, sorry for the noise I was confused on how PHP builtin extension (like date or other ones) were loaded (when they are not a shared file), and in fact it's just a template header file that is computed during make which list all extensions that will be compiled directly inside binaries (php cli / fpm / embed / etc ...) see https://github.com/php/php-src/blob/master/main/internal_functions.c.in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About this move i'm 👍 even if it's add boilerplate i find this just as verbose as it should to be self understandable with code without too much work
|
||
fn startup(ty: i32, mod_num: i32) -> i32 { | ||
// register extra classes, constants etc | ||
5.register_constant("SOME_CONST", mod_num); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo ? i believe it should be in the get_module function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think not. It's the global constant declaration here, and it's mostly used as a flag for the PHP end user
see:
Lines 15 to 23 in 417f495
/// Registers a global module constant in PHP, with the value as the content | |
/// of self. This function _must_ be called in the module startup | |
/// function, which is called after the module is initialized. The | |
/// second parameter of the startup function will be the module number. | |
/// By default, the case-insensitive and persistent flags are set when | |
/// registering the constant. | |
/// | |
/// Returns a result containing nothing if the constant was successfully | |
/// registered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo ? i believe it should be in the get_module function
No this code is correct.
The get_module
function is used for registering functions, ini settings and module metadata (name, version, dependencies). Everything else must happen in startup
(AKA MINIT
), unless it need to be allocated per-request, in which case one must use request_startup
(AKA RINIT
).
EDIT: I was thinking in terms of the zend engine and forgot that this library actually lets you register everything inside get_module
(by internally delaying the registration until startup
). The code is correct nevertheless.
to the `#[php_module]` attribute: | ||
`mod_num` is the module number assigned by PHP for its internal management. The `ModuleBuilder` always defaults this parameter to `0`. | ||
`ty` is the type of module (flag) which can be set to `MODULE_PERSISTENT = 0`. | ||
The return number is for internal purposes and should always be `0` in this case. It's an internal mechanism to tell to the ModuleBuilder weither to use the defautlt startup function or the overriden one. (see: `crates/macros/src/module.rs:L35`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you shure of this ?
The module lifecycle callbacks are supposed to return a zend_result_code
(either SUCCESS
or FAILURE
), so returning something like Result<(), ()>
or Result<(), Box<dyn Error>>
would be a much better choice IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be done in seperate PR, it will require some parsing here to enforce this signature
## Startup Function | ||
|
||
The `#[php_startup]` macro has been deprecated. Instead, define a function with | ||
the signature `fn(ty: i32, mod_num: i32) -> i32` and provide the function name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off the top of my head the module type can only be one of MODULE_PERSISTENT
or MODULE_TEMPORARY
, so it would be nicer to have a :
pub enum ModuleType {
Persistent,
Temporary,
}
impl From<i32> for ModuleType {
fn from(value: i32) -> Self {
match value => {
ffi::MODULE_PERSISTENT => Persistent,
ffi::MODULE_TEMPORARY => Temporary,
_ => panic!("onoes!")
}
}
}
// TODO: impl From<ModuleType> for i32
and then:
fn startup(ty: ModuleType, id: i32) -> Result<(), SomeErrorType> {
Ok(())
}
Yes the fact that these terms are mixed and matched in the source PHP source code and documentation is rather unfortunate (mostly due to legacy I guess) and doesn't ease communication. I personnaly think that «modules vs extensions» is clearer than «PHP extensions vs Zend extensions», but to each his own I guess. WRT the actual PRSorry, I don't have the bandwidth to make an exhaustive review but from what I can remember from last year it was a clear step forward to me, so I'm all in favour of merging it while it's hot ! |
closes #131