diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml new file mode 100644 index 00000000..ff138d8d --- /dev/null +++ b/.github/workflows/c-cpp.yml @@ -0,0 +1,37 @@ +name: C/C++ CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-16.04, ubuntu-18.04, ubuntu-20.04 ] + php: [ 7.2, 7.3, 7.4 ] + compiler: [ g++-8, g++-9, g++-10, clang-9, clang-10 ] + exclude: + - os: ubuntu-16.04 + compiler: g++-10 + - os: ubuntu-16.04 + compiler: clang-10 + steps: + - uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + - name: Setup Compilers + run: sudo apt install $COMPILER + env: + COMPILER: ${{ matrix.compiler }} + - name: make + run: make COMPILER=$COMPILER + env: + COMPILER: ${{ matrix.compiler }} + - name: make install + run: sudo make install diff --git a/CMakeLists.txt b/CMakeLists.txt index be97069f..2e1528e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,6 +190,7 @@ SET(PHPCPP_HEADERS_INCLUDE include/argument.h include/array.h include/arrayaccess.h + include/propertyptrptr.h include/base.h include/byref.h include/byval.h diff --git a/Makefile b/Makefile index 6ecab3fd..6c45edb1 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ endif # you want to leave that flag out on production servers). # -COMPILER_FLAGS = -Wall -c -std=c++11 -fvisibility=hidden -DBUILDING_PHPCPP -Wno-write-strings -MD +COMPILER_FLAGS = -Wall -c -std=c++17 -fvisibility=hidden -DBUILDING_PHPCPP -Wno-write-strings -MD SHARED_COMPILER_FLAGS = -fpic STATIC_COMPILER_FLAGS = PHP_COMPILER_FLAGS = ${COMPILER_FLAGS} `${PHP_CONFIG} --includes` diff --git a/README.md b/README.md index 6171477f..9e3a3c23 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ PHP-CPP ======= -[![Build Status](https://travis-ci.org/CopernicaMarketingSoftware/PHP-CPP.svg?branch=master)](https://travis-ci.org/CopernicaMarketingSoftware/PHP-CPP) +[![Build Status](https://github.com/kirmorozov/PHP-CPP/workflows/C/C++%20CI/badge.svg?branch=master)](https://github.com/kirmorozov/PHP-CPP/actions) The PHP-CPP library is a C++ library for developing PHP extensions. It offers a collection of well documented and easy-to-use classes that can be used and extended to build native diff --git a/include/class.h b/include/class.h index 14cbd95b..43e497af 100644 --- a/include/class.h +++ b/include/class.h @@ -24,7 +24,7 @@ namespace Php { * Class definition of the class */ template -class PHPCPP_EXPORT Class : private ClassBase +class PHPCPP_EXPORT Class : public ClassBase { public: /** @@ -302,6 +302,8 @@ class PHPCPP_EXPORT Class : private ClassBase template Class &extends(const Class &base) { ClassBase::extends(base); return *this; } + Class &extends(const ClassBase &base) { ClassBase::extends(base); return *this; } + private: /** * Method to create the object if it is default constructable diff --git a/include/namespace.h b/include/namespace.h index 82f0be04..f1f4109f 100644 --- a/include/namespace.h +++ b/include/namespace.h @@ -156,6 +156,11 @@ class PHPCPP_EXPORT Namespace return *this; } + std::shared_ptr getLastClass() { + return _classes.back(); + } + + /** * Add an interface to the namespace by moving it * @param interface The interface properties diff --git a/include/propertyptrptr.h b/include/propertyptrptr.h new file mode 100644 index 00000000..d72fc018 --- /dev/null +++ b/include/propertyptrptr.h @@ -0,0 +1,41 @@ +/** + * PropertyPtrPtr.h + * + * "Interface" that can be "implemented" by your class. If you do, you + * create your class like this: + * + * class MyClass : public Php::Base, public Php::PropertyPtrPtr { ... } + * + * @author Kirill Morozov + * @copyright 2020 Morozov + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class PHPCPP_EXPORT PropertyPtrPtr +{ +public: + + /** + * Retrieve a member + * @param key + * @return value + */ + virtual Php::Value getPropertyPtrPtr(const Php::Value &member, int type) = 0; + + /** + * Destructor + */ + virtual ~PropertyPtrPtr() = default; +}; + +/** + * End namespace + */ +} diff --git a/include/value.h b/include/value.h index 45f7464e..532ea0a5 100644 --- a/include/value.h +++ b/include/value.h @@ -22,6 +22,7 @@ * Dependencies */ #include "zval.h" +#include /** * Set up namespace @@ -113,6 +114,17 @@ class PHPCPP_EXPORT Value : private HashParent for (auto &iter : value) setRaw(iter.first.c_str(), iter.first.size(), iter.second); } + /** + * Constructor from a unordered_map (this will create an associative array) + * @param value + */ + template + Value(const std::unordered_map &value) : Value(Type::Array) + { + // set all elements + for (auto &iter : value) setRaw(iter.first.c_str(), iter.first.size(), iter.second); + } + /** * Wrap object around zval * @param zval Zval to wrap @@ -448,6 +460,12 @@ class PHPCPP_EXPORT Value : private HashParent */ std::string stringValue() const; + /** + * Retrieve the value as a string + * @return string + */ + std::string_view stringViewValue() const; + /** * Retrieve the value as decimal * @return double @@ -557,6 +575,40 @@ class PHPCPP_EXPORT Value : private HashParent return result; } + /** + * Convert the object to a unordered_map with string index and Php::Value value + * @return std::unordered_map + */ + std::unordered_map uo_mapValue() const; + + /** + * Convert the object to a unordered_map with string index and a specific type as value + * @return std::unordered_map + */ + template + std::unordered_map uo_mapValue() const + { + // must be an array or an object, otherwise the map is empty + if (!isArray() && !isObject()) return std::unordered_map(); + + // result variable + std::unordered_map result; + + // iterate over the values + iterate([&result](const Value &key, const Value &value) { + + // first convert the value to the appropriate type (otherwise + // compiler errors occur) + T val = value; + + // add the value to the array + result[key] = val; + }); + + // done + return result; + } + /** * Define the iterator type */ @@ -692,6 +744,14 @@ class PHPCPP_EXPORT Value : private HashParent { return stringValue(); } + /** + * Cast to a string + * @return string + */ + operator std::string_view () const + { + return stringViewValue(); + } /** * Cast to byte array @@ -744,6 +804,15 @@ class PHPCPP_EXPORT Value : private HashParent return mapValue(); } + /** + * Convert the object to a map with string index and Php::Value value + * @return std::unordered_map + */ + operator std::unordered_map () const + { + return uo_mapValue(); + } + /** * Convert the object to a map with string index and Php::Value value * @return std::map @@ -754,6 +823,16 @@ class PHPCPP_EXPORT Value : private HashParent return mapValue(); } + /** + * Convert the object to a map with string index and Php::Value value + * @return std::map + */ + template + operator std::unordered_map () const + { + return uo_mapValue(); + } + /** * Get access to a certain array member * @param index diff --git a/phpcpp.h b/phpcpp.h index 1b0da150..8ab55d14 100644 --- a/phpcpp.h +++ b/phpcpp.h @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 6ff96c02..904e3192 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -360,6 +360,8 @@ zend_object_handlers *ClassImpl::objectHandlers() _handlers.has_property = &ClassImpl::hasProperty; _handlers.unset_property = &ClassImpl::unsetProperty; + _handlers.get_property_ptr_ptr = &ClassImpl::getPropertyPtrPtr; + // when a method is called (__call and __invoke) _handlers.get_method = &ClassImpl::getMethod; _handlers.get_closure = &ClassImpl::getClosure; @@ -651,6 +653,46 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv) return std_object_handlers.read_dimension(object, offset, type, rv); } } +/** + * Function that is called when the object property is used for write operations in PHP + * + * This is the object->property[x]=y operation in PHP, and mapped to the getPropertyPtrPtr() method + * of the ArrayAccess PropertyPtrPtr + * + * @param object The object on which it is called + * @param member The name of the property + * @param type The type of operation 0 - read, 1 - write + * @return zval* + */ +zval *ClassImpl::getPropertyPtrPtr(zval *object, zval *member, int type, void **cache_slot) +{ + PropertyPtrPtr *p_ptr_ptr = dynamic_cast(ObjectImpl::find(object)->object()); + if (p_ptr_ptr) + { + try + { + Php::Value res = p_ptr_ptr->getPropertyPtrPtr(member, type); + return res.detach(true); + } + catch (Throwable &throwable) + { + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); + + // unreachable + return Value(nullptr).detach(false); + } + + } + else + { + // ArrayAccess not implemented, check if there is a default handler + if (!std_object_handlers.get_property_ptr_ptr) return nullptr; + + // call default + return std_object_handlers.get_property_ptr_ptr(object, member, type, cache_slot); + } +} /** * Function that is called when the object is used as an array in PHP diff --git a/zend/classimpl.h b/zend/classimpl.h index 225c3ad7..f654bc1d 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -235,6 +235,8 @@ class ClassImpl */ static zend_object_handlers *objectHandlers(zend_class_entry *entry); + static zval *getPropertyPtrPtr(zval *object, zval *member, int type, void **cache_slot); + /** * Function to create a new iterator to iterate over an object * @param entry The class entry diff --git a/zend/includes.h b/zend/includes.h index fd914611..5a60e20b 100644 --- a/zend/includes.h +++ b/zend/includes.h @@ -90,6 +90,7 @@ #include "../include/base.h" #include "../include/countable.h" #include "../include/arrayaccess.h" +#include "../include/propertyptrptr.h" #include "../include/serializable.h" #include "../include/iterator.h" #include "../include/traversable.h" diff --git a/zend/value.cpp b/zend/value.cpp index 6ded9556..2ca1048e 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1295,6 +1295,18 @@ std::string Value::stringValue() const return ret; } +/** + * Retrieve the value as string + * @return string + */ +std::string_view Value::stringViewValue() const +{ + zend_string* s = zval_get_string(_val); + std::string_view ret(ZSTR_VAL(s), ZSTR_LEN(s)); + zend_string_release(s); + return ret; +} + /** * Access to the raw buffer * @return char * @@ -1838,7 +1850,7 @@ std::string Value::debugZval() const */ std::ostream &operator<<(std::ostream &stream, const Value &value) { - return stream << value.stringValue(); + return stream << value.stringViewValue(); } /**