[PATCH] - Standardize argument parsing of objects

php.internals

Bob Silva

20 years ago
Please take under consideration this patch to fully utilize cast_object handlers in the new parameter parsing API. The old parameter API forced the called function to validate and convert its arguments using the convert_to_* functions. The convert_to_* functions make use of cast_object handlers if they exist on objects which makes passing objects (which implement cast_object) to these functions possible. However, with the new parameter parsing API (zend_parse_parameters, et.al.), it only makes use of the cast_object handler if the expected type is a string. This unnecessarily weakens the capability of the cast_object handler. It makes sense that if an object implements a handler for a string cast, then it should be able to be used anywhere a string can. The patch is also attached as a .txt file in case the email really hoses it. --- zend_API.c 2005-11-03 20:26:02.000000000 -0800 +++ zend_API.c 2005-11-03 20:26:02.000000000 -0800 @@ -312,8 +312,17 @@ *p = Z_LVAL_PP(arg); break; + case IS_OBJECT: { + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { + SEPARATE_ZVAL_IF_NOT_REF(arg); + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_LONG, 0 TSRMLS_CC) == SUCCESS) { + *p = Z_LVAL_PP(arg); + break; + } + } + } + case IS_ARRAY: - case IS_OBJECT: case IS_RESOURCE: default: return "long"; @@ -346,8 +355,17 @@ *p = Z_DVAL_PP(arg); break; + case IS_OBJECT: { + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { + SEPARATE_ZVAL_IF_NOT_REF(arg); + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_DOUBLE, 0 TSRMLS_CC) == SUCCESS) { + *p = Z_DVAL_PP(arg); + break; + } + } + } + case IS_ARRAY: - case IS_OBJECT: case IS_RESOURCE: default: return "double"; @@ -408,8 +426,17 @@ *p = Z_BVAL_PP(arg); break; + case IS_OBJECT: { + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { + SEPARATE_ZVAL_IF_NOT_REF(arg); + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_BOOL, 0 TSRMLS_CC) == SUCCESS) { + *p = Z_BVAL_PP(arg); + break; + } + } + } + case IS_ARRAY: - case IS_OBJECT: case IS_RESOURCE: default: return "boolean"; @@ -434,6 +461,15 @@ case 'a': { zval **p = va_arg(*va, zval **); + if (Z_TYPE_PP(arg) == IS_OBJECT) { + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { + SEPARATE_ZVAL_IF_NOT_REF(arg); + if (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_ARRAY, 0 TSRMLS_CC) == SUCCESS) { + *p = *arg; + break; + } + } + } if (Z_TYPE_PP(arg) != IS_ARRAY) { if (Z_TYPE_PP(arg) == IS_NULL && return_null) { *p = NULL;

Marcus Börger

20 years ago
Hello Bob, this is a) wrong in the way you call the cast handler and b) we will definitively not add this behavior before the next major release aka HEAD. However it would be better to call the conversion functions (zend_operators.h) here to have get handler used when no cast handler is available to avoid inconsistencies with auto conversions in other places. I guess I know where you're heading, i am not quite sure this is essantial or even the rigth thing to for PHP yet at least for stuff like the array functions and objects that overload ArrayAccess it is more than usefull. regards marcus p.s.: You still haven't shown me any of your extension code :-/ Friday, November 4, 2005, 9:06:27 AM, you wrote:
> --- zend_API.c 2005-11-03 20:26:02.000000000 -0800 > +++ zend_API.c 2005-11-03 20:26:02.000000000 -0800 > @@ -312,8 +312,17 @@ > *p = Z_LVAL_PP(arg); > break;
> + case IS_OBJECT: { > + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { > + SEPARATE_ZVAL_IF_NOT_REF(arg); > + if > (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_LONG, 0 TSRMLS_CC) == SUCCESS) { > + *p = Z_LVAL_PP(arg); > + break; > + } > + } > + } > + > case IS_ARRAY: > - case IS_OBJECT: > case IS_RESOURCE: > default: > return "long"; > @@ -346,8 +355,17 @@ > *p = Z_DVAL_PP(arg); > break;
> + case IS_OBJECT: { > + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { > + SEPARATE_ZVAL_IF_NOT_REF(arg); > + if > (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_DOUBLE, 0 TSRMLS_CC) == SUCCESS) { > + *p = Z_DVAL_PP(arg); > + break; > + } > + } > + } > + > case IS_ARRAY: > - case IS_OBJECT: > case IS_RESOURCE: > default: > return "double"; > @@ -408,8 +426,17 @@ > *p = Z_BVAL_PP(arg); > break;
> + case IS_OBJECT: { > + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { > + SEPARATE_ZVAL_IF_NOT_REF(arg); > + if > (Z_OBJ_HANDLER_PP(arg, cast_object)(*arg, *arg, IS_BOOL, 0 TSRMLS_CC) == SUCCESS) { > + *p = Z_BVAL_PP(arg); > + break; > + } > + } > + } > + > case IS_ARRAY: > - case IS_OBJECT: > case IS_RESOURCE: > default: > return "boolean"; > @@ -434,6 +461,15 @@ > case 'a': > { > zval **p = va_arg(*va, zval **); > + if (Z_TYPE_PP(arg) == IS_OBJECT) { > + if (Z_OBJ_HANDLER_PP(arg, cast_object)) { > + SEPARATE_ZVAL_IF_NOT_REF(arg); > + if (Z_OBJ_HANDLER_PP(arg, > cast_object)(*arg, *arg, IS_ARRAY, 0 TSRMLS_CC) == SUCCESS) { > + *p = *arg; > + break; > + } > + } > + } > if (Z_TYPE_PP(arg) != IS_ARRAY) { > if (Z_TYPE_PP(arg) == IS_NULL && return_null) { > *p = NULL;
Best regards, Marcus

Bob Silva

20 years ago
Hi Marcus, OK, I got my learning cap on, why is it the wrong way to call cast_object? I just copied it from the example for a string param and it worked so I am curious why it is wrong. I agree the convert_to_* functions are a better solution for the reasons you mentioned, again, I just followed the example for the string case. I guess I don't see what this modification has to do with the future of PHP though. Why restrict the parameter API to objects that can only convert to a string value? I'd say the current behavior is almost worthy of a bug. Imagine we have a class that represents a String and supports the cast_object handler for all base types. We'll name it ZString. The following code works fine: (Ignore the fact that I should be using an integer, not a string equivalent) $str = new ZString('PHP Object Wrappers'); $start = new ZString('4'); $length = new ZString('6'); echo substr($str, $start, $length); Result: Object however, this will fail: echo wordwrap($str, $length); Result: Warning: wordwrap() expects parameter 2 to be long The only difference is the method it uses to get its parameters. substr() uses zend_get_parameters_ex and wordwrap() uses zend_parse_parameters I can work around this restriction by pulling in my parameters as zvals and doing my own conversion, but IMHO, the engine really should handle it using the built-in capabilities that already exist and aren't being used. In regards to the extension, I have the base objects complete: ZArray, ZChar, ZBoolean, ZDouble, ZInt, ZString. They all work interchangeably (for the most part) with their PHP native counterparts as long as you aren't using them on the left side of an expression. $a = new ZString('PHP '); $b = new ZString('Object '); $c = new ZString('Wrappers'); echo $a.$b.$c; (no reliance on __toString, so concatenation is possible) PHP Object Wrappers $int = new ZInt(5); echo $int + 5; 10 They make extended use of the ArrayAccess interface to implement properties and indexers. $a = new ZString('PHP'); echo $a['Length']; 3 echo $a[0]; P echo $a['Chars']{1}; H echo $a->offsetGet(2); P Properties are also supported for derived user classes by defining accessors. class MyClass extends ZObject { function setMyProp($value) { if ($value is valid) $this->_props['MyProp'] = $value; } function getMyProp() { return $this->_props['MyProp']; } } $e = new MyClass; $e['MyProp'] = 'PHP Object Wrappers'; echo $e['MyProp']; Where appropriate, they implement Iterator or IteratorAggregate with C# type wrappers. foreach ($a as $c) echo $c; PHP $enum = $a->GetEnumerator(); while ($enum->MoveNext()) echo $enum->GetCurrent(); PHP Its also completely exception based, no PHP errors surface during runtime. As you can tell, I am using C# as my model for the objects. They actually flow pretty well in a real application as well. One last addition that would be cool but likely to never happen in the PHP distro is the following: $a = "some string"; $a->Substring(5,1); $b = 5; $b->Equals(3); (false) $b++; $b->Equals(6); (true) echo gettype($b); "object" Implicit conversion via cast handlers. I still have a couple weeks of work left on them. This is my first dabble in C so I have a lot of cleanup to do as well as learn how to turn it into a PECL extension. Why program if you can't have fun doing it? Bob

Marcus Börger

20 years ago
Hello Bob, Friday, November 4, 2005, 11:19:23 AM, you wrote:
> Hi Marcus,
> OK, I got my learning cap on, why is it the wrong way to call cast_object? I > just copied it from the example for a string param and it worked so I am > curious why it is wrong. I agree the convert_to_* functions are a better > solution for the reasons you mentioned, again, I just followed the example > for the string case.
> I guess I don't see what this modification has to do with the future of PHP > though. Why restrict the parameter API to objects that can only convert to a > string value? I'd say the current behavior is almost worthy of a bug.
[...]
> In regards to the extension, I have the base objects complete: ZArray, > ZChar, ZBoolean, ZDouble, ZInt, ZString. They all work interchangeably (for > the most part) with their PHP native counterparts as long as you aren't > using them on the left side of an expression.
And that is where get/set handlers come into play and why calling cat handler alone is incorrect. Apart from that the 5 api has the parameter should_free which should be non null if read and write zval are the same so that the handler knows it must call zval_dtor on the readval prior to assigning the new value. Since this interface is borked it has changed in head. There read and write zval must be different and the caller has to take care about any required free/destruction. That you had a look into the stuff and haven't come accross this is again proof that the old api was not only bork but also used wrong. Best regards, Marcus

Bob Silva

20 years ago
Hi Marcus, I think I understand what you are saying. I've added get and set handlers to my objects and can now achieve this: $a = new ZInt(5); $a += 1; echo ($a->Equals(6)) ? 'true':'false'; =true as well as: $a = new ZChar('A'); $a++; echo ($a->Equals('B')) ? 'true':'false'; =true Thanks for helping me understand this. An unexpected side-effect is that by declaring my objects ahead of time like a typed language, I can use natives in an object context which is what I was looking to achieve. I still have a lot of testing to do though. $a = new ZString('PHP'); $a .= ' Object Wrappers'; echo $a->ToString(); =PHP Object Wrappers My ZString objects are immutable, $a is actually a new ZString object after the concatenation. The parameter API (even in HEAD) is still a stumbling block though since it only converts objects for "syuTt" specifiers and not the others ("l", "b", "d" and preferably "a" which doesn't look possible without breaking BC since it relies on the get_properties handler first and convert_object_to_type second). Of course any good language provides a work around: (casting ahead of time) echo wordwrap(new ZString('PHP is cool'), (int)new ZInt(3)); =PHP is cool I really don't know if this will be useful for anyone else but it sure is fun programming it and I'm learning a lot. People that are looking for a strongly typed PHP may find it useful once I complete the rest of the object framework. Next up is Collections. Again, I really appreciate you taking the time to reply to my questions and point me in the right direction. Bob

Jessie Hernandez

20 years ago
Hi Bob, BTW, several months back I started working on a similar String class (you can see my post at http://news.php.net/php.pecl.dev/2512). It was meant as an OOP interface to all of the string-handling functions, and also had smart buffer management for speed. I started working on this hoping to have it be a part of the core, but at the time I was presented with several objections (most of them were because of the generic name chosen, "String", and because no one wanted it in the core, only on PECL). Even then, I was advised to wait for the Unicode changes to be merged before I continued (don't know if the Unicode work is done yet). I stopped working on this extension shortly thereafter and started working on the namespace patch (which is really more important anyways). I can post what I had done at the time if you want to take a look at it (I think I left the code at work, so it would have to wait till Monday). (IMHO, I still think a core String class would be very useful. There are many extensions that provide both a procedural and OOP interface. Take for example SQLite. You can either use the sqlite_* functions or use the SQLiteDatabase class. I don't see why strings should be any different.) Regards, Jessie Bob Silva wrote:

Bob Silva

20 years ago
Hi Jessie, I'd be more than happy to get some examples of how others would implement a string class. I am doing most of my modeling after the .NET 2.0 framework for my objects. I find it to be a well organized and capable framework, especially the new ASP.NET 2.0 features like the Role Manager. It also saves on my documentation, just go look at msdn2.microsoft.com. :) kidding I started this just for fun and an introduction to C, but I am planning on putting into PECL when it is stable, which is quite far off for the whole framework. Should be ready in time for PHP6. I was just reading all the Unicode docs tonight preparing to start my conversion to support it. Gets quite messy pretty fast. Any word on whether the core team is going to accept the namespace patch? It will be beneficial to my development as well for obvious reasons. Bob

Wez Furlong

20 years ago
On 11/5/05, Bob Silva <me@bobsilva.com> wrote:
> Any word on whether the core team is going to accept the namespace patch? It > will be beneficial to my development as well for obvious reasons.
That's one of the things we'll be discussing in Paris next week.
> I was just reading all the Unicode docs tonight preparing to start my > conversion to support it. Gets quite messy pretty fast.
I'd hold off from writing any code for that just yet; we're also discussing some of the unicode implementation details next week. --Wez.

Jessie Hernandez

20 years ago
Bob, As mentioned, attached is what I had for the String class (have not touched the code for many months now). It contains the methods below. What I was aiming for was to have the best of the C++ and Java String methods, plus some new methods named after the PHP-specific string functions. __construct __toString append clear contains empty indexOf length makeCopy reserve startsWith toLowerCase toUpperCase trim Regards, Jessie Hernandez ""Bob Silva"" <me@bobsilva.com> wrote in message news:000201c5e1d5$1f85c030$5d54edc6@jake...
> Hi Jessie, > > I'd be more than happy to get some examples of how others would implement
a
> string class. I am doing most of my modeling after the .NET 2.0 framework > for my objects. I find it to be a well organized and capable framework, > especially the new ASP.NET 2.0 features like the Role Manager. It also
saves
> on my documentation, just go look at msdn2.microsoft.com. :) kidding > > I started this just for fun and an introduction to C, but I am planning on > putting into PECL when it is stable, which is quite far off for the whole > framework. Should be ready in time for PHP6. > > I was just reading all the Unicode docs tonight preparing to start my > conversion to support it. Gets quite messy pretty fast. > > Any word on whether the core team is going to accept the namespace patch?
It
> will be beneficial to my development as well for obvious reasons. > > Bob > > > > > -----Original Message----- > > From: Jessie Hernandez [mailto:jrhernandez05@gmail.com] > > Sent: Friday, November 04, 2005 9:35 PM > > To: internals@lists.php.net > > Subject: RE: [PHP-DEV] [PATCH] - Standardize argument parsing of objects > > > > Hi Bob, > > > > BTW, several months back I started working on a similar String class
(you
> > can see my post at http://news.php.net/php.pecl.dev/2512). It was meant
as
> > an OOP interface to all of the string-handling functions, and also had > > smart buffer management for speed. I started working on this hoping to > > have > > it be a part of the core, but at the time I was presented with several > > objections (most of them were because of the generic name chosen, > > "String", > > and because no one wanted it in the core, only on PECL). Even then, I
was
> > advised to wait for the Unicode changes to be merged before I continued > > (don't know if the Unicode work is done yet). > > > > I stopped working on this extension shortly thereafter and started
working
> > on the namespace patch (which is really more important anyways). I can > > post > > what I had done at the time if you want to take a look at it (I think I > > left the code at work, so it would have to wait till Monday). > > > > (IMHO, I still think a core String class would be very useful. There are > > many extensions that provide both a procedural and OOP interface. Take
for
> > example SQLite. You can either use the sqlite_* functions or use the > > SQLiteDatabase class. I don't see why strings should be any different.) > > > > > > Regards, > > > > Jessie > > > > > > Bob Silva wrote: > > > > > Hi Marcus, > > > > > > I think I understand what you are saying. I've added get and set > > handlers > > > to my objects and can now achieve this: > > > > > > $a = new ZInt(5); > > > $a += 1; > > > echo ($a->Equals(6)) ? 'true':'false'; > > > > > > =true > > > > > > as well as: > > > > > > $a = new ZChar('A'); > > > $a++; > > > echo ($a->Equals('B')) ? 'true':'false'; > > > > > > =true > > > > > > Thanks for helping me understand this. An unexpected side-effect is
that
> > > by declaring my objects ahead of time like a typed language, I can use > > > natives in an object context which is what I was looking to achieve. I > > > still have a lot of testing to do though. > > > > > > $a = new ZString('PHP'); > > > $a .= ' Object Wrappers'; > > > echo $a->ToString(); > > > > > > =PHP Object Wrappers > > > > > > My ZString objects are immutable, $a is actually a new ZString object > > > after the concatenation. > > > > > > The parameter API (even in HEAD) is still a stumbling block though
since
> > > it only converts objects for "syuTt" specifiers and not the others
("l",
> > > "b", "d" and preferably "a" which doesn't look possible without
breaking
> > > BC since it relies on the get_properties handler first and > > > convert_object_to_type second). > > > > > > Of course any good language provides a work around: (casting ahead of > > > time) > > > > > > echo wordwrap(new ZString('PHP is cool'), (int)new ZInt(3)); > > > =PHP > > > is > > > cool > > > > > > > > > I really don't know if this will be useful for anyone else but it sure > > is > > > fun programming it and I'm learning a lot. People that are looking for
a
> > > strongly typed PHP may find it useful once I complete the rest of the > > > object framework. Next up is Collections. Again, I really appreciate
you
> > > taking the time to reply to my questions and point me in the right > > > direction. > > > > > > Bob > > > > > > > > > > > > > > >> -----Original Message----- > > >> From: Marcus Boerger [mailto:helly@php.net] > > >> Sent: Friday, November 04, 2005 10:59 AM > > >> To: Bob Silva > > >> Cc: internals@lists.php.net > > >> Subject: Re: [PHP-DEV] [PATCH] - Standardize argument parsing of > > objects > > >> > > >> Hello Bob, > > >> > > >> Friday, November 4, 2005, 11:19:23 AM, you wrote: > > >> > > >> > Hi Marcus, > > >> > > >> > OK, I got my learning cap on, why is it the wrong way to call > > >> cast_object? I > > >> > just copied it from the example for a string param and it worked so
I
> > >> > am curious why it is wrong. I agree the convert_to_* functions are
a
> > >> > better solution for the reasons you mentioned, again, I just
followed
> > >> > the > > >> example > > >> > for the string case. > > >> > > >> > I guess I don't see what this modification has to do with the
future
> > of > > >> PHP > > >> > though. Why restrict the parameter API to objects that can only > > convert > > >> to a > > >> > string value? I'd say the current behavior is almost worthy of a
bug.
> > >> > > >> [...] > > >> > > >> > In regards to the extension, I have the base objects complete: > > ZArray, > > >> > ZChar, ZBoolean, ZDouble, ZInt, ZString. They all work > > interchangeably > > >> (for > > >> > the most part) with their PHP native counterparts as long as you > > aren't > > >> > using them on the left side of an expression. > > >> > > >> And that is where get/set handlers come into play and why calling cat > > >> handler alone is incorrect. Apart from that the 5 api has the
parameter
> > >> should_free which should be non null if read and write zval are the > > same > > >> so that the handler knows it must call zval_dtor on the readval prior > > to > > >> assigning the new value. Since this interface is borked it has
changed
> > >> in head. There read and write zval must be different and the caller
has