diff options
Diffstat (limited to 'documentation')
-rw-r--r-- | documentation/magic-methods-and-interfaces.html | 274 |
1 files changed, 273 insertions, 1 deletions
diff --git a/documentation/magic-methods-and-interfaces.html b/documentation/magic-methods-and-interfaces.html index 6ece6a5..244ebcd 100644 --- a/documentation/magic-methods-and-interfaces.html +++ b/documentation/magic-methods-and-interfaces.html @@ -219,7 +219,7 @@ public: */ virtual void offsetSet(const Php::Value &key, const Php::Value &value) override { - _map[key] = value; + _map[key] = value.stringValue(); } /** @@ -311,3 +311,275 @@ echo($map["d"]."\n"); 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(Php::Base *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 has 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 + operator 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 + implementation when needed. +</p> |