summaryrefslogtreecommitdiff
path: root/documentation/classes-and-objects.html
blob: 63c96650481e2ddeffc7b0bc540f9857f19beec8 (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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
<h1>Classes and objects</h1>
<p>
    Serious business now. C++ and PHP are both object oriented programming 
    languages, in which you can create classes and objects. The PHP-CPP library
    gives you the tools to combine these two and make a native C++ class 
    accessible from PHP.
</p>
<p>
    Sadly (but also logically if you think about it) not every thinkable C++ class can be directly 
    exported to PHP. It takes a little more work (although not so much). For a
    start, you must make sure that your class is derived from Php::Base, and 
    secondly, when you add your class to the extension object, you must also  
    specify all methods that you want to make accessible from PHP.
</p>
<p>
<pre class="language-c++"><code>
#include &lt;phpcpp.h&gt;

// actual class implementation
class Counter : public Php::Base
{
private:
    int _value = 0;

public:
    Counter() {}
    virtual ~Counter() {}
    
    Php::Value increment() { return ++_value; }
    Php::Value decrement() { return --_value; }
    Php::Value value() const { return _value; }
};

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class&lt;Counter&gt; counter("Counter");
        counter.method("increment", &Counter::increment);
        counter.method("decrement", &Counter::decrement);
        counter.method("value", &Counter::value);
        
        // add the class to the extension
        myExtension.add(std::move(counter));
        
        // return the extension
        return myExtension;
    }
}
</code></pre>
</p>
<p>
    Let's talk about programming conventions first - I always use capitals for 
    the first letter of a classname, and my member variables always start with
    an underscore. Every class always has a destructor, and it always is virtual.
    That's just a convention - my convention - and you do not 
    have to follow that.
</p>
<p>
    On topic. The example shows a very simple Counter class with three methods:
    increment(), decrement() and value(). The two update methods return the value 
    of the counter after the operation, the value() method returns the current value.
</p>
<p>
    If you want to make a class method that is accessible from PHP, you must
    ensure that is has one of the eight supported signatures (which are the same
    signatures that <a href="functions">exportable plain functions</a> can have),
    plus their const variant.
</p>
<p>
<pre class="language-c++"><code>
void YourClass::example1();
void YourClass::example2(Php::Parameters &amp;params);
Php::Value YourClass::example3();
Php::Value YourClass::example4(Php::Parameters &amp;params);
void YourClass::example5() const;
void YourClass::example6(Php::Parameters &amp;params) const;
Php::Value YourClass::example7() const;
Php::Value YourClass::example8(Php::Parameters &amp;params) const;
</code></pre>
</p>
<p>
    In the example we have used the third and seventh method forms, methods that do
    not accept any parameters, and that return a Php::Value object. Methods
    work exactly the same as <a href="functions">regular functions</a>, with the 
    difference that in the methods you have (of course) access to the member
    variables of the object (and in C++ you you do not have to use "this->"
    explicitly to access members).
</p>
<p>
    To make the class accessible from PHP, you must add it to the extension
    object inside the get_module() function. The Php::Class template class can be 
    be used for that. The template parameter should be your 
    implementation class, so that the Php::Class object internally knows which 
    class to instantiate the moment the "new" operator is used inside a PHP script.
</p>
<p>
    The Php::Class constructor receives a string parameter, with the name of 
    the class in PHP. After you've created an instance of the Php::Class object,
    you should specify all methods that you want to make accessible from PHP,
    and finally, when all methods have been registered, you should add the 
    class to your extension object so that it will be accessible from PHP.
    Notice that in our example we have used the C++11 std::move() function for this, so
    that the class object is actually <i>moved</i> into the extension object,
    which is a more efficient operation than copying.
</p>
<h2>Method parameters</h2>
<p>
    Methods are just like functions, and just how you use the
    Php::ByVal and the Php::ByRef classes to <a href="parameters">specify the 
    parameters of a function</a>, you can specify method parameters too.
</p>
<p>
<pre class="language-c++"><code>
#include &lt;phpcpp.h&gt;

// actual class implementation
class Counter : public Php::Base
{
private:
    int _value = 0;

public:
    Counter() {}
    virtual ~Counter() {}
    
    Php::Value increment(Php::Parameters &params) 
    { 
        return _value += params.size() > 0 ? (int)params[0] : 1; 
    }
    
    Php::Value decrement(Php::Parameters &params) 
    { 
        return _value -= params.size() > 0 ? (int)params[0] : 1; 
    }
    
