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
|
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision: 330100 $ -->
<chapter xml:id="internals2.funcs" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Writing Functions</title>
<para>
Functions and Methods in PHP take much the same form, a method is simply a function with a specific scope; the scope of their class entry.
The <code>Hacker</code> will read about class entries in other sections of the <code>Hacker's</code> guide.
The purpose of this section is to introduce the <code>Hacker</code> to the anatomy of a function or method;
the <code>Hacker</code> will learn how to define functions, how to accept variables and how to return variables to the programmer of PHP.
</para>
<para>
The anatomy of a function could not be simpler:
</para>
<screen>
<![CDATA[
PHP_FUNCTION(hackers_function) {
/* your accepted arguments here */
long number;
/* accepting arguments */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &number) != SUCCESS) {
return;
}
/* do some work on the input */
number *= 2;
/* set return value */
RETURN_LONG(number);
}]]>
</screen>
<para>
The <code>PHP_FUNCTION(hackers_function)</code> preprocessor instruction will result in the following declaration:
</para>
<screen>
<![CDATA[
void zif_hackers_function(INTERNAL_FUNCTION_PARAMETERS)
]]>
</screen>
<para>
<code>INTERNAL_FUNCTION_PARAMETERS</code> is defined as a macro and explained in the following table:
</para>
<table xml:id="internals2.funcs.index.internal-func-params">
<title>INTERNAL_FUNCTION_PARAMETERS</title>
<tgroup cols="3">
<thead>
<row>
<entry>Name and type</entry>
<entry>Description</entry>
<entry>Access macros</entry>
</row>
</thead>
<tbody>
<row>
<entry><code>int ht</code></entry>
<entry>Number of actual parameters passed by user</entry>
<entry><code>ZEND_NUM_ARGS()</code></entry>
</row>
<row>
<entry><code>zval *return_value</code></entry>
<entry>
Pointer to a PHP variable that can be filled with the return value
passed to the user. The default type is <code>IS_NULL</code>.
</entry>
<entry><code>RETVAL_*</code>, <code>RETURN_*</code></entry>
</row>
<row>
<entry><code>zval **return_value_ptr</code></entry>
<entry>When returning references to PHP set this to a pointer to
your variable. It is not suggested to return references.</entry>
<entry/>
</row>
<row>
<entry><code>zval *this_ptr</code></entry>
<entry>
If this is a method call, points to the PHP variable
holding the <code>$this</code> object.
</entry>
<entry><code>getThis()</code></entry>
</row>
<row>
<entry><code>int return_value_used</code></entry>
<entry>
Flag indicating whether the returned value will be used by the
caller.
</entry>
<entry/>
</row>
</tbody>
</tgroup>
</table>
<para>
For clarity, the fully expanded declaration for <code>PHP_FUNCTION(hackers_function)</code> looks like:
</para>
<screen>
<![CDATA[
void zif_hackers_function(int ht, zval* return_value, zval** return_value_ptr, zval* this_ptr, int return_value_used)
]]>
</screen>
<para>
The precense of <code>this_ptr</code> may be confusing, classes are covered in detail in later sections,
suffice to say that <code>PHP_METHOD(MyClass, hackersFunction)</code> would result in the following declaration:
</para>
<screen>
<![CDATA[
void zim_MyClass_hackersFunction(INTERNAL_FUNCTION_PARAMETERS)
]]>
</screen>
<para>
<code>hackers_function</code> doesn't do anything useful, it accepts a number using the <code>zend_parse_parameters</code> API, doubles it, and returns it to the engine.
It is obvious that a normal function would have to do something more complex than double the input, for the purposes of education we are keeping it simple.
On entry to the function <code>return_value</code> is allocated and initialized to <code>null</code>, making <code>null</code> the default return value of any function in PHP.
</para>
<para>
If <code>zend_parse_parameters</code> does not recieve what is specified by the <code>Hacker</code> as the correct arguments, and the arguments recieved cannot be converted
to conform with <code>type_spec</code> an error will be raised, and by convention, the <code>Hacker</code> should <code>return</code> immediately.
</para>
<note>
<para><code>Arrays</code>, <code>Objects</code>, and <code>Resources</code> cannot be converted.</para>
</note>
<table xml:id="internals2.funcs.parameters.api">
<title>Parsing Parameters Prototypes</title>
<tgroup cols="1">
<tbody>
<row>
<entry><code>int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...)</code></entry>
</row>
<row>
<entry><code>int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...)</code></entry>
</row>
<row>
<entry><code>int zend_parse_parameter(int flags, int arg_num TSRMLS_DC, zval **arg, const char *spec, ...)</code></entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>
<code>zend_parse_parameter</code> is available from version 5.5, it behaves like <code>zend_parse_parameters_ex</code>
except that instead of reading the arguments from the stack, it receives a single zval to convert, which may be changed in place.
</para>
</note>
<note>
<para>
<code>flags</code> is intended to be a mask, currently only <code>ZEND_PARSE_PARAMS_QUIET</code> will have any impact (supress warnings)
</para>
</note>
<para>
The variable arguments recieved by these API functions are expected to be the address of C variables, and should be considered the output of the <code>zend_parse_parameters</code> API functions.
</para>
<table xml:id="internals2.funcs.parameters.types">
<title>Type Specifiers</title>
<tgroup cols="3">
<thead>
<row>
<entry>Spec</entry>
<entry>Type</entry>
<entry>Locals</entry>
</row>
</thead>
<tbody>
<row>
<entry>a</entry>
<entry><code>array</code></entry>
<entry><code>zval*</code></entry>
</row>
<row>
<entry>A</entry>
<entry><code>array</code> or <code>object</code></entry>
<entry><code>zval*</code></entry>
</row>
<row>
<entry>b</entry>
<entry><code>boolean</code></entry>
<entry><code>zend_bool</code></entry>
</row>
<row>
<entry>C</entry>
<entry><code>class</code></entry>
<entry><code>zend_class_entry*</code></entry>
</row>
<row>
<entry>d</entry>
<entry><code>double</code></entry>
<entry><code>double</code></entry>
</row>
<row>
<entry>f</entry>
<entry><code>function</code></entry>
<entry><code>zend_fcall_info*</code>, <code>zend_fcall_info_cache*</code></entry>
</row>
<row>
<entry>h</entry>
<entry><code>array</code></entry>
<entry><code>HashTable*</code></entry>
</row>
<row>
<entry>H</entry>
<entry><code>array</code> or <code>object</code></entry>
<entry><code>HashTable*</code></entry>
</row>
<row>
<entry>l</entry>
<entry><code>long</code></entry>
<entry><code>long</code></entry>
</row>
<row>
<entry>L</entry>
<entry><code>long</code> (limits out-of-range LONG_MAX/LONG_MIN)</entry>
<entry><code>long</code></entry>
</row>
<row>
<entry>o</entry>
<entry><code>object</code></entry>
<entry><code>zval*</code></entry>
</row>
<row>
<entry>O</entry>
<entry><code>object</code> (of specified <code>zend_class_entry</code>)</entry>
<entry><code>zval*</code>, <code>zend_class_entry*</code></entry>
</row>
<row>
<entry>p</entry>
<entry><code>string</code> (a valid path)</entry>
<entry><code>char*</code>, <code>int</code></entry>
</row>
<row>
<entry>r</entry>
<entry><code>resource</code></entry>
<entry><code>char*</code></entry>
</row>
<row>
<entry>s</entry>
<entry><code>string</code></entry>
<entry><code>char*</code>, <code>uint</code></entry>
</row>
<row>
<entry>z</entry>
<entry><code>mixed</code></entry>
<entry><code>zval*</code></entry>
</row>
<row>
<entry>Z</entry>
<entry><code>mixed</code></entry>
<entry><code>zval**</code></entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>
Where the type specifier is <code>O</code>, the local <code>zend_class_entry*</code> is to be
considered input (part of the type spec) to <code>zend_parse_parameter</code>
</para>
</note>
<table xml:id="internals2.funcs.parameters.advanced">
<title>Advanced Type Specifiers</title>
<tgroup cols="3">
<thead>
<row>
<entry>Spec</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>*</entry>
<entry>a variable number of argument of the preceeding type, 0 or more</entry>
</row>
<row>
<entry>+</entry>
<entry>a variable number of argument of the preceeding type, 1 or more</entry>
</row>
<row>
<entry>|</entry>
<entry>indicates that the remaining parameters are optional</entry>
</row>
<row>
<entry>/</entry>
<entry><code>SEPARATE_ZVAL_IF_NOT_REF</code> on the parameter it follows</entry>
</row>
<row>
<entry>!</entry>
<entry>
the preceeding parameter can be of the specified type or <code>null</code>
For 'b', 'l' and 'd', an extra argument of type zend_bool* must be passed after
the corresponding <code>bool*</code>, <code>long*</code> or <code>double*</code>
addresses which will be set <code>true</code> if <code>null</code> is recieved.
</entry>
</row>
</tbody>
</tgroup>
</table>
<note>
<para>Consult <code>README.PARAMETER_PARSING_API</code> included in source distributions for more information on parsing parameters</para>
</note>
<para>
Once the <code>Hacker's</code> function has executed whatever it was implemented to execute, it is time to set the <code>return_value</code> for the engine.
The <code>RETURN_</code> and <code>RETVAL_</code> macros are just wrappers around <code>Z_*_P</code> macros that work with <code>return_value</code>.
</para>
<note>
<para><code>RETURN_</code> macros cause execution to leave the function immediately (ie: <code>return;</code>), while <code>RETVAL_</code> macros allow execution to continue after <code>return_value</code> has been set.</para>
</note>
<para>
The <code>Hacker</code> should now have a reasonable understanding of the anatomy of a function, and to some degree, the anatomy of a method.
</para>
</chapter>
<!-- 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
-->
|