summaryrefslogtreecommitdiff
path: root/documentation/parameters.html
blob: 0051f514fe05ac4ff09c4a2101723a0cdef6cb82 (plain)
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
<h1>Specifying function parameters</h1>
<p>
    PHP has a mechanism to enforce the types of function parameters, and to accept
    parameters either by reference or by value. In the <a href="functions">
    earlier examples</a>, we had not yet used that mechanism, and we left it to 
    the function implementations to inspect the 'Php::Parameters' object (which
    is a std::vector of Php::Value objects), and to check whether the number of 
    parameters is correct, and of the right type.
</p>
<p>
    However, the 'Extension::add()' method takes a third optional parameter that
    you can use to specify the number of parameters that are supported, if
    the parameters are passed by reference or by value, and what the type of
    the parameters is:
</p>
<p>
<pre class="language-c++"><code>#include &lt;phpcpp.h&gt;

void example(Php::Parameters &amp;params)
{
}

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        myExtension.add("example", example, {
            Php::ByVal("a", Php::Type::Numeric),
            Php::ByVal("b", "ExampleClass"),
            Php::ByVal("c", "OtherClass")
        });
        return myExtension;
    }
}</pre></code>
</p>
<p>
    Above you see that we pass in additional information when we register the
    "example" function. We tell the PHP engine that our function accepts three parameters:
    the first parameter must be numeric, while the other ones are 
    instances of type "ExampleClass" and "OtherClass". In the end, your native C++ 
    "example" function will still be called with a Php::Parameters instance, but 
    the moment it gets called, you can be sure that the Php::Parameters object 
    will be filled with three members, and that two of them are objects of the 
    appropriate type.
</p>
<h2 id="enforce-scalar-parameters">Can you really enforce scalar parameters?</h2>
<p>
    You may be surprised to see that we specified the first parameter to be of
    type Numeric. After all, in PHP there is no offical way to enforce the type of a 
    scalar parameter. When you write a function in PHP, it is possible to enforce 
    that the function receives an object or an array, but not that you want to receive 
    a string or an integer.
</p>
<p>
<pre class="language-php"><code>
&lt;?php
// example how you can enforce that a function can only be called with an object
function example1(MyClass $param)
{
    ...
}

// another example to enforce an array parameter
function example2(array $param)
{
    ...
}

// this is not (yet?) possible in PHP
function example3(int $param)
{
    ...
}

