summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-15 01:32:22 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-15 01:32:22 +0100
commit3300d64c4dd61e71b62d5b55b1f16e34d6335680 (patch)
treec2bd999ba1a2a1320acba20fb99acf0defb9651e
parent681d1e6aa735568a492140d2307a89063e7aadb9 (diff)
parentcda98dc0dea7144941a46ff20598f3c095d7a6d0 (diff)
implemented __clone method
-rw-r--r--Makefile5
-rw-r--r--documentation/constructors-and-destructors.html8
-rw-r--r--documentation/install.html37
-rw-r--r--documentation/magic-methods.html82
-rw-r--r--documentation/properties.html272
-rw-r--r--documentation/variables.html22
-rw-r--r--include/array.h7
-rw-r--r--include/base.h12
-rw-r--r--include/callstatic.h64
-rw-r--r--include/class.h22
-rw-r--r--include/classbase.h46
-rw-r--r--include/globals.h2
-rw-r--r--include/hashmember.h30
-rw-r--r--include/super.h76
-rw-r--r--include/value.h29
-rw-r--r--phpcpp.h1
-rw-r--r--src/arithmetic.h14
-rw-r--r--src/base.cpp35
-rw-r--r--src/classbase.cpp242
-rw-r--r--src/extension.cpp2
-rw-r--r--src/includes.h4
-rw-r--r--src/numericmember.h (renamed from src/longmember.h)10
-rw-r--r--src/property.h160
-rw-r--r--src/super.cpp59
-rw-r--r--src/value.cpp66
25 files changed, 1095 insertions, 212 deletions
diff --git a/Makefile b/Makefile
index 734590b..32fd0b0 100644
--- a/Makefile
+++ b/Makefile
@@ -80,8 +80,11 @@ COMPILER_FLAGS = -Wall -c -I. -I${PHP_DIR} -I${PHP_DIR}/main -I${PHP_DIR}/ext
# Just like the compiler, the linker can have flags too. The default flag
# is probably the only one you need.
#
+# Are you compiling on OSX? You may have to append the option "-undefined dynamic_lookup"
+# to the linker flags
+#
-LINKER_FLAGS = -shared
+LINKER_FLAGS = -shared `php-config --ldflags`
#
diff --git a/documentation/constructors-and-destructors.html b/documentation/constructors-and-destructors.html
index d7cc61b..399367a 100644
--- a/documentation/constructors-and-destructors.html
+++ b/documentation/constructors-and-destructors.html
@@ -135,10 +135,10 @@ extern "C" {
// description of the class so that PHP knows which methods are accessible
Php::Class&lt;Counter&gt; counter("Counter");
- counter.method("__construct", &Counter::__construct);
- counter.method("increment", &Counter::increment);
- counter.method("decrement", &Counter::decrement);
- counter.method("value", &Counter::value);
+ counter.method("__construct", &amp;Counter::__construct);
+ counter.method("increment", &amp;Counter::increment);
+ counter.method("decrement", &amp;Counter::decrement);
+ counter.method("value", &amp;Counter::value);
// add the class to the extension
myExtension.add(std::move(counter));
diff --git a/documentation/install.html b/documentation/install.html
index 7f7a2c3..d1d1cb0 100644
--- a/documentation/install.html
+++ b/documentation/install.html
@@ -5,17 +5,16 @@
system(s).
</p>
<p>
- Luckily, for most of us (those who use Linux environments), this will be
- a piece of cake. If you're on a different platform however, you are left on
- your own, because we (as in me, the PHP-CPP developer), only uses Linux
- systems. There is however no reason why this library should not also work on
- other platforms, because it only uses straight forward C++ code. Thus, if
- you are on a different platform and have managed to compile the library on
- it, please give us feedback so that we can update these installation
- instructions and include other platforms as well.
+ Luckily, for most of us (those who use Linux or Apple environments), this
+ will be a piece of cake. If you're on a different platform however, you are
+ left on your own, because we (as in me, the PHP-CPP developer), only uses
+ Linux systems. There is however no reason why this library should not also
+ work on other platforms, because it only uses straight forward C++ code.
+ Thus, if you are on a different platform and have managed to compile the
+ library on it, please give us feedback so that we can update these
+ installation instructions and include other platforms as well.
</p>
-
<h2 id="limitations">Limitations</h2>
<p>
At this moment, PHP-CPP only supports single-threaded PHP installations.
@@ -85,7 +84,20 @@
<pre><code>make</code></pre>
</p>
<p>
- The PHP-CPP library has now been built, and all that is left to do is
+ This will start the compiler and build the library.
+</p>
+
+<h2 id="osx">Compiling on OSX?</h2>
+<p>
+ If you compile the software on OSX, you may run into linking and "unresolved
+ symbol" errors. In that case you will have to make a change to the Makefile.
+ Somewhere in this Makefile there is an option "LINKER_FLAGS". This option
+ should be extended, and the extra flag "-undefined dynamic_lookup" should
+ be added to it.
+</p>
+
+<p>
+ After you ran 'make', and the PHP-CPP library was built, all that is left to do is
install it on your system. You can use the "make install" command for it.
This command should be executed as root, either by using "sudo", or by
logging on as root first.
@@ -94,6 +106,7 @@
<pre><code>sudo make install</code></pre>
</p>
<p>
- Congratulations! You are now the happy owner of a system with PHP-CPP installed
- and nothing can stop you from building your first fast native PHP extension.
+ That was it! After these steps you are now the happy owner of a system with
+ PHP-CPP installed and nothing can stop you from building your first fast
+ native PHP extension.
</p>
diff --git a/documentation/magic-methods.html b/documentation/magic-methods.html
index 8a1fbfa..6356d2a 100644
--- a/documentation/magic-methods.html
+++ b/documentation/magic-methods.html
@@ -10,13 +10,21 @@
in your class, and if they do, they will be compiled into your extension
and called when they are accessed from PHP.
</p>
+<h2 id="compile-time-detection">Compile time detection</h2>
<p>
- The signature of the methods is flexible. Most of the methods accept
- Php::Value objects and also return Php::Value objects, but if your own
- magic methods have a slightly different signature (they return an integer
- for example) it will be picked up by the compiler too because the Php::Value
- has many implicit constructors to convert other types into Php::Value
- objects.
+ Although you may have expected that the magic methods are virtual methods in
+ the Php::Base class that can be overridden, they are not. The methods are
+ detected by the C++ compiler at compile time - and are very normal methods
+ that just happen to have a certain name.
+</p>
+<p>
+ Because of the compile time detection, the signature of the methods is
+ somewhat flexible. The return values of many magic methods are assigned to
+ Php::Value objects, which means that as long as you make sure
+ that your magic method returns a type that is assignable to a Php::Value,
+ you can use it in your class. Your __toString() method may thus return a
+ char*, a std::string, Php::Value (and even an integer!), because all these
+ types can be assigned to a Php::Value.
</p>
<p>
The nice thing about magic methods implemented with PHP-CPP is that they
@@ -25,6 +33,68 @@
can not be called explicitly from PHP scripts - but they do get called
when a property is accessed.
</p>
+<h2 id="constructors">Constructors</h2>
+<p>
+ Normally, magic methods do not have
+ to be registered to make them work. When you add a magic method like
+ __toString() or __get() to your class, it will automatically be
+ called when an object is casted to a string or a property is accessed. There
+ is no need to enable the magic method explicitly in the get_module() startup
+ function.
+</p>
+<p>
+ The only exception to this rule is the __construct() method. This method
+ does have to be explicitly registered. There are a number of reasons for this.
+ For a start, the __construct() method does not have a fixed signature, and
+ by explicitly adding it to the extension, you can also specify what
+ parameters it accepts, and whether the __construct() method should be public,
+ private or protected (if you want to create classes that can not be
+ instantiated from PHP).
+</p>
+<p>
+ The other reason why you have to explicitly register the __construct() method,
+ is that, unlike other magic methods, the __construct method <i>must</i>
+ be visible from PHP. Inside constructors of derived classes, it often is
+ necessary to make a call to parent::__construct(). By registering the
+ __construct() method in the get_module() function you make the function
+ visible from PHP.
+</p>
+<p>
+ We have a special article about <a href="constructors-and-destructors">
+ constructors and destructors</a> with multiple examples how to register
+ the __construct() method.
+<p>
+<h2 id="clone-and-destruct">Clone and destruct</h2>
+<p>
+ The magic __clone() method is very similar to the __construct() method. It
+ also is a method that is called directly <i>after</i> an object is constructed.
+ The difference is that __clone() is called after an object is <i>copy
+ constructed</i> (or <i>cloned</i/> if you prefer the PHP idiom), while
+ __construct() is called right after the normal constructor.
+</p>
+<p>
+ The magic __destruct() method gets called right before the object gets
+ destructed (and right before the C++ destructor runs).
+</p>
+<p>
+ The __clone() and __destruct() methods are regular magic methods (unlike
+ __construct()) and you therefore do not have to register them to make them
+ active. If you add one of these two methods to your class, you will not have
+ to make any changes to the get_module() startup function. The PHP-CPP library
+ calls them automatically if they are available.
+</p>
+<p>
+ In normal circumstances you probably have no need for these methods.
+ The C++ copy constructor and the C++ destructor can be used too. The only
+ difference is that the magic methods are called on objects that are in a
+ fully initialized state, while the C++ copy constructor and C++ destructor
+ work on objects that are <i>being initialized</i>, or that are
+ <i>being destructed</i>.
+</p>
+<p>
+ The article about mentioned above about <a href="constructors-and-destructors">
+ constructors and destructors</a> has more details and examples.
+</p>
<h2 id="pseudo-properties">Pseudo properties</h2>
<p>
With the methods __get(), __set(), __unset() and __isset() you can define
diff --git a/documentation/properties.html b/documentation/properties.html
index bdbdfa7..f7f96c6 100644
--- a/documentation/properties.html
+++ b/documentation/properties.html
@@ -2,22 +2,274 @@
<p>
When you define a class completely in PHP, you can add properties (member
variables) to it. When you add member variables to a native C++ class however,
- you better use regular C++ member variables for that, instead of PHP variables.
+ you better use regular native member variables for that, instead of PHP variables.
Native variables have an immensely better performance than PHP variables,
and it would be insane to store integers or strings in Php::Value objects
if you can store them in int's and std::string objects as well.
</p>
+<h2 id="normal-members">Normal member variables</h2>
<p>
- To access these member variables you could create getX() and setX()
- methods, or alternatively implement __get() and __set() methods if you
- want to make your native member variables look like public or protected
- properties.
+ It is difficult to imagine that someone in the world would like to create
+ a native class, with regular non-typed public PHP properties on it. However,
+ if you insist, you can use the PHP-CPP library for this. Let's take an example
+ class in PHP, and see what it would look like in C++.
</p>
<p>
- I can not imagine that there is anyone in the world who would like to create
- a native class, with regular public PHP properties on it. But still, in this
- article we explain how you can do that.
+<pre class="language-php"><code>
+&lt;?php
+/**
+ * PHP example class
+ */
+class Example
+{
+ /**
+ * Define a public property
+ */
+ public $property1;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ // initialize the property
+ $this->property1 = "xyz";
+ }
+
+ /**
+ * Example method
+ */
+ public function method()
+ {
+ // do something with the public property (like changing it)
+ $this->property = "abc";
+ }
+}
+
+// create an instance
+$example = new Example();
+
+// overwrite the public property
+$example->property1 = "new value";
+
+?&gt;
+</code></pre>
</p>
<p>
- ... this article is not finished yet
-</p> \ No newline at end of file
+ The above example creates a class with one public property. This property
+ can be accessed by the Example class, and because it is public also by
+ everyone else, as is shown in the example. If you like such classes, you can
+ write something similar with PHP-CPP.
+</p>
+<p>
+<pre class="language-cpp"><code>
+#include &lt;phpcpp.h&gt;
+
+/**
+ * C++ Example class
+ */
+class Example : public Php::Base
+{
+public:
+ /**
+ * c++ constructor
+ */
+ Example() {}
+
+ /**
+ * c++ destructor
+ */
+ virtual ~Example() {}
+
+ /**
+ * php "constructor"
+ * @param params
+ */
+ void __construct()
+ {
+ // get self reference as Php::Value object
+ Php::Value self(this);
+
+ // initialize a public property
+ self["property1"] = "xyz";
+ }
+
+ /**
+ * Example method
+ */
+ void method()
+ {
+ // get self reference as Php::Value object
+ Php::Value self(this);
+
+ // overwrite the property
+ self["property1"] = "abc";
+ }
+};
+
+/**
+ * Switch to C context so that the get_module() function can be
+ * called by C programs (which the Zend engine is)
+ */
+extern "C" {
+ /**
+ * Startup function for the extension
+ * @return void*
+ */
+ PHPCPP_EXPORT void *get_module() {
+ // create static 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;Example&gt; example("Example");
+
+ // register the methods
+ example.method("__construct", &amp;Example::__construct);
+ example.method("method", &amp;Example::method);
+
+ // the Example class has one public property
+ example.property("property1", "xyz", Php::Public);
+
+ // add the class to the extension
+ myExtension.add(std::move(example));
+
+ // return the extension
+ return myExtension;
+ }
+}
+</code></pre>
+</p>
+<p>
+ The example code shows how you initialize the properties inside
+ the get_module() function.
+</p>
+<p>
+ Instead of public properties, you can also define private or protected
+ properties, but even that is probably not what you want, as storing
+ data in native C++ variables is much faster.
+</p>
+<h2>Smart properties</h2>
+<p>
+ With the <a href="magic-methods">magic methods __get() and __set()</a> you
+ can make more advanced properties that are directly mapped to C++
+ variables, and that allow you to perform additional checks when a property
+ is overwritten, so that an object always remains in a valid state.
+</p>
+<p>
+ On top of that, with the PHP-CPP library you can also assign getter and
+ setter methods to properties. Every time a property is accessed, your getter
+ or setter method is automatically called.
+</p>
+<p>
+<pre class="language-cpp"><code>
+#include &lt;phpcpp.h&gt;
+
+/**
+ * C++ Example class
+ */
+class Example : public Php::Base
+{
+private:
+ /**
+ * Example property
+ * @var int
+ */
+ int _value = 0;
+
+
+public:
+ /**
+ * c++ constructor
+ */
+ Example() {}
+
+ /**
+ * c++ destructor
+ */
+ virtual ~Example() {}
+
+ /**
+ * Method to get access to the property
+ * @return Php::Value
+ */
+ Php::Value getValue() const
+ {
+ return _value;
+ }
+
+ /**
+ * Method to overwrite the property
+ * @param value
+ */
+ void setValue(const Php::Value &amp;value)
+ {
+ // overwrite property
+ _value = value;
+
+ // sanity check: the value should never exceed 100
+ if (_value > 100) _value = 100;
+ }
+
+ /**
+ * Method to retrieve the double property value
+ * @return Php::Value
+ */
+ Php::Value getDouble() const
+ {
+ return _value * 2;
+ }
+};
+
+/**
+ * Switch to C context so that the get_module() function can be
+ * called by C programs (which the Zend engine is)
+ */
+extern "C" {
+ /**
+ * Startup function for the extension
+ * @return void*
+ */
+ PHPCPP_EXPORT void *get_module() {
+ // create static 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;Example&gt; example("Example");
+
+ // register the "value" property, with the methods to get and set it
+ example.property("value", &amp;Example::getValue, &amp;Example::setValue);
+
+ // register a read-only "double" property, with a method to get it
+ example.property("double", &amp;Example::getDouble);
+
+ // add the class to the extension
+ myExtension.add(std::move(example));
+
+ // return the extension
+ return myExtension;
+ }
+}
+</code></pre>
+</p>
+<p>
+ The following PHP script uses this. It created an example object, sets the
+ value property to 500 (which is not allowed, values higher than 100 are
+ rounded to 100), and then it reads out the double value.
+</p>
+<p>
+<pre class="language-php"><code>
+&lt;?php
+// create object
+$object = new Example();
+
+// set the value
+$object->value = 500;
+
+// show the double value
+echo($object->double."\n");
+
+// update the double value
+// (this will trigger an error, this is a read-only property)
+$object->double = 300;
+?&gt;
+</code></pre>
diff --git a/documentation/variables.html b/documentation/variables.html
index 6b63598..b3f4330 100644
--- a/documentation/variables.html
+++ b/documentation/variables.html
@@ -286,7 +286,7 @@ std::cout &lt;&lt; array("Y-m-d H:i:s") &lt;&lt; std::endl;
<h2 id="global-variables">Global variables</h2>
<p>
To read or update global PHP variables, you can use the Php::GLOBALS
- variable. This variable works more or less the same as the $_GLOBALS
+ variable. This variable works more or less the same as the $GLOBALS
variable in a PHP script.
</p>
<p>
@@ -305,7 +305,25 @@ std::cout &lt;&lt; Php::GLOBALS["b"] &lt;&lt; std::endl;
</code></pre>
</p>
<p>
- Unlike PHP scripts, that handles only single pageviews, an extension is
+ Next to the $GLOBALS variable, PHP allows you to access variables using
+ the $_GET, $_POST, $_COOKIE, $_FILES, $_SERVER, $_REQUEST and $_ENV variables.
+ In your C++ extension you can do something similar with the global variables
+ Php::GET, Php::POST, Php::COOKIE, Php::FILES, Php::SERVER, Php::REQUEST and
+ Php::ENV. These are global, read-only, objects with an overloaded operator[]
+ method. You can thus access them as if it were associative arrays.
+</p>
+<p>
+<pre class="language-c++"><code>
+// retrieve the value of a request variable
+int age = Php::REQUEST["name"];
+
+// or retrieve the value of a server variable
+std::string referer = Php::SERVER["HTTP_REFERER"];
+</code></pre>
+</p>
+<h2 id="global-natives">Be careful with global C++ variables</h2>
+<p>
+ Unlike PHP scripts, that only handle single pageviews, an extension is
used to handle multiple pageviews after each other. This means that when
you use global C++ (!) variables in your extension, that these variables are
not set back to their initial value in between the pageviews. The
diff --git a/include/array.h b/include/array.h
index dcd5e5f..26fec24 100644
--- a/include/array.h
+++ b/include/array.h
@@ -12,7 +12,7 @@
* Set up namespace
*/
namespace Php {
-
+
/**
* Class definition
*/
@@ -124,11 +124,6 @@ public:
};
/**
- * Define for arrays and objects
- */
-using Array = Array;
-
-/**
* End of namespace
*/
}
diff --git a/include/base.h b/include/base.h
index b9dced2..97b153d 100644
--- a/include/base.h
+++ b/include/base.h
@@ -111,6 +111,11 @@ public:
{
return Value(this)[name];
}
+
+ /**
+ * Overridable method that is called right before an object is destructed
+ */
+ void __destruct() const;
/**
* Overridable method that is called right after an object is cloned
@@ -120,13 +125,6 @@ public:
void __clone() const {}
/**
- * Overridable method that is called right before an object is destructed
- *
- * The default implementation does nothing
- */
- void __destruct() const {}
-
- /**
* Overridable method that is called to check if a property is set
*
* The default implementation does nothing, and the script will fall back
diff --git a/include/callstatic.h b/include/callstatic.h
deleted file mode 100644
index 3979ef3..0000000
--- a/include/callstatic.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * CallStatic.h
- *
- * Class that performs a lot of C++11 magic to find out if the __callStatic()
- * method was implemented by the user, and if it was, calls it
- *
- */
-
-namespace Php {
-
-/**
- * SFINAE test to check if the __callStatic method is defined
- *
- * This type trait checks if the __callStatic method is defined in class T
- *
- * @see http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
- */
-template <typename T>
-class HasCallStatic
-{
- typedef char one;
- typedef long two;
-
- template <typename C> static one test( decltype(&C::__callStatic) ) ;
- template <typename C> static two test(...);
-
-public:
- static const bool value = sizeof(test<T>(0)) == sizeof(char);
-};
-
-/**
- * Function that only exists if the class T has a __callStatic method
- * @param name Name of the function
- * @param params Parameters passed to the function
- * @return Value
- */
-template<typename T>
-typename std::enable_if<HasCallStatic<T>::value, Value >::type
-callStatic(const char *name, Parameters &params)
-{
- // call the __callStatic() function
- return T::__callStatic(name, params);
-}
-
-/**
- * Function that only exists if the class T does not have a __callStatic method
- * @param name Name of the function
- * @param params Parameters passed to the function
- * @return Value
- */
-template<typename T>
-typename std::enable_if<!HasCallStatic<T>::value >::type
-callStatic(const char *name, Parameters &params)
-{
- std::cout << "has NO call static" << std::endl;
-
- return nullptr;
-}
-
-/**
- * End namespace
- */
-}
-
diff --git a/include/class.h b/include/class.h
index f8e3365..f8ba578 100644
--- a/include/class.h
+++ b/include/class.h
@@ -156,7 +156,27 @@ public:
void property(const char *name, const std::string &value, int flags = Public) { ClassBase::property(name, value, flags); }
void property(const char *name, bool value, int flags = Public) { ClassBase::property(name, value, flags); }
void property(const char *name, double value, int flags = Public) { ClassBase::property(name, value, flags); }
-
+
+ /**
+ * Properties as methods
+ *
+ * This is a smarter way for adding properties to a class. You can define
+ * a property and a method that gets called every time the property is
+ * set or unset.
+ *
+ * If you do not set a setter method, your property will be read-only.
+ *
+ * @param name Name of the property
+ * @param getter The getter method
+ * @param setter The setter method
+ */
+ void property(const char *name, Value (T::*getter)() ) { ClassBase::property(name, static_cast<getter_callback_0>(getter)); }
+ void property(const char *name, Value (T::*getter)() const ) { ClassBase::property(name, static_cast<getter_callback_1>(getter)); }
+ void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast<getter_callback_0>(getter), static_cast<setter_callback_0>(setter)); }
+ void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast<getter_callback_1>(getter), static_cast<setter_callback_0>(setter)); }
+ void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast<getter_callback_0>(getter), static_cast<setter_callback_1>(setter)); }
+ void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast<getter_callback_1>(getter), static_cast<setter_callback_1>(setter)); }
+
private:
/**
* Construct a new instance of the object
diff --git a/include/classbase.h b/include/classbase.h
index fac9dbd..6a71ecb 100644
--- a/include/classbase.h
+++ b/include/classbase.h
@@ -52,10 +52,19 @@ typedef Value (Base::*method_callback_6)() const;
typedef Value (Base::*method_callback_7)(Parameters &) const;
/**
+ * Signatures for getters and setters
+ */
+typedef Value (Base::*getter_callback_0)();
+typedef Value (Base::*getter_callback_1)() const;
+typedef void (Base::*setter_callback_0)(const Php::Value &value);
+typedef void (Base::*setter_callback_1)(const Php::Value &value) const;
+
+/**
* Forward declarations
*/
class Method;
class Member;
+class Property;
/**
* Class definition
@@ -87,6 +96,7 @@ public:
_type(that._type),
_methods(that._methods),
_members(that._members),
+ _properties(that._properties),
_entry(nullptr) {}
/**
@@ -98,6 +108,7 @@ public:
_type(that._type),
_methods(std::move(that._methods)),
_members(std::move(that._members)),
+ _properties(std::move(that._properties)),
_entry(that._entry)
{
// other entry are invalid now (not that it is used..., class objects are
@@ -271,6 +282,19 @@ protected:
void property(const char *name, const char *value, int flags = Php::Public);
void property(const char *name, double value, int flags = Php::Public);
+ /**
+ * Set property with callbacks
+ * @param name Name of the property
+ * @param getter Getter method
+ * @param setter Setter method
+ */
+ void property(const char *name, const getter_callback_0 &getter);
+ void property(const char *name, const getter_callback_1 &getter);
+ void property(const char *name, const getter_callback_0 &getter, const setter_callback_0 &setter);
+ void property(const char *name, const getter_callback_1 &getter, const setter_callback_0 &setter);
+ void property(const char *name, const getter_callback_0 &getter, const setter_callback_1 &setter);
+ void property(const char *name, const getter_callback_1 &getter, const setter_callback_1 &setter);
+
private:
/**
* Retrieve an array of zend_function_entry objects that hold the
@@ -283,6 +307,14 @@ private:
const struct _zend_function_entry *entries();
/**
+ * Helper method to turn a property into a zval
+ * @param value
+ * @param type
+ * @return Value
+ */
+ static struct _zval_struct *toZval(Value &&value, int type);
+
+ /**
* Static member functions to create or clone objects based on this class
* @param entry Pointer to class information
* @param val The object to be cloned
@@ -290,6 +322,8 @@ private:
*/
static struct _zend_object_value createObject(struct _zend_class_entry *entry);
static struct _zend_object_value cloneObject(struct _zval_struct *val);
+ static void destructObject(struct _zend_object *object, unsigned int handle);
+ static void freeObject(struct _zend_object *object);
/**
* Static member function that get called when a method or object is called
@@ -502,6 +536,18 @@ private:
* @var std::list
*/
std::list<std::shared_ptr<Member>> _members;
+
+ /**
+ * Map of dynamically accessible properties
+ * @var std::map
+ */
+ std::map<std::string,std::shared_ptr<Property>> _properties;
+
+ /**
+ * Base object has access to the members
+ * This is needed by the Base::store() method
+ */
+ friend class Base;
};
/**
diff --git a/include/globals.h b/include/globals.h
index 0278c88..b996131 100644
--- a/include/globals.h
+++ b/include/globals.h
@@ -68,7 +68,7 @@ public:
};
/**
- * We always have one instance
+ * We always have one instance of the GLOBALS instance
* @var Globals
*/
extern Globals &GLOBALS;
diff --git a/include/hashmember.h b/include/hashmember.h
index 65ca23d..41c40d3 100644
--- a/include/hashmember.h
+++ b/include/hashmember.h
@@ -43,7 +43,7 @@ public:
HashMember &operator=(const Value &value)
{
// set property in parent array
- _base.set(_index, value);
+ _base->set(_index, value);
// if there is a parent, it should sets its value too
if (_parent) _parent->operator=(_base);
@@ -58,7 +58,7 @@ public:
*/
Value value() const
{
- return _base.get(_index);
+ return _base->get(_index);
}
/**
@@ -67,7 +67,7 @@ public:
*/
operator Value () const
{
- return _base.get(_index);
+ return _base->get(_index);
}
/**
@@ -76,7 +76,7 @@ public:
*/
operator int16_t () const
{
- return _base.get(_index).numericValue();
+ return _base->get(_index).numericValue();
}
/**
@@ -85,7 +85,7 @@ public:
*/
operator int32_t () const
{
- return _base.get(_index).numericValue();
+ return _base->get(_index).numericValue();
}
/**
@@ -94,7 +94,7 @@ public:
*/
operator int64_t () const
{
- return _base.get(_index).numericValue();
+ return _base->get(_index).numericValue();
}
/**
@@ -103,7 +103,7 @@ public:
*/
operator bool () const
{
- return _base.get(_index).boolValue();
+ return _base->get(_index).boolValue();
}
/**
@@ -112,7 +112,7 @@ public:
*/
operator std::string () const
{
- return _base.get(_index).stringValue();
+ return _base->get(_index).stringValue();
}
/**
@@ -121,7 +121,7 @@ public:
*/
operator const char * () const
{
- return _base.get(_index).rawValue();
+ return _base->get(_index).rawValue();
}
/**
@@ -130,7 +130,7 @@ public:
*/
operator double () const
{
- return _base.get(_index).decimalValue();
+ return _base->get(_index).decimalValue();
}
/**
@@ -141,7 +141,7 @@ public:
*/
HashMember operator[](int index)
{
- return _base.get(_index)[index].add(this);
+ return _base->get(_index)[index].add(this);
}
/**
@@ -152,7 +152,7 @@ public:
*/
HashMember operator[](const std::string &key)
{
- return _base.get(_index)[key].add(this);
+ return _base->get(_index)[key].add(this);
}
/**
@@ -163,7 +163,7 @@ public:
*/
HashMember operator[](const char *key)
{
- return _base.get(_index)[key].add(this);
+ return _base->get(_index)[key].add(this);
}
/**
@@ -359,7 +359,7 @@ private:
* @param base Base value
* @param index Index in the array
*/
- HashMember(const Value *base, Type index) : _base(*base), _index(index) {}
+ HashMember(Value *base, Type index) : _base(base), _index(index) {}
// @todo add move constructor
@@ -390,7 +390,7 @@ private:
* Base value
* @var Value
*/
- Value _base;
+ Value *_base;
/**
* Parent member (in case of nested members)
diff --git a/include/super.h b/include/super.h
new file mode 100644
index 0000000..73e3761
--- /dev/null
+++ b/include/super.h
@@ -0,0 +1,76 @@
+/**
+ * Super.h
+ *
+ * The Super class is used to implement one of the super variables $_POST,
+ * $_GET, $_SERVER, et cetera
+ *
+ * @copyright 2014 Copernica BV
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class Super
+{
+public:
+ /**
+ * Constructor
+ *
+ * Extension writers do not have to access the super-globals themselves.
+ * They are always accessible via Php::POST, Php::GET, et cetera.
+ *
+ * @param index number
+ */
+ Super(int index) : _index(index) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~Super() {}
+
+ /**
+ * Array access operator
+ * This can be used for accessing associative arrays
+ * @param key
+ * @return Value
+ */
+ Value operator[](const std::string &key) const;
+
+ /**
+ * Array access operator
+ * This can be used for accessing associative arrays
+ * @param key
+ * @return Value
+ */
+ Value operator[](const char *key) const;
+
+private:
+ /**
+ * Index number
+ * @var int
+ */
+ int _index;
+};
+
+/**
+ * A number of super-globals are always accessible
+ */
+extern Super POST;
+extern Super GET;
+extern Super COOKIE;
+extern Super SERVER;
+extern Super ENV;
+extern Super FILES;
+extern Super REQUEST;
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/include/value.h b/include/value.h
index f2850c9..fce8750 100644
--- a/include/value.h
+++ b/include/value.h
@@ -112,6 +112,12 @@ public:
Value(struct _zval_struct *zval, bool ref = false);
/**
+ * Wrap around a hash table
+ * @param ht Hashtable to wrap
+ */
+ Value(struct _hashtable *ht);
+
+ /**
* Wrap around an object implemented by us
* @param object Object to be wrapped
*/
@@ -375,9 +381,15 @@ public:
/**
* Retrieve the value as number
- * @return long
+ *
+ * We force this to be a int64_t because we assume that most
+ * servers run 64 bits nowadays, and because we use int32_t, int64_t
+ * almost everywhere, instead of 'long' and on OSX neither of
+ * these intxx_t types is defined as 'long'...
+ *
+ * @return int64_t
*/
- long numericValue() const;
+ int64_t numericValue() const;
/**
* Retrieve the value as boolean
@@ -846,6 +858,18 @@ protected:
struct _zval_struct *detach();
/**
+ * Attach a different zval
+ *
+ * This will first detach the current zval, and link the Value object to
+ * a different zval. Versions exist to attach to a zval and to an entire
+ * hash table
+ *
+ * @param val
+ */
+ void attach(struct _zval_struct *val);
+ void attach(struct _hashtable *hashtable);
+
+ /**
* Set a certain property without running any checks (you must already know
* for sure that this is an array, and that the index is not yet in use)
*
@@ -874,6 +898,7 @@ protected:
friend class Member;
friend class ClassBase;
friend class Iterator;
+ friend class Extension;
};
/**
diff --git a/phpcpp.h b/phpcpp.h
index 599c2cb..f58eb8d 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -36,6 +36,7 @@
#include <phpcpp/byval.h>
#include <phpcpp/byref.h>
#include <phpcpp/global.h>
+#include <phpcpp/super.h>
#include <phpcpp/hashmember.h>
#include <phpcpp/parameters.h>
#include <phpcpp/modifiers.h>
diff --git a/src/arithmetic.h b/src/arithmetic.h
index ae690bc..21343d1 100644
--- a/src/arithmetic.h
+++ b/src/arithmetic.h
@@ -55,7 +55,7 @@ public:
if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value));
// apply to natural numbers
- return Value(F<long>()(_value->numericValue(), value));
+ return Value(F<int64_t>()(_value->numericValue(), value));
}
/**
@@ -69,7 +69,7 @@ public:
if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value));
// apply to natural numbers
- return Value(F<long>()(_value->numericValue(), value));
+ return Value(F<int64_t>()(_value->numericValue(), value));
}
/**
@@ -83,7 +83,7 @@ public:
if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value));
// apply to natural numbers
- return Value(F<long>()(_value->numericValue(), value));
+ return Value(F<int64_t>()(_value->numericValue(), value));
}
/**
@@ -97,7 +97,7 @@ public:
if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value?1:0));
// apply to natural numbers
- return Value(F<long>()(_value->numericValue(), value?1:0));
+ return Value(F<int64_t>()(_value->numericValue(), value?1:0));
}
/**
@@ -114,7 +114,7 @@ public:
if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), v));
// apply to natural numbers
- return Value(F<long>()(_value->numericValue(), v));
+ return Value(F<int64_t>()(_value->numericValue(), v));
}
/**
@@ -174,7 +174,7 @@ public:
if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value));
// do a numeric operation
- return _value->operator=(F<long>()(_value->numericValue(), value));
+ return _value->operator=(F<int64_t>()(_value->numericValue(), value));
}
/**
@@ -188,7 +188,7 @@ public:
if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value));
// do a numeric operation
- return _value->operator=(F<long>()(_value->numericValue(), value));
+ return _value->operator=(F<int64_t>()(_value->numericValue(), value));
}
/**
diff --git a/src/base.cpp b/src/base.cpp
index 2c46147..f677bcf 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -13,29 +13,6 @@
namespace Php {
/**
- * Function that is called to clean up space that is occupied by the object
- * @param object The object to be deallocated
- */
-static void deallocate_object(void *object TSRMLS_DC)
-{
- // allocate memory for the object
- MixedObject *obj = (MixedObject *)object;
-
- // deallocate the cpp object
- if (obj->cpp) delete obj->cpp;
-
- // get rid of the object properties
- // @todo if we enable the following two lines, segmentation
- // faults and memory corruption occurs. however, the online
- // documentation does it like this
- //zend_hash_destroy(obj->php.properties);
- //FREE_HASHTABLE(obj->php.properties);
-
- // deallocate the entire object
- efree(obj);
-}
-
-/**
* Store the object in the PHP object cache
* @param entry Class entry
* @return MixedObject
@@ -71,13 +48,23 @@ MixedObject *Base::store(zend_class_entry *entry)
// the destructor and clone handlers are set to NULL. I dont know why, but they do not
// seem to be necessary...
- _handle = zend_objects_store_put(result, NULL, deallocate_object, NULL TSRMLS_CC);
+ _handle = zend_objects_store_put(result, (zend_objects_store_dtor_t)ClassBase::destructObject, (zend_objects_free_object_storage_t)ClassBase::freeObject, NULL TSRMLS_CC);
// done
return result;
}
/**
+ * Overridable method that is called right before an object is destructed
+ */
+void Base::__destruct() const
+{
+ // throw exception, so that the PHP-CPP library will check if the user
+ // somehow registered an explicit __destruct method
+ throw NotImplemented();
+}
+
+/**
* Overridable method that is called to check if a property is set
*
* The default implementation does nothing, and the script will fall back
diff --git a/src/classbase.cpp b/src/classbase.cpp
index ccfb9da..0de62dd 100644
--- a/src/classbase.cpp
+++ b/src/classbase.cpp
@@ -649,22 +649,7 @@ zval *ClassBase::readDimension(zval *object, zval *offset, int type)
try
{
// ArrayAccess is implemented, call function
- Value value = arrayaccess->offsetGet(offset);
-
- // because we do not want the value object to destruct the zval when
- // it falls out of scope, we detach the zval from it, if this is a regular
- // read operation we can do this right away
- if (type == 0) return value.detach();
-
- // this is a more complicated read operation, the scripts wants to get
- // deeper access to the returned value. This, however, is only possible
- // if the value has more than once reference (if it has a refcount of one,
- // the value object that we have here is the only instance of the zval,
- // and it is simply impossible to return a reference or so
- if (value.refcount() <= 1) return value.detach();
-
- // we're dealing with an editable zval, return a reference variable
- return Value(value.detach(), true).detach();
+ return toZval(arrayaccess->offsetGet(offset), type);
}
catch (Exception &exception)
{
@@ -817,6 +802,30 @@ void ClassBase::unsetDimension(zval *object, zval *member)
}
/**
+ * Helper method to turn a property into a zval
+ * @param value
+ * @param type
+ * @return Value
+ */
+zval *ClassBase::toZval(Value &&value, int type)
+{
+ // because we do not want the value object to destruct the zval when
+ // it falls out of scope, we detach the zval from it, if this is a regular
+ // read operation we can do this right away
+ if (type == 0) return value.detach();
+
+ // this is a more complicated read operation, the scripts wants to get
+ // deeper access to the returned value. This, however, is only possible
+ // if the value has more than once reference (if it has a refcount of one,
+ // the value object that we have here is the only instance of the zval,
+ // and it is simply impossible to return a reference or so
+ if (value.refcount() <= 1) return value.detach();
+
+ // we're dealing with an editable zval, return a reference variable
+ return Value(value.detach(), true).detach();
+}
+
+/**
* Function that is called when a property is read
* @param object
* @param name
@@ -860,23 +869,23 @@ zval *ClassBase::readProperty(zval *object, zval *name, int type, const struct _
// the exception we know if the object was implemented by the user or not
try
{
- // retrieve value
- Value value = meta->callGet(base, name);
-
- // because we do not want the value object to destruct the zval when
- // it falls out of scope, we detach the zval from it, if this is a regular
- // read operation we can do this right away
- if (type == 0) return value.detach();
+ // convert name to a Value object
+ Value key(name);
- // this is a more complicated read operation, the scripts wants to get
- // deeper access to the returned value. This, however, is only possible
- // if the value has more than once reference (if it has a refcount of one,
- // the value object that we have here is the only instance of the zval,
- // and it is simply impossible to return a reference or so
- if (value.refcount() <= 1) return value.detach();
+ // is it a property with a callback?
+ auto iter = meta->_properties.find(key);
- // we're dealing with an editable zval, return a reference variable
- return Value(value.detach(), true).detach();
+ // was it found?
+ if (iter == meta->_properties.end())
+ {
+ // retrieve value from the __get method
+ return toZval(meta->callGet(base, key), type);
+ }
+ else
+ {
+ // get the value
+ return toZval(iter->second->get(base), type);
+ }
}
catch (const NotImplemented &exception)
{
@@ -932,8 +941,26 @@ void ClassBase::writeProperty(zval *object, zval *name, zval *value, const struc
// we know for sure that the user has not overridden the __set method
try
{
- // call the default
- meta->callSet(base, name, value);
+ // wrap the name
+ Value key(name);
+
+ // check if the property has a callback
+ auto iter = meta->_properties.find(key);
+
+ // is it set?
+ if (iter == meta->_properties.end())
+ {
+ // use the __set method
+ meta->callSet(base, key, value);
+ }
+ else
+ {
+ // check if it could be set
+ if (iter->second->set(base, value)) return;
+
+ // read-only property
+ zend_error(E_ERROR, "Unable to write to read-only property %s", (const char *)key);
+ }
}
catch (const NotImplemented &exception)
{
@@ -992,15 +1019,21 @@ int ClassBase::hasProperty(zval *object, zval *name, int has_set_exists, const s
// we need the C++ class meta-information object
ClassBase *meta = cpp_class(entry);
+
+ // convert the name to a Value object
+ Value key(name);
+
+ // check if this is a callback property
+ if (meta->_properties.find(key) != meta->_properties.end()) return true;
// call the C++ object
- if (!meta->callIsset(base, name)) return false;
+ if (!meta->callIsset(base, key)) return false;
// property exists, but what does the user want to know
if (has_set_exists == 2) return true;
// we have to retrieve the property
- Value value = meta->callGet(base, name);
+ Value value = meta->callGet(base, key);
// should we check on NULL?
switch (has_set_exists) {
@@ -1055,9 +1088,18 @@ void ClassBase::unsetProperty(zval *object, zval *member, const struct _zend_lit
// we need the C++ class meta-information object
ClassBase *meta = cpp_class(entry);
-
- // call the __unset method
- meta->callUnset(cpp_object(object), member);
+
+ // property name
+ Value name(member);
+
+ // is this a callback property?
+ auto iter = meta->_properties.find(name);
+
+ // if the property does not exist, we forward to the __unset
+ if (iter == meta->_properties.end()) meta->callUnset(cpp_object(object), member);
+
+ // callback properties cannot be unset
+ zend_error(E_ERROR, "Property %s can not be unset", (const char *)name);
}
catch (const NotImplemented &exception)
{
@@ -1080,6 +1122,53 @@ void ClassBase::unsetProperty(zval *object, zval *member, const struct _zend_lit
}
/**
+ * Function that is called when an object is about to be destructed
+ * This will call the magic __destruct method
+ */
+void ClassBase::destructObject(zend_object *object, zend_object_handle handle)
+{
+ // allocate memory for the object
+ MixedObject *obj = (MixedObject *)object;
+
+ // get meta info
+ ClassBase *meta = cpp_class(object->ce);
+
+ // prevent exceptions
+ try
+ {
+ // call the destruct function
+ if (obj->cpp) meta->callDestruct(obj->cpp);
+ }
+ catch (const NotImplemented &exception)
+ {
+ // fallback on the default destructor call
+ zend_objects_destroy_object(object, handle);
+ }
+ catch (Exception &exception)
+ {
+ // a regular Php::Exception was thrown by the extension, pass it on
+ // to PHP user space
+ exception.process();
+ }
+}
+
+/**
+ * Function that is called to clean up space that is occupied by the object
+ * @param object The object to be deallocated
+ */
+void ClassBase::freeObject(zend_object *object)
+{
+ // allocate memory for the object
+ MixedObject *obj = (MixedObject *)object;
+
+ // deallocate the cpp object
+ if (obj->cpp) delete obj->cpp;
+
+ // pass on to the default destructor
+ zend_objects_free_object_storage(object);
+}
+
+/**
* Function that is called when an instance of the class needs to be created.
* This function will create the C++ class, and the PHP object
* @param entry Pointer to the class information
@@ -1509,7 +1598,7 @@ void ClassBase::property(const char *name, std::nullptr_t value, int flags)
void ClassBase::property(const char *name, int16_t value, int flags)
{
// add property
- _members.push_back(std::make_shared<LongMember>(name, value, flags));
+ _members.push_back(std::make_shared<NumericMember>(name, value, flags));
}
/**
@@ -1521,7 +1610,7 @@ void ClassBase::property(const char *name, int16_t value, int flags)
void ClassBase::property(const char *name, int32_t value, int flags)
{
// add property
- _members.push_back(std::make_shared<LongMember>(name, value, flags));
+ _members.push_back(std::make_shared<NumericMember>(name, value, flags));
}
/**
@@ -1533,7 +1622,7 @@ void ClassBase::property(const char *name, int32_t value, int flags)
void ClassBase::property(const char *name, int64_t value, int flags)
{
// add property
- _members.push_back(std::make_shared<LongMember>(name, value, flags));
+ _members.push_back(std::make_shared<NumericMember>(name, value, flags));
}
/**
@@ -1597,6 +1686,77 @@ void ClassBase::property(const char *name, double value, int flags)
}
/**
+ * Set property with callbacks
+ * @param name Name of the property
+ * @param getter Getter method
+ */
+void ClassBase::property(const char *name, const getter_callback_0 &getter)
+{
+ // add property
+ _properties[name] = std::make_shared<Property>(getter);
+}
+
+/**
+ * Set property with callbacks
+ * @param name Name of the property
+ * @param getter Getter method
+ */
+void ClassBase::property(const char *name, const getter_callback_1 &getter)
+{
+ // add property
+ _properties[name] = std::make_shared<Property>(getter);
+}
+
+/**
+ * Set property with callbacks
+ * @param name Name of the property
+ * @param getter Getter method
+ * @param setter Setter method
+ */
+void ClassBase::property(const char *name, const getter_callback_0 &getter, const setter_callback_0 &setter)
+{
+ // add property
+ _properties[name] = std::make_shared<Property>(getter,setter);
+}
+
+/**
+ * Set property with callbacks
+ * @param name Name of the property
+ * @param getter Getter method
+ * @param setter Setter method
+ */
+void ClassBase::property(const char *name, const getter_callback_1 &getter, const setter_callback_0 &setter)
+{
+ // add property
+ _properties[name] = std::make_shared<Property>(getter,setter);
+}
+
+/**
+ * Set property with callbacks
+ * @param name Name of the property
+ * @param getter Getter method
+ * @param setter Setter method
+ */
+void ClassBase::property(const char *name, const getter_callback_0 &getter, const setter_callback_1 &setter)
+{
+ // add property
+ _properties[name] = std::make_shared<Property>(getter,setter);
+}
+
+/**
+ * Set property with callbacks
+ * @param name Name of the property
+ * @param getter Getter method
+ * @param setter Setter method
+ */
+void ClassBase::property(const char *name, const getter_callback_1 &getter, const setter_callback_1 &setter)
+{
+ // add property
+ _properties[name] = std::make_shared<Property>(getter,setter);
+}
+
+
+/**
* End namespace
*/
}
diff --git a/src/extension.cpp b/src/extension.cpp
index 3f29d9c..4c6a46f 100644
--- a/src/extension.cpp
+++ b/src/extension.cpp
@@ -117,7 +117,7 @@ int Extension::onStartup(int type, int module_number)
// is the callback registered?
if (extension->_onStartup) extension->_onStartup();
-
+
// done
return BOOL2SUCCESS(true);
}
diff --git a/src/includes.h b/src/includes.h
index 50d4144..0c00df3 100644
--- a/src/includes.h
+++ b/src/includes.h
@@ -55,6 +55,7 @@
#include "../include/byval.h"
#include "../include/byref.h"
#include "../include/global.h"
+#include "../include/super.h"
#include "../include/hashmember.h"
#include "../include/parameters.h"
#include "../include/modifiers.h"
@@ -82,13 +83,14 @@
#include "method.h"
#include "member.h"
#include "nullmember.h"
-#include "longmember.h"
+#include "numericmember.h"
#include "boolmember.h"
#include "stringmember.h"
#include "floatmember.h"
#include "arithmetic.h"
#include "origexception.h"
#include "notimplemented.h"
+#include "property.h"
#ifndef ZVAL_COPY_VALUE
#define ZVAL_COPY_VALUE(z, v) \
diff --git a/src/longmember.h b/src/numericmember.h
index 09d3d8a..99889c6 100644
--- a/src/longmember.h
+++ b/src/numericmember.h
@@ -1,7 +1,7 @@
/**
- * LongMember.h
+ * NumericMember.h
*
- * Implementation for a property that is initially set to a long value
+ * Implementation for a property that is initially set to a numeric value
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2013, 2014 Copernica BV
@@ -15,7 +15,7 @@ namespace Php {
/**
* Class definition
*/
-class LongMember : public Member
+class NumericMember : public Member
{
private:
/**
@@ -31,12 +31,12 @@ public:
* @param value
* @param flags
*/
- LongMember(const char *name, long value, int flags) : Member(name, flags), _value(value) {}
+ NumericMember(const char *name, long value, int flags) : Member(name, flags), _value(value) {}
/**
* Destructor
*/
- virtual ~LongMember() {}
+ virtual ~NumericMember() {}
/**
* Declare class constant
diff --git a/src/property.h b/src/property.h
new file mode 100644
index 0000000..f0fd46f
--- /dev/null
+++ b/src/property.h
@@ -0,0 +1,160 @@
+/**
+ * Property.h
+ *
+ * Internal class for properties that are defined with a getter and setter method
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class Property
+{
+private:
+ /**
+ * The getter
+ * @var getter_callback
+ */
+ union {
+ getter_callback_0 g0;
+ getter_callback_1 g1;
+ } _getter;
+
+ /**
+ * The setter
+ * @var setter_callback
+ */
+ union {
+ setter_callback_0 s0;
+ setter_callback_1 s1;
+ } _setter;
+
+ /**
+ * Type of getter
+ * @var char
+ */
+ int _gtype = 0;
+
+ /**
+ * Type of setter
+ * @var char
+ */
+ int _stype = 100;
+
+public:
+ /**
+ * Constructor
+ * @param getter
+ * @param setter
+ */
+ Property(const getter_callback_0 &getter) : _gtype(0)
+ {
+ _getter.g0 = getter;
+ }
+
+ /**
+ * Constructor
+ * @param getter
+ * @param setter
+ */
+ Property(const getter_callback_1 &getter) : _gtype(1)
+ {
+ _getter.g1 = getter;
+ }
+
+
+ /**
+ * Constructor
+ * @param getter
+ * @param setter
+ */
+ Property(const getter_callback_0 &getter, const setter_callback_0 &setter) : _gtype(0), _stype(0)
+ {
+ _getter.g0 = getter;
+ _setter.s0 = setter;
+ }
+
+ /**
+ * Constructor
+ * @param getter
+ * @param setter
+ */
+ Property(const getter_callback_1 &getter, const setter_callback_0 &setter) : _gtype(1), _stype(0)
+ {
+ _getter.g1 = getter;
+ _setter.s0 = setter;
+ }
+
+ /**
+ * Constructor
+ * @param getter
+ * @param setter
+ */
+ Property(const getter_callback_0 &getter, const setter_callback_1 &setter) : _gtype(0), _stype(1)
+ {
+ _getter.g0 = getter;
+ _setter.s1 = setter;
+ }
+
+ /**
+ * Constructor
+ * @param getter
+ * @param setter
+ */
+ Property(const getter_callback_1 &getter, const setter_callback_1 &setter) : _gtype(1), _stype(1)
+ {
+ _getter.g1 = getter;
+ _setter.s1 = setter;
+ }
+
+ /**
+ * Copy constructor
+ * @param that
+ */
+ Property(const Property &that) :
+ _getter(that._getter), _setter(that._setter), _gtype(that._gtype), _stype(that._stype) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~Property() {}
+
+ /**
+ * Get the property
+ * @param base Object to call it on
+ * @return Value
+ */
+ Value get(Base *base)
+ {
+ if (_gtype == 0) return (base->*_getter.g0)();
+ else return (base->*_getter.g1)();
+ }
+
+ /**
+ * Set the property
+ * @param base Object to call it on
+ * @param value New value
+ * @return bool
+ */
+ bool set(Base *base, const Value &value)
+ {
+ switch (_stype) {
+ case 0: (base->*_setter.s0)(value); return true;
+ case 1: (base->*_setter.s1)(value); return true;
+ default: return false;
+ }
+ }
+};
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/src/super.cpp b/src/super.cpp
new file mode 100644
index 0000000..6b02916
--- /dev/null
+++ b/src/super.cpp
@@ -0,0 +1,59 @@
+/**
+ * Super.cpp
+ *
+ * @copyright 2014 Copernica BV
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * A number of super-globals are always accessible
+ */
+Super POST (TRACK_VARS_POST);
+Super GET (TRACK_VARS_GET);
+Super COOKIE (TRACK_VARS_COOKIE);
+Super SERVER (TRACK_VARS_SERVER);
+Super ENV (TRACK_VARS_ENV);
+Super FILES (TRACK_VARS_FILES);
+Super REQUEST (TRACK_VARS_REQUEST);
+
+/**
+ * Array access operator
+ * This can be used for accessing associative arrays
+ * @param key
+ * @return Value
+ */
+Value Super::operator[](const std::string &key) const
+{
+ // create a value object that wraps around the actual zval
+ Value value(PG(http_globals)[_index]);
+
+ // pass on the call
+ return value[key];
+}
+
+/**
+ * Array access operator
+ * This can be used for accessing associative arrays
+ * @param key
+ * @return Value
+ */
+Value Super::operator[](const char *key) const
+{
+ // create a value object that wraps around the actual zval
+ Value value(PG(http_globals)[_index]);
+
+ // pass on the call
+ return value[key];
+}
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/src/value.cpp b/src/value.cpp
index a78c2d4..9af2d14 100644
--- a/src/value.cpp
+++ b/src/value.cpp
@@ -74,7 +74,7 @@ Value::Value(int32_t value)
}
/**
- * Constructor based on long value
+ * Constructor based on int64_t value
* @param value
*/
Value::Value(int64_t value)
@@ -170,6 +170,21 @@ Value::Value(struct _zval_struct *val, bool ref)
}
/**
+ * Wrap around a hash table
+ * @param ht Hashtable to wrap
+ */
+Value::Value(HashTable *ht)
+{
+ // construct a zval
+ MAKE_STD_ZVAL(_val);
+ Z_ARRVAL_P(_val) = ht;
+ Z_TYPE_P(_val) = IS_ARRAY;
+
+ // add a reference
+ Z_ADDREF_P(_val);
+}
+
+/**
* Wrap around an object
* @param object
*/
@@ -302,6 +317,9 @@ Value::~Value()
*/
zval *Value::detach()
{
+ // leap out if already detached
+ if (!_val) return nullptr;
+
// copy return value
zval *result = _val;
@@ -316,6 +334,50 @@ zval *Value::detach()
}
/**
+ * Attach a different zval
+ *
+ * This will first detach the current zval, and link the Value object to
+ * a different zval.
+ *
+ * @param val
+ */
+void Value::attach(struct _zval_struct *val)
+{
+ // detach first
+ if (_val) detach();
+
+ // store the zval
+ _val = val;
+
+ // add one more reference
+ Z_ADDREF_P(_val);
+}
+
+/**
+ * Attach a different zval
+ *
+ * This will first detach the current zval, and link the Value object to
+ * a new zval
+ *
+ * @param hashtable
+ */
+void Value::attach(struct _hashtable *hashtable)
+{
+ // detach first
+ if (_val) detach();
+
+ // construct a new zval
+ MAKE_STD_ZVAL(_val);
+
+ // store pointer to the hashtable, and mark the zval as an array
+ Z_ARRVAL_P(_val) = hashtable;
+ Z_TYPE_P(_val) = IS_ARRAY;
+
+ // add a reference
+ Z_ADDREF_P(_val);
+}
+
+/**
* Retrieve the refcount
* @return int
*/
@@ -1353,7 +1415,7 @@ Value Value::clone(Type type) const
* Retrieve the value as integer
* @return long
*/
-long Value::numericValue() const
+int64_t Value::numericValue() const
{
// already a long?
if (isNumeric()) return Z_LVAL_P(_val);