    Php::Value value() const 
    { 
        return _value; 
    }
};

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class&lt;Counter&gt; counter("Counter");
        
        // register the increment method, and specify its parameters
        counter.method("increment", &Counter::increment, { 
            Php::ByVal("change", Php::Type::Numeric, false) 
        });
        
        // register the decrement, and specify its parameters
        counter.method("decrement", &Counter::decrement, { 
            Php::ByVal("change", Php::Type::Numeric, false) 
        });
        
        // register the value method
        counter.method("value", &Counter::value);
        
        // add the class to the extension
        myExtension.add(std::move(counter));
        
        // return the extension
        return myExtension;
    }
}
</code></pre>
</p>
<p>
    In the code above we have modified our first example. The increment and
    decrement method now get an optional 'change' parameter, which is a numeric
    value that holds the change that should be applied to the counter. Note that
    this parameter is optional - so inside the method implementation we 
    check if the number of parameters is bigger than zero to prevent nasty 
    segmentation faults.
</p>
<p>
<pre class="language-php"><code>
&lt;?php
$counter = new Counter();
$counter->increment(5);
$counter->increment();
$counter->decrement(3);
echo($counter->value()."\n");
?&gt;
</code></pre>
</p>
<p>
    The code above shows a PHP script that uses the native Counter class.
    The output of above script is (as you had of course expected) 3.
</p>
<p>
    In the example code we have not shown how to use the Php::ByRef class, but
    that works exaclty the same as in methods, so we thought that an example 
    was not really necessary.
</p>
<h2>Access modifiers</h2>
<p>
    In PHP (and in C++ too) you can mark methods as public, private or protected.
    To achieve this for your native class too, you should pass in an additional 
    flags parameter when you add the method to the Php::Class object. Image that
    you want to make the increment and decrement methods in our previous example
    protected, then you can simply add a flag:
</p>
<p>
<pre class="language-c++"><code>
extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class&lt;Counter&gt; counter("Counter");
        
        // register the increment method, and specify its parameters
        counter.method("increment", &Counter::increment, Php::Protected, { 
            Php::ByVal("change", Php::Type::Numeric, false) 
        });
        
        // register the decrement, and specify its parameters
        counter.method("decrement", &Counter::decrement, Php::Protected, { 
            Php::ByVal("change", Php::Type::Numeric, false) 
        });
        
        // register the value method
        counter.method("value", &Counter::value, Php::Public | Php::Final);
        
        // add the class to the extension
        myExtension.add(std::move(counter));
        
        // return the extension
        return myExtension;
    }
}
</code></pre>
</p>
<p>
    By default, every method (and every property too, but we deal with that later) 
    is public. You can pass in an additional Php::Protected or Php::Private flag
    if you want to mark a method as being either protected or private. The flag
    parameter can be bitwise-or'ed with Php::Abstract or Php::Final if you also 
    want to mark your method as being either abstract or final. We did this with
    the value() method, so that it becomes impossible to override this method in a
    derived class.
</p>
<p>
    Remember that the exported methods in your C++ class must always be public - even
    when you've marked them as being private or protected in PHP. This makes sense,
    because after all, your methods are called by the PHP-CPP library, and if you make
    them private, the becomes invisible for the library.
</p>
<h2>Abstract and final</h2>
<p>
    In the previous section we showed how to use the Php::Final and Php::Abstract
    flags to create a final or abstract method. If you want to make your entire
    class abstract or final, you can do so by using Php::FinalClass or 
    Php::AbstractClass instead of Php::Class.
</p>
<p>
<pre class="language-c++"><code>
extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::FinalClass&lt;Counter&gt; counter("Counter");
        
        // register methods
        ...
        
        // return the extension
        return myExtension;
    }
}
</code></pre>
</p>
<p>
    It may seem strange that you have to pass in the address of a real C++ method
    when you mark a method as being abstract. Abstract methods do normally not 
    have an implementation, so what do you need this C++ implementation for?
    Luckily, there also is a different way for registering abstract methods.
</p>
<p>
<pre class="language-c++"><code>
extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class&lt;Counter&gt; counter("Counter");
        
        // register an abstract method
        counter.method("myAbstractMethod", { Php::ByVal("value", Php::Type::String, true) });
        
        // register other methods
        ...
        
        // return the extension
        return myExtension;
    }
}
</code></pre>
</p>
<p>
    To register abstract methods, you can simply use an alternative form of the 
    Counter::method() method that does not take a pointer to a C++ method.
</p>
<p>
    There is much more to say about classes and objects, in the next section
    we'll explain <a href="constructors-and-destructors">constructors and 
    destructors</a>.
</p>