1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
|
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: 297078 $ -->
<sect2 xml:id="internals2.ze1.zendapi.arguments" xmlns="http://docbook.org/ns/docbook">
<title>Accepting Arguments</title>
<para>
One of the most important issues for language extensions is
accepting and dealing with data passed via arguments. Most
extensions are built to deal with specific input data (or require
parameters to perform their specific actions), and function
arguments are the only real way to exchange data between the PHP
level and the C level. Of course, there's also the possibility of
exchanging data using predefined global values (which is also
discussed later), but this should be avoided by all means, as it's
extremely bad practice.
</para>
<para>
PHP doesn't make use of any formal function declarations; this is
why call syntax is always completely dynamic and never checked for
errors. Checking for correct call syntax is left to the user code.
For example, it's possible to call a function using only one
argument at one time and four arguments the next time - both
invocations are syntactically absolutely correct.
</para>
<sect3 xml:id="internals2.ze1.zendapi.arguments.count">
<title>Determining the Number of Arguments</title>
<para>
Since PHP doesn't have formal function definitions with support
for call syntax checking, and since PHP features variable
arguments, sometimes you need to find out with how many arguments
your function has been called. You can use the
<literal>ZEND_NUM_ARGS</literal> macro in this case. In previous
versions of PHP, this macro retrieved the number of arguments with
which the function has been called based on the function's hash
table entry, <envar>ht</envar>, which is passed in the
<literal>INTERNAL_FUNCTION_PARAMETERS</literal> list. As
<envar>ht</envar> itself now contains the number of arguments that
have been passed to the function, <literal>ZEND_NUM_ARGS</literal>
has been stripped down to a dummy macro (see its definition in
<filename>zend_API.h</filename>). But it's still good practice to
use it, to remain compatible with future changes in the call
interface. <emphasis>Note:</emphasis> The old PHP equivalent of
this macro is <literal>ARG_COUNT</literal>.
</para>
<para>
The following code checks for the correct number of arguments:
<programlisting role="c">
if(ZEND_NUM_ARGS() != 2) WRONG_PARAM_COUNT;
</programlisting>
If the function is not called with two
arguments, it exits with an error message. The code snippet above
makes use of the tool macro <literal>WRONG_PARAM_COUNT</literal>,
which can be used to generate a standard error message like:
<![CDATA[
"Warning: Wrong parameter count for firstmodule() in /home/www/htdocs/firstmod.php on line 5"
]]>
</para>
<para>
This macro prints a default error message and then returns to the caller.
Its definition can also be found in <filename>zend_API.h</filename> and looks
like this:
<programlisting role="c">
<![CDATA[
ZEND_API void wrong_param_count(void);
#define WRONG_PARAM_COUNT { wrong_param_count(); return; }
]]>
</programlisting>
As you can see, it calls an internal function
named <function>wrong_param_count</function> that's responsible for printing
the warning. For details on generating customized error
messages, see the later section "Printing Information."
</para>
</sect3>
<sect3 xml:id="internals2.ze1.zendapi.arguments.retrieval">
<title>Retrieving Arguments</title>
<note>
<title>
New parameter parsing API
</title>
<para>
This chapter documents the new Zend parameter parsing API
introduced by Andrei Zmievski. It was introduced in the
development stage between PHP 4.0.6 and 4.1.0.
</para>
</note>
<para>
Parsing parameters is a very common operation and it may get a bit
tedious. It would also be nice to have standardized error checking
and error messages. Since PHP 4.1.0, there is a way to do just
that by using the new parameter parsing API. It greatly simplifies
the process of receiving parameters, but it has a drawback in
that it can't be used for functions that expect variable number of
parameters. But since the vast majority of functions do not fall
into those categories, this parsing API is recommended as the new
standard way.
</para>
<para>
The prototype for parameter parsing function looks like this:
<programlisting role="c">
<![CDATA[
int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...);
]]>
</programlisting>
The first argument to this function is supposed to be the number
of actual parameters passed to your function, so
<literal>ZEND_NUM_ARGS()</literal> can be used for that. The
second parameter should always be <literal>TSRMLS_CC</literal>
macro. The third argument is a string that specifies the number
and types of arguments your function is expecting, similar to how
printf format string specifies the number and format of the output
values it should operate on. And finally the rest of the arguments
are pointers to variables which should receive the values from the
parameters.
</para>
<para>
<function>zend_parse_parameters</function> also performs type
conversions whenever possible, so that you always receive the data
in the format you asked for. Any type of scalar can be converted
to another one, but conversions between complex types (arrays,
objects, and resources) and scalar types are not allowed.
</para>
<para>
If the parameters could be obtained successfully and there were no
errors during type conversion, the function will return
<literal>SUCCESS</literal>, otherwise it will return
<literal>FAILURE</literal>. The function will output informative
error messages, if the number of received parameters does not
match the requested number, or if type conversion could not be
performed.
</para>
<para>
Here are some sample error messages:
<screen>
Warning - ini_get_all() requires at most 1 parameter, 2 given
Warning - wddx_deserialize() expects parameter 1 to be string, array given
</screen>
Of course each error message is accompanied by the filename and
line number on which it occurs.
</para>
<para>
Here is the full list of type specifiers:
<itemizedlist>
<listitem>
<para><literal>l</literal> - long</para>
</listitem>
<listitem>
<para><literal>d</literal> - double</para>
</listitem>
<listitem>
<para><literal>s</literal> - string (with possible null bytes) and its length</para>
</listitem>
<listitem>
<para><literal>b</literal> - boolean</para>
</listitem>
<listitem>
<para><literal>r</literal> - resource, stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>a</literal> - array, stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>o</literal> - object (of any class), stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>O</literal> - object (of class specified by class entry), stored in <literal>zval*</literal></para>
</listitem>
<listitem>
<para><literal>z</literal> - the actual <literal>zval*</literal></para>
</listitem>
</itemizedlist>
The following characters also have a meaning in the specifier
string:
<itemizedlist>
<listitem>
<para>
<literal>|</literal> - indicates that the remaining
parameters are optional. The storage variables
corresponding to these parameters should be initialized to
default values by the extension, since they will not be
touched by the parsing function if the parameters are not
passed.
</para>
</listitem>
<listitem>
<para>
<literal>/</literal> - the parsing function will
call <function>SEPARATE_ZVAL_IF_NOT_REF</function> on
the parameter it follows, to provide a copy of the
parameter, unless it's a reference.
</para>
</listitem>
<listitem>
<para>
<literal>!</literal> - the parameter it follows can
be of specified type or <literal>NULL</literal> (only
applies to a, o, O, r, and z). If <literal>NULL</literal>
value is passed by the user, the storage pointer will be
set to <literal>NULL</literal>.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The best way to illustrate the usage of this function is through
examples:
<programlisting role="c">
<![CDATA[
/* Gets a long, a string and its length, and a zval. */
long l;
char *s;
int s_len;
zval *param;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
"lsz", &l, &s, &s_len, ¶m) == FAILURE) {
return;
}
/* Gets an object of class specified by my_ce, and an optional double. */
zval *obj;
double d = 0.5;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
"O|d", &obj, my_ce, &d) == FAILURE) {
return;
}
/* Gets an object or null, and an array.
If null is passed for object, obj will be set to NULL. */
zval *obj;
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!a", &obj, &arr) == FAILURE) {
return;
}
/* Gets a separated array. */
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &arr) == FAILURE) {
return;
}
/* Get only the first three parameters (useful for varargs functions). */
zval *z;
zend_bool b;
zval *r;
if (zend_parse_parameters(3, "zbr!", &z, &b, &r) == FAILURE) {
return;
}
]]>
</programlisting>
</para>
<para>
Note that in the last example we pass 3 for the number of received
parameters, instead of <function>ZEND_NUM_ARGS</function>. What
this lets us do is receive the least number of parameters if our
function expects a variable number of them. Of course, if you want
to operate on the rest of the parameters, you will have to use
<function>zend_get_parameters_array_ex</function> to obtain
them.
</para>
<para>
The parsing function has an extended version that allows for an
additional flags argument that controls its actions.
<programlisting role="c">
<![CDATA[
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);
]]>
</programlisting>
</para>
<para>
The only flag you can pass currently is <literal>ZEND_PARSE_PARAMS_QUIET</literal>,
which instructs the function to not output any error messages
during its operation. This is useful for functions that expect
several sets of completely different arguments, but you will have
to output your own error messages.
</para>
<para>
For example, here is how you would get either a set of three longs
or a string:
<programlisting role="c">
<![CDATA[
long l1, l2, l3;
char *s;
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS() TSRMLS_CC,
"lll", &l1, &l2, &l3) == SUCCESS) {
/* manipulate longs */
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS(), "s", &s, &s_len) == SUCCESS) {
/* manipulate string */
} else {
php_error(E_WARNING, "%s() takes either three long values or a string as argument",
get_active_function_name(TSRMLS_C));
return;
}]]>
</programlisting>
</para>
<para>
With all the abovementioned ways of receiving function parameters
you should have a good handle on this process. For even more
example, look through the source code for extensions that are
shipped with PHP - they illustrate every conceivable situation.
</para>
</sect3>
<sect3 xml:id="internals2.ze1.zendapi.arguments.deprecated-retrieval">
<title>Old way of retrieving arguments (deprecated)</title>
<note>
<title>
Deprecated parameter parsing API
</title>
<para>
This API is deprecated and superseded by the new ZEND
parameter parsing API.
</para>
</note>
<para>
After having checked the number of arguments, you need to get access
to the arguments themselves. This is done with the help of
<function>zend_get_parameters_ex</function>:
<programlisting role="c">
<![CDATA[
zval **parameter;
if(zend_get_parameters_ex(1, ¶meter) != SUCCESS)
WRONG_PARAM_COUNT;
]]>
</programlisting>
All arguments are stored in a <envar>zval</envar> container,
which needs to be pointed to <emphasis>twice</emphasis>. The snippet above
tries to retrieve one argument and make it available to us via the
<envar>parameter</envar> pointer.
</para>
<para>
<function>zend_get_parameters_ex</function> accepts at least two
arguments. The first argument is the number of arguments to
retrieve (which should match the number of arguments with which
the function has been called; this is why it's important to check
for correct call syntax). The second argument (and all following
arguments) are pointers to pointers to pointers to
<envar>zval</envar>s. (Confusing, isn't it?) All these pointers
are required because Zend works internally with
<envar>**zval</envar>; to adjust a local <envar>**zval</envar> in
our function,<function>zend_get_parameters_ex</function> requires
a pointer to it.
</para>
<para>
The return value of <function>zend_get_parameters_ex</function>
can either be <literal>SUCCESS</literal> or
<literal>FAILURE</literal>, indicating (unsurprisingly) success or
failure of the argument processing. A failure is most likely
related to an incorrect number of arguments being specified, in
which case you should exit with
<literal>WRONG_PARAM_COUNT</literal>.
</para>
<para>
To retrieve more than one argument, you can use a similar snippet:
<programlisting role="c">
<![CDATA[
zval **param1, **param2, **param3, **param4;
if(zend_get_parameters_ex(4, ¶m1, ¶m2, ¶m3, ¶m4) != SUCCESS)
WRONG_PARAM_COUNT;
]]>
</programlisting>
</para>
<para>
<function>zend_get_parameters_ex</function> only checks whether
you're trying to retrieve too many parameters. If the function is
called with five arguments, but you're only retrieving three of
them with <function>zend_get_parameters_ex</function>, you won't
get an error but will get the first three parameters instead.
Subsequent calls of <function>zend_get_parameters_ex</function>
won't retrieve the remaining arguments, but will get the same
arguments again.
</para>
</sect3>
<sect3 xml:id="internals2.ze1.zendapi.arguments.variable">
<title>Dealing with a Variable Number of Arguments/Optional Parameters</title>
<para>
If your function is meant to accept a variable number of
arguments, the snippets just described are sometimes suboptimal
solutions. You have to create a line calling
<function>zend_get_parameters_ex</function> for every possible
number of arguments, which is often unsatisfying.
</para>
<para>
For this case, you can use the
function <function>zend_get_parameters_array_ex</function>, which accepts the
number of parameters to retrieve and an array in which to store them:
<programlisting role="c">
<![CDATA[
zval **parameter_array[4];
/* get the number of arguments */
argument_count = ZEND_NUM_ARGS();
/* see if it satisfies our minimal request (2 arguments) */
/* and our maximal acceptance (4 arguments) */
if(argument_count < 2 || argument_count > 4)
WRONG_PARAM_COUNT;
/* argument count is correct, now retrieve arguments */
if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS)
WRONG_PARAM_COUNT;
]]>
</programlisting>
First, the number of arguments is checked to make sure that it's in the accepted range. After that,
<function>zend_get_parameters_array_ex</function> is used to
fill <envar>parameter_array</envar> with valid pointers to the argument
values.
</para>
<para>
A very clever implementation of this can be found in the code
handling PHP's <function>fsockopen</function> located in
<filename>ext/standard/fsock.c</filename>, as shown in
<xref linkend="internals2.ze1.zendapi.example.fsockopen"/>. Don't worry if you don't know all the functions used in this
source yet; we'll get to them shortly.
</para>
<example xml:id="internals2.ze1.zendapi.example.fsockopen">
<title>PHP's implementation of variable arguments in fsockopen().</title>
<programlisting role="c">
<![CDATA[
pval **args[5];
int *sock=emalloc(sizeof(int));
int *sockp;
int arg_count=ARG_COUNT(ht);
int socketd = -1;
unsigned char udp = 0;
struct timeval timeout = { 60, 0 };
unsigned short portno;
unsigned long conv;
char *key = NULL;
FLS_FETCH();
if (arg_count > 5 || arg_count < 2 || zend_get_parameters_array_ex(arg_count,args)==FAILURE) {
CLOSE_SOCK(1);
WRONG_PARAM_COUNT;
}
switch(arg_count) {
case 5:
convert_to_double_ex(args[4]);
conv = (unsigned long) (Z_DVAL_PP(args[4]) * 1000000.0);
timeout.tv_sec = conv / 1000000;
timeout.tv_usec = conv % 1000000;
/* fall-through */
case 4:
if (!PZVAL_IS_REF(*args[3])) {
php_error(E_WARNING,"error string argument to fsockopen not passed by reference");
}
pval_copy_constructor(*args[3]);
ZVAL_EMPTY_STRING(*args[3]);
/* fall-through */
case 3:
if (!PZVAL_IS_REF(*args[2])) {
php_error(E_WARNING,"error argument to fsockopen not passed by reference");
return;
}
ZVAL_LONG(*args[2], 0);
break;
}
convert_to_string_ex(args[0]);
convert_to_long_ex(args[1]);
portno = (unsigned short) Z_LVAL_P(args[1]);
key = emalloc(Z_STRLEN_P(args[0]) + 10);
]]>
</programlisting>
</example>
<para>
<function>fsockopen</function> accepts two, three, four, or five
parameters. After the obligatory variable declarations, the
function checks for the correct range of arguments. Then it uses a
fall-through mechanism in a <literal>switch()</literal> statement
to deal with all arguments. The <literal>switch()</literal>
statement starts with the maximum number of arguments being passed
(five). After that, it automatically processes the case of four
arguments being passed, then three, by omitting the otherwise
obligatory <literal>break</literal> keyword in all stages. After
having processed the last case, it exits the
<literal>switch()</literal> statement and does the minimal
argument processing needed if the function is invoked with only
two arguments.
</para>
<para>
This multiple-stage type of processing, similar to a stairway, allows
convenient processing of a variable number of arguments.
</para>
</sect3>
<sect3 xml:id="zend.arguments.access">
<title>Accessing Arguments</title>
<para>
To access arguments, it's necessary for each argument to have a
clearly defined type. Again, PHP's extremely dynamic nature
introduces some quirks. Because PHP never does any kind of type
checking, it's possible for a caller to pass any kind of data to
your functions, whether you want it or not. If you expect an
integer, for example, the caller might pass an array, and vice
versa - PHP simply won't notice.
</para>
<para>
To work around this, you have to use a set of API functions to
force a type conversion on every argument that's being passed (see
<xref linkend="internals2.ze1.zendapi.tab.arg-conv"/>).
</para>
<para>
<emphasis>Note:</emphasis> All conversion functions expect a
<envar>**zval</envar> as parameter.
</para>
<table xml:id="internals2.ze1.zendapi.tab.arg-conv">
<title>Argument Conversion Functions</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.02*"/>
<colspec colnum="2" colname="col2" colwidth="1.00*"/>
<tbody>
<row>
<entry colname="col1">Function</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_boolean_ex</function></entry>
<entry colname="col2">
Forces conversion to a Boolean type. Boolean values remain
untouched. Longs, doubles, and strings containing
<literal>0</literal> as well as NULL values will result in
Boolean <literal>0</literal> (FALSE). Arrays and objects are
converted based on the number of entries or properties,
respectively, that they have. Empty arrays and objects are
converted to FALSE; otherwise, to TRUE. All other values
result in a Boolean <literal>1</literal> (TRUE).
</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_long_ex</function></entry>
<entry colname="col2">
Forces conversion to a long, the default integer type. NULL
values, Booleans, resources, and of course longs remain
untouched. Doubles are truncated. Strings containing an
integer are converted to their corresponding numeric
representation, otherwise resulting in <literal>0</literal>.
Arrays and objects are converted to <literal>0</literal> if
empty, <literal>1</literal> otherwise.
</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_double_ex</function></entry>
<entry colname="col2">
Forces conversion to a double, the default floating-point
type. NULL values, Booleans, resources, longs, and of course
doubles remain untouched. Strings containing a number are
converted to their corresponding numeric representation,
otherwise resulting in <literal>0.0</literal>. Arrays and
objects are converted to <literal>0.0</literal> if empty,
<literal>1.0</literal> otherwise.
</entry>
</row>
<row>
<entry colname="col1"><function>convert_to_string_ex</function></entry>
<entry colname="col2">
Forces conversion to a string. Strings remain untouched. NULL
values are converted to an empty string. Booleans containing
TRUE are converted to <literal>"1"</literal>, otherwise
resulting in an empty string. Longs and doubles are converted
to their corresponding string representation. Arrays are
converted to the string <literal>"Array"</literal> and
objects to the string <literal>"Object"</literal>.
</entry>
</row>
<row>
<entry colname="col1"><literal>convert_to_array_ex(value)</literal></entry>
<entry colname="col2">
Forces conversion to an array. Arrays remain untouched.
Objects are converted to an array by assigning all their
properties to the array table. All property names are used as
keys, property contents as values. NULL values are converted
to an empty array. All other values are converted to an array
that contains the specific source value in the element with
the key <literal>0</literal>.
</entry>
</row>
<row>
<entry colname="col1"><literal>convert_to_object_ex(value)</literal></entry>
<entry colname="col2">
Forces conversion to an object. Objects remain untouched.
NULL values are converted to an empty object. Arrays are
converted to objects by introducing their keys as properties
into the objects and their values as corresponding property
contents in the object. All other types result in an object
with the property <literal>scalar</literal> , having the
corresponding source value as content.
</entry>
</row>
<row>
<entry colname="col1"><literal>convert_to_null_ex(value)</literal></entry>
<entry colname="col2">Forces the type to become a NULL value, meaning empty.</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>
You can find a demonstration of the behavior in
<filename>cross_conversion.php</filename> on the accompanying
CD-ROM.
</para>
</note>
<mediaobject xml:id="internals2.ze1.zendapi.fig.cross-convert">
<alt>Cross-conversion behavior of PHP.</alt>
<imageobject>
<imagedata fileref="en/internals2/ze1/zendapi/figures/zend.04-cross-converter.png"/>
</imageobject>
</mediaobject>
<para>
Using these functions on your arguments will ensure type safety
for all data that's passed to you. If the supplied type doesn't
match the required type, PHP forces dummy contents on the
resulting value (empty strings, arrays, or objects,
<literal>0</literal> for numeric values, <literal>FALSE</literal>
for Booleans) to ensure a defined state.
</para>
<para>
Following is a quote from the sample module discussed
previously, which makes use of the conversion functions:
<programlisting role="c">
<![CDATA[
zval **parameter;
if((ZEND_NUM_ARGS() != 1) || (zend_get_parameters_ex(1, ¶meter) != SUCCESS))
{
WRONG_PARAM_COUNT;
}
convert_to_long_ex(parameter);
RETURN_LONG(Z_LVAL_P(parameter));
]]>
</programlisting>
After retrieving the parameter pointer, the parameter value is
converted to a long (an integer), which also forms the return value of
this function. Understanding access to the contents of the value requires a
short discussion of the <envar>zval</envar> type, whose definition is shown in <xref linkend="internals2.ze1.zendapi.example.zval-typedef"/>.
</para>
<example xml:id="internals2.ze1.zendapi.example.zval-typedef">
<title>PHP/Zend <envar>zval</envar> type definition.</title>
<programlisting role="c">
<![CDATA[
typedef pval zval;
typedef struct _zval_struct zval;
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
struct {
zend_class_entry *ce;
HashTable *properties;
} obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
unsigned char type; /* active type */
unsigned char is_ref;
short refcount;
};
]]>
</programlisting>
</example>
<para>
Actually, <envar>pval</envar> (defined in <filename>php.h</filename>) is
only an alias of <envar>zval</envar> (defined in <filename>zend.h</filename>),
which in turn refers to <envar>_zval_struct</envar>. This is a most interesting
structure. <envar>_zval_struct</envar> is the "master" structure, containing
the value structure, type, and reference information. The substructure
<envar>zvalue_value</envar> is a union that contains the variable's contents.
Depending on the variable's type, you'll have to access different members of
this union. For a description of both structures, see
<xref linkend="internals2.ze1.zendapi.tab.struct-zval"/>,
<xref linkend="internals2.ze1.zendapi.tab.struct-zvalue-value"/> and
<xref linkend="internals2.ze1.zendapi.tab.ztype-constants"/>.
</para>
<table xml:id="internals2.ze1.zendapi.tab.struct-zval">
<title>Zend <envar>zval</envar> Structure</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.00*"/>
<colspec colnum="2" colname="col2" colwidth="1.66*"/>
<tbody>
<row>
<entry colname="col1">Entry</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><envar>value</envar></entry>
<entry colname="col2">
Union containing this variable's contents. See
<xref linkend="internals2.ze1.zendapi.tab.struct-zvalue-value"/> for a description.
</entry>
</row>
<row>
<entry colname="col1"><envar>type</envar></entry>
<entry colname="col2">
Contains this variable's type. For a list of available
types, see <xref linkend="internals2.ze1.zendapi.tab.ztype-constants"/>.
</entry>
</row>
<row>
<entry colname="col1"><envar>is_ref</envar></entry>
<entry colname="col2">
0 means that this variable is not a reference; 1 means that this variable is a reference to another variable.
</entry>
</row>
<row>
<entry colname="col1"><envar>refcount</envar></entry>
<entry colname="col2">
The number of references that exist for this variable. For
every new reference to the value stored in this variable,
this counter is increased by 1. For every lost reference,
this counter is decreased by 1. When the reference counter
reaches 0, no references exist to this value anymore, which
causes automatic freeing of the value.
</entry>
</row>
</tbody>
</tgroup>
</table>
<table xml:id="internals2.ze1.zendapi.tab.struct-zvalue-value">
<title>Zend <envar>zvalue_value</envar> Structure</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.00*"/>
<colspec colnum="2" colname="col2" colwidth="1.66*"/>
<tbody>
<row>
<entry colname="col1">Entry</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><envar>lval</envar></entry>
<entry colname="col2">Use this property if the variable is of the
type <literal>IS_LONG</literal>,
<literal>IS_BOOLEAN</literal>, or <literal>IS_RESOURCE</literal>.</entry>
</row>
<row>
<entry colname="col1"><envar>dval</envar></entry>
<entry colname="col2">Use this property if the variable is of the
type <literal>IS_DOUBLE</literal>.</entry>
</row>
<row>
<entry colname="col1"><envar>str</envar></entry>
<entry colname="col2">
This structure can be used to access variables of
the type <literal>IS_STRING</literal>. The member <envar>len</envar> contains the
string length; the member <envar>val</envar> points to the string itself. Zend
uses C strings; thus, the string length contains a trailing
<literal>0x00</literal>.</entry>
</row>
<row>
<entry colname="col1"><envar>ht</envar></entry>
<entry colname="col2">This entry points to the variable's hash table entry if the variable is an array.</entry>
</row>
<row>
<entry colname="col1"><envar>obj</envar></entry>
<entry colname="col2">Use this property if the variable is of the
type <literal>IS_OBJECT</literal>.</entry>
</row>
</tbody>
</tgroup>
</table>
<table xml:id="internals2.ze1.zendapi.tab.ztype-constants">
<title>Zend Variable Type Constants</title>
<tgroup cols="2">
<colspec colnum="1" colname="col1" colwidth="1.00*"/>
<colspec colnum="2" colname="col2" colwidth="1.65*"/>
<tbody>
<row>
<entry colname="col1">Constant</entry>
<entry colname="col2">Description</entry>
</row>
<row>
<entry colname="col1"><literal>IS_NULL</literal></entry>
<entry colname="col2">Denotes a NULL (empty) value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_LONG</literal></entry>
<entry colname="col2">A long (integer) value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_DOUBLE</literal></entry>
<entry colname="col2">A double (floating point) value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_STRING</literal></entry>
<entry colname="col2">A string.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_ARRAY</literal></entry>
<entry colname="col2">Denotes an array.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_OBJECT</literal></entry>
<entry colname="col2">An object.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_BOOL</literal></entry>
<entry colname="col2">A Boolean value.</entry>
</row>
<row>
<entry colname="col1"><literal>IS_RESOURCE</literal></entry>
<entry colname="col2">A resource (for a discussion of resources, see the
appropriate section below).</entry>
</row>
<row>
<entry colname="col1"><literal>IS_CONSTANT</literal></entry>
<entry colname="col2">A constant (defined) value.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
To access a long you access <envar>zval.value.lval</envar>, to
access a double you use <envar>zval.value.dval</envar>, and so on.
Because all values are stored in a union, trying to access data
with incorrect union members results in meaningless output.
</para>
<para>
Accessing arrays and objects is a bit more complicated and
is discussed later.
</para>
</sect3>
<sect3 xml:id="internals2.ze1.zendapi.arguments.by-reference">
<title>Dealing with Arguments Passed by Reference</title>
<para>
If your function accepts arguments passed by reference that you
intend to modify, you need to take some precautions.
</para>
<para>
What we didn't say yet is that under the circumstances presented so
far, you don't have write access to any <envar>zval</envar> containers
designating function parameters that have been passed to you. Of course, you
can change any <envar>zval</envar> containers that you created within
your function, but you mustn't change any <envar>zval</envar>s that refer to
Zend-internal data!
</para>
<para>
We've only discussed the so-called <function>*_ex</function> API
so far. You may have noticed that the API functions we've used are
called <function>zend_get_parameters_ex</function> instead of
<function>zend_get_parameters</function>,
<function>convert_to_long_ex</function> instead of
<function>convert_to_long</function>, etc. The
<function>*_ex</function> functions form the so-called new
"extended" Zend API. They give a minor speed increase over the old
API, but as a tradeoff are only meant for providing read-only
access.
</para>
<para>
Because Zend works internally with references, different variables
may reference the same value. Write access to a
<envar>zval</envar> container requires this container to contain
an isolated value, meaning a value that's not referenced by any
other containers. If a <envar>zval</envar> container were
referenced by other containers and you changed the referenced
<envar>zval</envar>, you would automatically change the contents
of the other containers referencing this <envar>zval</envar>
(because they'd simply point to the changed value and thus change
their own value as well).
</para>
<para>
<function>zend_get_parameters_ex</function> doesn't care about
this situation, but simply returns a pointer to the desired
<envar>zval</envar> containers, whether they consist of references
or not. Its corresponding function in the traditional API,
<function>zend_get_parameters</function>, immediately checks for
referenced values. If it finds a reference, it creates a new,
isolated <envar>zval</envar> container; copies the referenced data
into this newly allocated space; and then returns a pointer to the
new, isolated value.
</para>
<para>
This action is called <emphasis>zval separation</emphasis>
(or pval separation). Because the <function>*_ex</function> API
doesn't perform zval separation, it's considerably faster, while
at the same time disabling write access.
</para>
<para>
To change parameters, however, write access is required. Zend deals
with this situation in a special way: Whenever a parameter to a function is
passed by reference, it performs automatic zval separation. This means that
whenever you're calling a function like
this in PHP, Zend will automatically ensure
that <envar>$parameter</envar> is being passed as an isolated value, rendering it
to a write-safe state:
<programlisting role="c">
<![CDATA[
my_function(&$parameter);
]]>
</programlisting>
</para>
<para>
But this <emphasis>is not</emphasis> the case with regular parameters!
All other parameters that are not passed by reference are in a read-only
state.
</para>
<para>
This requires you to make sure that you're really working with a
reference - otherwise you might produce unwanted results. To check for a
parameter being passed by reference, you can use the macro
<literal>PZVAL_IS_REF</literal>. This macro accepts a <literal>zval*</literal>
to check if it is a reference or not. Examples are given in
in <xref linkend="internals2.ze1.zendapi.example.pass-by-ref"/>.
</para>
<example xml:id="internals2.ze1.zendapi.example.pass-by-ref">
<title>Testing for referenced parameter passing.</title>
<programlisting role="c">
<![CDATA[
zval *parameter;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", ¶meter) == FAILURE)
return;
/* check for parameter being passed by reference */
if (!PZVAL_IS_REF(parameter)) {
{
zend_error(E_WARNING, "Parameter wasn't passed by reference");
RETURN_NULL();
}
/* make changes to the parameter */
ZVAL_LONG(parameter, 10);
]]>
</programlisting>
<mediaobject>
<alt>Testing for referenced parameter passing</alt>
<imageobject>
<imagedata fileref="en/internals2/ze1/zendapi/figures/zend.05-reference-test.png"/>
</imageobject>
</mediaobject>
</example>
</sect3>
<sect3 xml:id="internals2.ze1.zendapi.arguments.write-safety">
<title>Assuring Write Safety for Other Parameters</title>
<para>
You might run into a situation in which you need write access to a
parameter that's retrieved with <function>zend_get_parameters_ex</function>
but not passed by reference. For this case, you can use the macro
<literal>SEPARATE_ZVAL</literal>, which does a zval separation on the provided
container. The newly generated <envar>zval</envar> is detached from internal
data and has only a local scope, meaning that it can be changed or destroyed
without implying global changes in the script context:
<programlisting role="c">
<![CDATA[
zval **parameter;
/* retrieve parameter */
zend_get_parameters_ex(1, ¶meter);
/* at this stage, <parameter> still is connected */
/* to Zend's internal data buffers */
/* make <parameter> write-safe */
SEPARATE_ZVAL(parameter);
/* now we can safely modify <parameter> */
/* without implying global changes */
]]>
</programlisting>
<literal>SEPARATE_ZVAL</literal> uses <function>emalloc</function>
to allocate the new <envar>zval</envar> container, which means that even if you
don't deallocate this memory yourself, it will be destroyed automatically upon
script termination. However, doing a lot of calls to this macro
without freeing the resulting containers will clutter up your RAM.
</para>
<para>
<emphasis>Note:</emphasis> As you can easily work around the lack
of write access in the "traditional" API (with
<function>zend_get_parameters</function> and so on), this API
seems to be obsolete, and is not discussed further in this
chapter.
</para>
</sect3>
</sect2>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->
|