summaryrefslogtreecommitdiff
path: root/documentation/classes-and-objects.html
blob: 6bb804d67a87a13b91328ec871db9f57e3939fd7 (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
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
<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 native C++ classes 
    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;

/**
 *  Counter class that can be used for counting
 */
class Counter : public Php::Base
{
private:
    /**
     *  The initial value
     *  @var    int
     */
    int _value = 0;

public:
    /**
     *  C++ constructor and destructor
     */
    Counter() {}
    virtual ~Counter() {}
    
    /**
     *  Update methods to increment or decrement the counter
     *  Both methods return the NEW value of the counter
     *  @return int
     */
    Php::Value increment() { return ++_value; }
    Php::Value decrement() { return --_value; }
    
    /**
     *  Method to retrieve the current counter value
     *  @return int
     */
    Php::Value value() const { return _value; }
};

/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object
        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);
        counter.method("instantiate", &Counter::instantiate);
        
        // 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 - we always use capitals for 
    the first letter of a classname, and member variables always start with
    an underscore. Every class always has a destructor, and it always is virtual.
    That's just a convention - our 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 accessible from PHP, you must
    ensure that it matches one of the supported signatures. These are essentially
    the same signatures as <a href="functions">exportable plain functions</a>
    can have, but with versions for const and non-const methods.
</p>
<p>
<pre class="language-c++"><code>
// signatures of supported regular methods
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>
    Methods work exactly the same as <a href="functions">regular functions</a>, with the 
    difference that in a method you have (of course) access to the member
    variables of the object.
</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 should 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 needs a string parameter, with the name of 
    the class in PHP. The method Php::Class::method() can then be used, as you can
    see in the example above, to register methods that you want to make accessible 
    from PHP. Did you see that in the example we used the C++11 std::move() function 
    to add the class to the extension? This will actually <i>move</i> the class
    object into the extension, 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;

/**
 *  Counter class that can be used for counting
 */
class Counter : public Php::Base
{
private:
    /**
     *  The internal value
     *  @var    int
     */
    int _value = 0;

public:
    /**
     *  C++ constructor and destructor
     */
    Counter() {}
    virtual ~Counter() {}
    
    /**
     *  Increment operation
     *  This method gets one optional parameter holding the change
     *  @param  int     Optional increment value
     *  @return int     New value
     */
    Php::Value increment(Php::Parameters &amp;params) 
    { 
        return _value += params.size() > 0 ? (int)params[0] : 1; 
    }

    /**
     *  Decrement operation
     *  This method gets one optional parameter holding the change
     *  @param  int     Optional decrement value
     *  @return int     New value
     */
    Php::Value decrement(Php::Parameters &amp;params) 
    { 
        return _value -= params.size() > 0 ? (int)params[0] : 1; 
    }
    
    /**
     *  Method to retrieve the current value
     *  @return int
     */
    Php::Value value() const 
    { 
        return _value; 
    }
};