?&gt;
</code></pre>
</p>
<p>
    The same is true for native functions. Although the core PHP engine and PHP-CPP
    library both offer the possibility to specify that your function accepts parameters of type 
    "Php::Type::Numeric" or of type "Php::Type::String", this setting is further 
    completely ignored. Maybe this is going to change in the 
    future (let's hope so), but for now it is only meaningful to specify the
    parameter type for objects and arrays. We have however chosen to still offer 
    this feature in PHP-CPP, because it is also offered by the core PHP engine, 
    so that we are ready for future versions of PHP. But for now
    the specification of the numeric parameter in our example is meaningless.
</p>
<p>
    To come back to our example, the following functions calls can now be done
    from PHP user space:
</p>
<p>
<pre class="language-php"><code>
&lt;?php
// correct call, parameters are numeric and two objects of the right type
example(12, new ExampleClass(), new OtherClass());

// also valid, first parameter is not numeric but an array, but the 
// Zend engine does not check this even though it was specified
example(array(1,2,3), new ExampleClass(), new OtherClass());

// invalid, wrong number of parameters
example(12, new ExampleClass());

// invalid, wrong objects
example(12, new DateTime(), new DateTime());

// invalid, "x" and "z" are no objects
example("x", "y", "z");
?&gt;
</code></pre>
</p>
<p>
    The PHP engine will trigger an error if your function is called with wrong
    parameters, and will not make the actual call to the native function.
</p>
<h2 id="byval-explained">The Php::ByVal class further explained</h2>
<p>
    The Php::ByVal class that we showed can be constructed in two ways.
    Let's look at the first constructor from the C++ header file:
</p>
<p>
<pre class="language-c++"><code>
/**
 *  Constructor
 *  @param  name        Name of the parameter
 *  @param  type        Parameter type
 *  @param  required    Is this parameter required?
 */
ByVal(const char *name, Php::Type type, bool required = true);
</code></pre>
</p>
<p>
    The first parameter of the constructor should always be the  
    parameter name. This is a little strange, because PHP does not support the
    concept of 'named variables', like other languages do. Internally, this 
    name is only used to generate an error messages when your function is 
    called in the wrong way.
</p>
<p>
    The Php::Type parameter is more interesting. The following types are
    supported:
</p>
<p>
<pre>
Php::Type::Null
Php::Type::Numeric
Php::Type::Float
Php::Type::Bool
Php::Type::Array
Php::Type::Object
Php::Type::String
Php::Type::Resource
Php::Type::Constant
Php::Type::ConstantArray
Php::Type::Callable
</pre>
</p>
<p>
    In practice, it only makes a difference if you specify Php::Type::Array or
    Php::Type::Object as type, because all other types are not enforced by the 
    underlying PHP engine.
</p>
<p>
    The final parameter (do you remember that it was called 'required'?) can be 
    used to set whether the parameter is optional or not.  If you set it
    to true, PHP will trigger an error when your
    function is called without this parameter. This setting only
    works for the trailing parameters, as it is (of course) not 
    possible to mark the first parameter as optional, and all subsequent 
    parameters as required.
<p>
<p>
    If you write a function that accepts an object as parameter, you can
    use the second form of the Php::ByVal constructor:
</p>
<p>
<pre class="language-c++"><code>
/**
 *  Constructor
 *  @param  name        Name of the parameter
 *  @param  classname   Name of the class
 *  @param  nullable    Can it be null?
 *  @param  required    Is this parameter required?
 */
ByVal(const char *name, const char *classname, bool nullable = false, bool required = true);
</code></pre>
</p>
<p>
    This alternative constructor also has a 'name' and 'required' parameter, but the
    Php::Type parameter that was available before is now replaced by a 'classname'
    and 'nullable' parameter. This constructor can be used for functions that 
    accept an object of a specific type as parameter. For example, take a look 
    at the following code in PHP:
</p>
<p>
<pre class="language-php"><code>
&lt;?php
function example1(DateTime $time) { ... }
function example1(DateTime $time = null) { Php::Value time = params[0]; ... }
?&gt;
</code></pre>
</p>
<p>
    This would be identical to the following code in C++:
</p>
<p>
<pre class="language-c++"><code>
#include &lt;phpcpp.h&gt;

void example1(Php::Parameters &amp;params) { Php::Value time = params[0]; ... }
void example2(Php::Parameters &amp;params) { Php::Value time = params[0]; ... }

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        myExtension.add("example1", example1, { Php::ByVal("time", "DateTime", false); });
        myExtension.add("example2", example2, { Php::ByVal("time", "DateTime", true); });
        return myExtension;
    }
}
</pre></code>
</p>
<h2 id="parameters-by-reference">Parameters by reference</h2>
<p>
    By the name of the Php::ByVal class you may have concluded that there
    must also be a Php::ByRef class - and you could not have been more right than that. 
    There is indeed a Php::ByRef class.
    If you create a function that takes a parameter by reference (and that can
    thus 'return' a value via a parameter) you can specify that too.
</p>
<p>
    The Php::ByRef class has exactly the same signature as the Php::ByVal class,
    and can be used in exactly the same way. The difference is that ByRef
    also checks if someone tries to call your function with a literal instead
    of a variable - which is not possible for variables by reference. If this
    happens, an error is triggered and the function will not be called.
    Let's show an example. The following extension creates a function that
    swaps the contents of two variables.
</p>
<p>
<pre class="language-c++"><code>
#include &lt;phpcpp.h&gt;

void swap(Php::Parameters &amp;params) 
{
    Php::Value temp = params[0];
    params[0] = params[1];
    params[1] = temp;
}
    
extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        myExtension.add("swap", swap, { 
            Php::ByRef("a", Php::Type::Numeric),
            Php::ByRef("b", Php::Type::Numeric) 
        });
        return myExtension;
    }
}
</code></pre>
<p>
    And let's see how this function can now be called:
</p>
<p>
<pre class="language-php"><code>
&lt;?php
// define two variables
$a = 1;
$b = 2;

// swap the variables
swap($a, $b);

// invalid, literals cannot be passed by reference
swap(10,20);
?&gt;
</code></pre>
</p>
<h2 id="summary">Summary</h2>
<p>
    When you add your native functions to the extension object, you may supply
    an optional third parameter with a list of Php::ByVal and Php::ByRef objects
    with the names and types of the parameters that are accepted.
    Internally, the PHP engine runs a check right before every 
    function call to verify that the passed in parameters are compatible with 
    the parameter specification that you gave, and will trigger an error if they 
    are not.
</p>
<p>
    Specifying parameters is optional. If you choose to leave this specification
    out it is up to you to check <i>inside</i> the function if there are enough
    parameters, and if the types are correct.
</p>