diff options
author | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-10 13:37:09 +0100 |
---|---|---|
committer | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-10 13:37:09 +0100 |
commit | dd3422f6ed454d06e6091ad5023da7fc294595b8 (patch) | |
tree | d37cc9cc541f4e6cc2fb4c1c04c704620b7151c7 /documentation/magic-interfaces.html | |
parent | 4872cc627642044f46ba8a8726902592a1fae05f (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.html | 579 |
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 <phpcpp.h> + +/** + * 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<Counter> 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> +<?php +// create a counter +$counter = new Counter(); +$counter->increment(); +$counter->increment(); +$counter->increment(); + +// show the current value +echo(count($counter)."\n"); +?> +</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 <phpcpp.h> + +/** + * 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<std::string,std::string> + */ + std::map<std::string,std::string> _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<Map> 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> +<?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"); + +?> +</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> +<?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"); +} +?> +</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 <phpcpp.h> + +/** + * 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<std::string,std::string> + */ + const std::map<std::string,std::string> &_map; + + /** + * The actual C++ iterator + * @var std::map<std::string,std::string>l;::const_iterator; + */ + std::map<std::string,std::string>::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<std::string,std::string> &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<std::string,std::string> + */ + std::map<std::string,std::string> _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<Map> 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> |