summaryrefslogtreecommitdiff
path: root/documentation/magic-interfaces.html
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-10 13:37:09 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-10 13:37:09 +0100
commitdd3422f6ed454d06e6091ad5023da7fc294595b8 (patch)
treed37cc9cc541f4e6cc2fb4c1c04c704620b7151c7 /documentation/magic-interfaces.html
parent4872cc627642044f46ba8a8726902592a1fae05f (diff)
splitted magic-methods-and-interfaces documentation into two seperate documentation files, added documentation about __set(), __get(), __isset() and __unset()
Diffstat (limited to 'documentation/magic-interfaces.html')
-rw-r--r--documentation/magic-interfaces.html579
1 files changed, 579 insertions, 0 deletions
diff --git a/documentation/magic-interfaces.html b/documentation/magic-interfaces.html
new file mode 100644
index 0000000..1664e3c
--- /dev/null
+++ b/documentation/magic-interfaces.html
@@ -0,0 +1,579 @@
+<h1>Magic interfaces</h1>
+<p>
+ A core PHP installation comes with a number of special "magic" PHP interfaces
+ that script writers can implement to add special features to a class. These
+ are interfaces with names like 'Countable', 'ArrayAccess' and 'Serializable'.
+ The features that these interfaces bring, can also be implemented using
+ PHP-CPP.
+</p>
+<p>
+ You may wonder why PHP sometimes uses <a href="magic-methods">magic methods</a>
+ (for example __set and __unset) and sometimes uses interfaces to change the
+ behavior of a class. There does not seem to be any uniformity in the choice
+ between these magic methods and interfaces. To us it is unclear why some
+ special features are implemented with magic methods, while others are activated
+ by implementing interfaces. In our eyes the Serializable interface could just
+ as well have been implemented with magic __serialize() and __unserialize()
+ methods, or the __invoke() method could just as well have been an "Invokable"
+ interface. PHP is not a standardized language, and some things just seem to be
+ the way they are because someone felt like implementing it this way or another...
+</p>
+<p>
+ The PHP-CPP library tries to stay as close to PHP as possible. That's why in
+ your C++ classes you can also use the special interfaces - and because C++
+ does not have interfaces like PHP has, classes with pure virtual methods are
+ used instead.
+</p>
+<h2>Support for the SPL</h2>
+<p>
+ A standard PHP installation comes with the Standard PHP Library (SPL). This
+ is an extension that is built on top of the Zend engine and that uses features
+ from the Zend engine to create classes and interfaces like Countable, Iterator
+ and ArrayAccess.
+</p>
+<p>
+ The PHP-CPP library also has interfaces with these names, and they behave in
+ more or less the same way as the SPL interfaces. But internally, the PHP-CPP
+ does not depend on the SPL. If you implement a C++ interface like
+ Php::ArrayAccess or Php::Countable, it is something different than writing
+ a class in PHP that implements a SPL interface.
+</p>
+<p>
+ Both PHP-CPP and the SPL are directly built on top of the Zend core and
+ offer the same sort of features, but they do not rely on each other. You can
+ thus safely use PHP-CPP if you have not loaded the SPL extension.
+</p>
+<h2>The Countable interface</h2>
+<p>
+ By implementing the Php::Countable interface, you can create objects that
+ can be passed to the PHP count() function.
+</p>
+<p>
+<pre class="language-c++"><code>
+#include &lt;phpcpp.h&gt;
+
+/**
+ * The famous counter class, now also implements
+ * the Php::Countable interface
+ */
+class Counter : public Php::Base, public Php::Countable
+{
+private:
+ /**
+ * The internal counter value
+ * @var int
+ */
+ int _value = 0;
+
+public:
+ /**
+ * C++ constructor and C++ destructpr
+ */
+ Counter() {}
+ virtual ~Counter() {}
+
+ /**
+ * Methods to increment and decrement the counter
+ */
+ Php::Value increment() { return ++_value; }
+ Php::Value decrement() { return --_value; }
+
+ /**
+ * Method from the Php::Countable interface, that
+ * is used when a Counter instance is passed to the
+ * PHP count() function
+ *
+ * @return long
+ */
+ virtual long count() override { 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() {
+
+ // 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");
+
+ // add methods
+ counter.method("increment", &Counter::increment);
+ counter.method("decrement", &Counter::decrement);
+
+ // add the class to the extension
+ myExtension.add(std::move(counter));
+
+ // return the extension
+ return myExtension;
+ }
+}
+</code></pre>
+</p>
+<p>
+ The Counter class that we used before has been modified to
+ show how to make classes that implement the Php::Countable interface.
+ It is very simple, all you have to to is add the Php::Countable class
+ as base class. This Php::Countable class has one pure virtual method,
+ count(), that has to be implemented.
+</p>
+<p>
+ And that's is all that you have to do. There is no need to register the
+ special count() function inside the get_module() function, adding
+ Php::Countable as base class is sufficient.
+</p>
+<p>
+<pre class="language-php"><code>
+&lt;?php
+// create a counter
+$counter = new Counter();
+$counter->increment();
+$counter->increment();
+$counter->increment();
+
+// show the current value
+echo(count($counter)."\n");
+?&gt;
+</pre></code>
+</p>
+<p>
+ The output is, as expected, the value 3.
+</p>
+<h2>The ArrayAccess interface</h2>
+<p>
+ A PHP object can be turned into a variable that behaves like an array by
+ implementing the Php::ArrayAccess interface. When you do this, objects
+ can be accessed with array access operators ($object["property"]).
+</p>
+<p>
+ In the following example we use the Php::Countable and the Php::ArrayAccess
+ interfaces to create an associative array class than can
+ be used for storing strings (remember: this is just an example, PHP already
+ has support for associative arrays, so it is debatable how useful the
+ example is).
+</p>
+<p>
+<pre class="language-c++"><code>
+#include &lt;phpcpp.h&gt;
+
+/**
+ * A sample Map class, that can be used to map string-to-strings
+ */
+class Map : public Php::Base, public Php::Countable, public Php::ArrayAccess
+{
+private:
+ /**
+ * Internally, a C++ map is used
+ * @var std::map&lt;std::string,std::string&gt;
+ */
+ std::map&lt;std::string,std::string&gt; _map;
+
+public:
+ /**
+ * C++ constructor and C++ destructpr
+ */
+ Map() {}
+ virtual ~Map() {}
+
+ /**
+ * Method from the Php::Countable interface that
+ * returns the number of elements in the map
+ * @return long
+ */
+ virtual long count() override
+ {
+ return _map.size();
+ }
+
+ /**
+ * Method from the Php::ArrayAccess interface that is
+ * called to check if a certain key exists in the map
+ * @param key
+ * @return bool
+ */
+ virtual bool offsetExists(const Php::Value &key) override
+ {
+ return _map.find(key) != _map.end();
+ }
+
+ /**
+ * Set a member
+ * @param key
+ * @param value
+ */
+ virtual void offsetSet(const Php::Value &key, const Php::Value &value) override
+ {
+ _map[key] = value.stringValue();
+ }
+
+ /**
+ * Retrieve a member
+ * @param key
+ * @return value
+ */
+ virtual Php::Value offsetGet(const Php::Value &key) override
+ {
+ return _map[key];
+ }
+
+ /**
+ * Remove a member
+ * @param key
+ */
+ virtual void offsetUnset(const Php::Value &key) override
+ {
+ _map.erase(key);
+ }
+};
+
+/**
+ * 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() {
+
+ // 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;Map&gt; map("Map");
+
+ // add the class to the extension
+ myExtension.add(std::move(map));
+
+ // return the extension
+ return myExtension;
+ }
+}
+</code></pre>
+</p>
+<p>
+ The Php::ArrayAccess has four pure virtual methods that have to be
+ implemented. These are methods to retrieve and overwrite an element,
+ to check if an element with a certain key exists, and a method to
+ remove an element. In the example these methods have all been implemented
+ to be forwarded to a regular C++ std::map object.
+</p>
+<p>
+ Inside the get_module() function, the Map is registered and added to the
+ extension. But unlike many other examples, none of the class methods are
+ exported to PHP. It only implements the Php::Countable interface and
+ Php::ArrayAccess interface, so it is perfectly usable to store and retrieve
+ properties, but from a PHP script it does not have any callable methods.
+ The following script shows how to use it.
+</p>
+<p>
+<pre class="language-php"><code>
+&lt;?php
+// create a map
+$map = new Map();
+
+// store some values
+$map["a"] = 1234;
+$map["b"] = "xyz";
+$map["c"] = 0;
+
+// show the values
+echo($map["a"]."\n");
+echo($map["b"]."\n");
+echo($map["c"]."\n");
+
+// access a value that does not exist
+echo($map["d"]."\n");
+
+?&gt;
+</pre></code>
+</p>
+<p>
+ The output speaks for itself. The map has three members, "1234" (a string
+ variable), "xyz" and "0".
+</p>
+<h2>The Traversable interface</h2>
+<p>
+ Classes can also be used in foreach loops, just like regular arrays. If you
+ want to enable this feature, your class should extend from the Php::Traverable
+ base class and implement the getIterator() method.
+</p>
+<p>
+<pre class="language-php"><code>
+&lt;?php
+// fill a map
+$map = new Map();
+$map["a"] = 1234;
+$map["b"] = 5678;
+
+// iterate over it
+foreach ($map as $key => $value)
+{
+ // output the key and value
+ echo("$key: $value\n");
+}
+?&gt;
+</code></pre>
+</p>
+<p>
+ The PHP-CPP library implements iterators in a slightly different manner than
+ the SPL does, and that you are used to if you have been working with PHP.
+ In PHP, to make a class traversable (usable in foreach loops), you have to
+ implement either the Iterator interface, or the IteratorAggregate interface.
+ This is a peculiar architecture - if not to say faulty. When you think of it,
+ it is not the container object itself that is the iterator, that container object is
+ only iterat<i>able</i>! It is <i>being iterated over</i>. In our above example,
+ the $map variable is not the actual iterator, but the container that is
+ iterated over. The real iterator is a hidden object that is not exposed to
+ your PHP script and that controls the foreach loop. The SPL however, would call
+ the map an iterator too.
+</p>
+<p>
+ In PHP-CPP we have therefore decided not to follow the SPL API,
+ and create a completely new way of implementing traversable classes.
+ To make a class traversable, it must be extended from the Php::Traversable
+ base class, that forces you to implement the getIterator() method. This
+ method should return a Php::Iterator instance.
+</p>
+<p>
+ The Php::Iterator object has five methods that are needed for
+ running the foreach loop. Note that your Iterator class does not have to
+ be a class that is accessible from PHP, and does not have to be derived from
+ Php::Base. It is an internal class that is used by foreach loops.
+</p>
+<p>
+<pre class="language-c++"><code>
+#include &lt;phpcpp.h&gt;
+
+/**
+ * A sample iterator class that can be used to iterate
+ * over a map of strings
+ */
+class MapIterator : public Php::Iterator
+{
+private:
+ /**
+ * The map that is being iterated over
+ * This is a reference to the actual map
+ * @var std::map&lt;std::string,std::string&gt;
+ */
+ const std::map&lt;std::string,std::string&gt; &_map;
+
+ /**
+ * The actual C++ iterator
+ * @var std::map&lt;std::string,std::string&gtl;::const_iterator;
+ */
+ std::map&lt;std::string,std::string&gt;::const_iterator _iter;
+
+public:
+ /**
+ * Constructor
+ * @param object The object that is being iterated over
+ * @param map The internal C++ map that is being iterated over
+ */
+ MapIterator(Map *object, const std::map&lt;std::string,std::string&gt; &map) :
+ Php::Iterator(object), _map(map), _iter(map.begin()) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~MapIterator() {}
+
+ /**
+ * Is the iterator on a valid position
+ * @return bool
+ */
+ virtual bool valid() override
+ {
+ return _iter != _map.end();
+ }
+
+ /**
+ * The value at the current position
+ * @return Value
+ */
+ virtual Php::Value current() override
+ {
+ return _iter->second;
+ }
+
+ /**
+ * The key at the current position
+ * @return Value
+ */
+ virtual Php::Value key() override
+ {
+ return _iter->first;
+ }
+
+ /**
+ * Move to the next position
+ */
+ virtual void next() override
+ {
+ _iter++;
+ }
+
+ /**
+ * Rewind the iterator to the front position
+ */
+ virtual void rewind() override
+ {
+ _iter = _map.begin();
+ }
+};
+
+
+/**
+ * A sample Map class, that can be used to map string-to-strings
+ */
+class Map :
+ public Php::Base,
+ public Php::Countable,
+ public Php::ArrayAccess,
+ public Php::Traversable
+{
+private:
+ /**
+ * Internally, a C++ map is used
+ * @var std::map&lt;std::string,std::string&gt;
+ */
+ std::map&lt;std::string,std::string&gt; _map;
+
+public:
+ /**
+ * C++ constructor and C++ destructpr
+ */
+ Map() {}
+ virtual ~Map() {}
+
+ /**
+ * Method from the Php::Countable interface that
+ * returns the number of elements in the map
+ * @return long
+ */
+ virtual long count() override
+ {
+ return _map.size();
+ }
+
+ /**
+ * Method from the Php::ArrayAccess interface that is
+ * called to check if a certain key exists in the map
+ * @param key
+ * @return bool
+ */
+ virtual bool offsetExists(const Php::Value &key) override
+ {
+ return _map.find(key) != _map.end();
+ }
+
+ /**
+ * Set a member
+ * @param key
+ * @param value
+ */
+ virtual void offsetSet(const Php::Value &key, const Php::Value &value) override
+ {
+ _map[key] = value.stringValue();
+ }
+
+ /**
+ * Retrieve a member
+ * @param key
+ * @return value
+ */
+ virtual Php::Value offsetGet(const Php::Value &key) override
+ {
+ return _map[key];
+ }
+
+ /**
+ * Remove a member
+ * @param key
+ */
+ virtual void offsetUnset(const Php::Value &key) override
+ {
+ _map.erase(key);
+ }
+
+ /**
+ * Get the iterator
+ * @return Php::Iterator
+ */
+ virtual Php::Iterator *getIterator() override
+ {
+ // construct a new map iterator on the heap
+ // the (PHP-CPP library will delete it when ready)
+ return new MapIterator(this, _map);
+ }
+};
+
+/**
+ * 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() {
+
+ // 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;Map&gt; map("Map");
+
+ // add the class to the extension
+ myExtension.add(std::move(map));
+
+ // return the extension
+ return myExtension;
+ }
+}
+</code></pre>
+</p>
+<p>
+ The above example further extends the Map class. It now implements
+ Php::Countable, Php::ArrayAccess and Php::Traversable. This means that
+ Map objects can now also be used inside foreach loop to iterate over the
+ properties.
+</p>
+<p>
+ For this to work, we had to add the Php::Traversable class as base class
+ to the Map class, and implement the getIterator() method. This method
+ returns a new MapIterator class, which is allocated on the heap. Don't
+ worry about memory management: the PHP-CPP library will destruct your
+ iterator the moment the foreach loop is finished.
+</p>
+<p>
+ The MapIterator class is derived from the Php::Iterator class, and
+ implements the five methods that are needed for running foreach
+ loops (current(), key(), next(), rewind() and valid()). Note that the
+ base Php::Iterator class expects that the object over which it iterates
+ is passed to the constructor. This is required so that the iterator object
+ can ensure that this iterated object stays in scope for as long as the
+ iterator exists.
+</p>
+<p>
+ Our MapIterator implementation internally is just a small wrapper around
+ a C++ iterator class. It is of course up to you to create more complex
+ iterators when needed.
+</p>