/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object
        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", &amp;Counter::increment, { 
            Php::ByVal("change", Php::Type::Numeric, false) 
        });
        
        // register the decrement, and specify its parameters
        counter.method("decrement", &amp;Counter::decrement, { 
            Php::ByVal("change", Php::Type::Numeric, false) 
        });
        
        // register the value method
        counter.method("value", &amp;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 <a href="functions">as for functions</a>, so we 
    thought that an example was not really necessary (and we're not a big fan
    of parameters-by-reference either).
</p>
<h2>Static methods</h2>
<p>
    Static methods are supported too. A static method is a method that does
    not have access to a 'this' pointer. In C++, such static methods
    are therefore identical to regular functions, that also do not have access 
    to a 'this' pointer. The only difference between static C++ methods and 
    regular C++ functions is at compile time: the compiler allows static 
    methods to access private data. The signature of a static method is however
    completely identical to the signature of a regular function.
</p>
<p>
    PHP-CPP allows you to register static methods. But because of the fact that
    the signature of a static method is identical to the signature of a regular 
    function, the method that you register does not even have to be a method of 
    the same class. Regular functions and static methods of other classes
    have exactly the same signature and can be registered too! From a software 
    architectural standpoint, it is better to use only static methods of the 
    same class, but C++ allows you to do much more.
</p>
<pre class="language-c++"><code>
#include &lt;phpcpp.h&gt;

/**
 *  Regular function
 *
 *  Because a regular function does not have a 'this' pointer,
 *  it has the same signature as static methods
 *
 *  @param  params      Parameters passed to the function
 */
void regularFunction(Php::Parameters &amp;params)
{
    // @todo add implementation
}

/**
 *  A very simple class that will <b>not</b> be exported to PHP
 */
class PrivateClass
{
public:
    /**
     *  C++ constructor and destructor
     */
    PrivateClass() {}
    virtual ~PrivateClass() {}

    /** 
     *  Static method
     *
     *  A static method also has no 'this' pointer and has
     *  therefore a signature identical to regular functions
     *
     *  @param  params      Parameters passed to the method
     */
    static void staticMethod(Php::Parameters &amp;params)
    {
        // @todo add implementation
    }
};

/**
 *  A very simple class that will be exported to PHP
 */
class PublicClass : public Php::Base
{
public:
    /**
     *  C++ constructor and destructor
     */
    PublicClass() {}
    virtual ~PublicClass() {}

    /** 
     *  Another static method
     *
     *  This static has exactly the same signature as the
     *  regular function and static method that were mentioned
     *  before
     *
     *  @param  params      Parameters passed to the method
     */
    static void staticMethod(Php::Parameters &amp;params)
    {
        // @todo add implementation
    }
};

/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object
        static Php::Extension myExtension("my_extension", "1.0");
        
        // description of the class so that PHP knows which methods are accessible
        Php::Class&lt;PublicClass&gt; myClass("MyClass");
        
        // register the PublicClass::staticMethod to be a
        // static method callable from PHP
        myClass.method("static1", &amp;PublicClass::staticMethod);
        
        // regular functions have the same signatures as 
        // static methods. So nothing forbids you to register
        // a normal function as static method too
        myClass.method("static2", regularFunction);
        
        // and even static methods from completely different
        // classes have the same function signature and can
        // thus be registered
        myClass.method("static3", &amp;PrivateClass::staticMethod);
        
        // add the class to the extension
        myExtension.add(std::move(myClass));
        
        // In fact, because a static method has the same signature
        // as a regular function, you can also register static
        // C++ methods as regular global PHP functions
        myExtension.add("myFunction", &amp;PrivateClass::staticMethod);
        
        // return the extension
        return myExtension;
    }
}
</code></pre>
</p>
<p>
    It is questionable how useful this all is. It is probably advisable to keep 
    your code clean, simple and maintainable, and only register static PHP methods 
    that are also in C++ static methods of the same class. But C++ does not forbid
    you to do it completely different. Let's round up with an example how to 
    call the static methods
</p>
<p>
<pre class="language-c++"><code>
&lt;?php
// this will call PublicClass::staticMethod()
MyClass::static1();

// this will call PrivateClass::staticMethod()
MyClass::static2();

// this will call regularFunction
MyClass::static3();

// this will call PrivateClass::staticMethod
myFunction();
?&gt;
</code></pre>
</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. Imagine that
    you want to make the increment and decrement methods in the previous example
    protected, then you can simply add a flag:
</p>
<p>
<pre class="language-c++"><code>
/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object
        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 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 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 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, they 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>
/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object
        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 register an abstract method. 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>
/**
 *  Switch to C context to ensure that the get_module() function
 *  is callable by C programs (which the Zend engine is)
 */
extern "C" {
    /**
     *  Startup function that is called by the Zend engine 
     *  to retrieve all information about the extension
     *  @return void*
     */
    PHPCPP_EXPORT void *get_module() {
        // create static instance of the extension object
        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>