PHP-CPP是一個用於開發PHP擴展的C++庫。本節講解如何在C++中實現PHP類。php
怎樣在PHP-CPP裏寫出PHP的類呢?很簡單,看下面的例子:
main.cpp
ios
/** * User: 公衆號: 飛鴻影的博客(fhyblog) * Date: 2018/7 */ #include <time.h> #include <phpcpp.h> /** * Counter class that can be used for counting */ class Counter : public Php::Base { private: /** * The initial value * @var int */ int _value = 0; public: /** * C++ constructor and destructor */ Counter() = default; virtual ~Counter() = default; /** * Update methods to increment or decrement the counter * Both methods return the NEW value of the counter * @return int */ Php::Value increment() { return ++_value; } Php::Value decrement() { return --_value; } /** * Method to retrieve the current counter value * @return int */ Php::Value value() const { return _value; } //類的靜態成員函數 static Php::Value gettime() {return time(NULL);} }; /** * 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() { // 必須是static類型,由於擴展對象須要在PHP進程內常駐內存 static Php::Extension extension("helloworld", "1.0.0"); //初始化導出類 Php::Class<Counter> counter("Counter"); //註冊導出類的可訪問普通函數 counter.method<&Counter::increment> ("increment"); counter.method<&Counter::decrement> ("decrement"); counter.method<&Counter::value> ("value"); //註冊導出類的可訪問靜態函數 counter.method<&Counter::gettime>("gettime"); //註冊導出類,使用右值引用方式,優化資源使用 extension.add(std::move(counter)); // 返回擴展對象指針 return extension; } }
首先,C++類必須繼承自Php::Base
;其次,當咱們將類添加到擴展對象時,還必須指定要從PHP訪問的全部方法;最後再註冊導出類。c++
咱們先測試:編程
/** * User: 公衆號: 飛鴻影的博客(fhyblog) * Date: 2018/7 */ $counter = new Counter; echo 'result of increment() = '. $counter->increment() . PHP_EOL; echo 'result of increment() = '. $counter->increment() . PHP_EOL; echo 'result of decrement() = '. $counter->decrement() . PHP_EOL; echo 'result of value() = '. $counter->value() . PHP_EOL; echo 'result of gettime() = '. Counter::gettime() . PHP_EOL;
輸出:app
result of increment() = 1 result of increment() = 2 result of decrement() = 1 result of value() = 1 result of gettime() = 1531621728
咱們還能夠對導出的方法添加訪問修飾符:ide
//初始化導出類 Php::Class<Counter> counter("Counter"); //註冊導出類的可訪問普通函數 counter.method<&Counter::increment> ("increment", Php::Private, { Php::ByVal("a", Php::Type::Numeric) }); counter.method<&Counter::decrement> ("decrement", Php::Protected, { Php::ByVal("a", Php::Type::Numeric) }); counter.method<&Counter::value> ("value");
Php::Class::method
第二個參數支持設置訪問修飾符,默認是public;第三個參數和普通函數同樣,支持設置參數類型。函數
支持的訪問修飾符:oop
extern PHPCPP_EXPORT const int Static; extern PHPCPP_EXPORT const int Abstract; extern PHPCPP_EXPORT const int Final; extern PHPCPP_EXPORT const int Public; extern PHPCPP_EXPORT const int Protected; extern PHPCPP_EXPORT const int Private; extern PHPCPP_EXPORT const int Const;
有一點須要注意:C++裏要導出的方法,必須全是Public的, 即便咱們在PHP中將它們標記爲私有或受保護。由於咱們寫的方法由PHP-CPP庫調用,若是將它們設爲私有,它們將對庫不可見。測試
聲明類爲Final很簡單,只須要在初始化導出類的時候聲明一下便可:優化
Php::Class<Counter> counter("Counter", Php::Final);
那麼怎麼聲明一個抽象類呢?上面的例子裏Php::Class::method
都傳入了真正的C ++方法的地址,可是抽象方法一般沒有實現,那麼咱們須要怎麼提供指向方法的指針?幸運的是,在PHP-CPP裏註冊抽象方法不用提供指向C ++方法的指針。
示例:
抽象類原申明:
/** * User: 公衆號: 飛鴻影的博客(fhyblog) * Date: 2018/7 */ #include <phpcpp.h> //類聲明 class MyAbstract : public Php::Base{}; extern "C" { PHPCPP_EXPORT void *get_module() { // 必須是static類型,由於擴展對象須要在PHP進程內常駐內存 static Php::Extension extension("helloworld", "1.0.0"); //初始化導出類 Php::Class<MyAbstract> my_abstract("MyAbstract", Php::Abstract); //註冊抽象方法:若是不給出C++方法的地址,該方法自動變成抽象方法 my_abstract.method("myAbstractMethod", { Php::ByVal("a", Php::Type::String, true) }); extension.add(std::move(my_abstract)); // 返回擴展對象指針 return extension; } }
咱們在test.php嘗試去實例化MyAbstract
類,提示:
PHP Fatal error: Uncaught Error: Cannot instantiate abstract class MyAbstract
注:官方示例裏初始化導出類裏沒有加
Php::Abstract
,測試的時候發現仍是能夠實例化的,只是調用抽象方法才報錯。
在C++代碼裏,PHP的構造函數和析構函數本質上是普通方法。明白了這點,就不難實現了。
示例:
/** * User: 公衆號: 飛鴻影的博客(fhyblog) * Date: 2018/7 */ #include <phpcpp.h> /** * Simple counter class */ class Counter : public Php::Base { private: /** * Internal value * @var int */ int _value = 0; public: /** * c++ constructor */ Counter() = default; /** * c++ destructor */ virtual ~Counter() = default; /** * php "constructor" * @param params */ void __construct(Php::Parameters ¶ms) { // copy first parameter (if available) if (!params.empty()) _value = params[0]; } /** * functions to increment and decrement */ Php::Value increment() { return ++_value; } Php::Value decrement() { return --_value; } Php::Value value() const { return _value; } }; /** * 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() { 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"); counter.method<&Counter::__construct>("__construct"); counter.method<&Counter::increment>("increment"); counter.method<&Counter::decrement>("decrement"); counter.method<&Counter::value>("value"); // add the class to the extension myExtension.add(std::move(counter)); // return the extension return myExtension; } }
若是須要構造函數爲私有的,只須要在註冊的時候加個flag:
counter.method<&Counter::__construct>("__construct", Php::Private);
若是要禁止被clone,能夠:
// alternative way to make an object unclonable counter.method("__clone", Php::Private);
接口(Interface)因爲不須要具體方法的實現,咱們能夠經過與定義類的方式相似的方式來實現。惟一的區別是咱們不使用Php::Class<YourClass>
,而是一個Php::Interface
實例。
//初始化 Php::Interface interface("MyInterface"); //添加成員方法 interface.method("myMethod", { Php::ByVal("value", Php::Type::String, true) }); //註冊到擴展 extension.add(std::move(interface));
咱們除了能夠在PHP代碼去實現接口或者繼承類,也能夠在C++裏實現。該Php::Class<YourClass>
對象有extends()
和implements()
,可用於指定基類和實現的接口。咱們須要傳入以前配置的類或接口。咱們來看一個例子。
/** * User: 公衆號: 飛鴻影的博客(fhyblog) * Date: 2018/7 */ #include <phpcpp.h> #include <iostream> class MyClass : public Php::Base { public: Php::Value myMethod(Php::Parameters ¶ms){ Php::out << "MyClass" << std::endl; return params; } }; extern "C" { PHPCPP_EXPORT void *get_module() { static Php::Extension extension("helloworld", "1.0.0"); //定義接口 Php::Interface interface("MyInterface"); interface.method("myMethod", { Php::ByVal("value", Php::Type::String, true) }); extension.add(std::move(interface)); // 註冊一個自定義類 Php::Class<MyClass> myClass("MyClass"); // 實現接口定義 myClass.implements(interface); myClass.method<&MyClass::myMethod>("myMethod", { Php::ByVal("value", Php::Type::String, true) }); extension.add(std::move(myClass)); // 返回擴展對象指針 return extension; } }
測試:
$obj = new MyClass(); var_dump($obj->myMethod(11));
PHP的繼承與C++的繼承沒有直接關係,必須顯示使用Php::Class::extends()
進行繼承。
仍是接着上面的例子說明。
/** * User: 公衆號: 飛鴻影的博客(fhyblog) * Date: 2018/7 */ #include <phpcpp.h> #include <iostream> class MyClass : public Php::Base { public: Php::Value myMethod(Php::Parameters ¶ms){ Php::out << "MyClass" << std::endl; return params; } }; class MySubClass : public Php::Base{ }; extern "C" { PHPCPP_EXPORT void *get_module() { static Php::Extension extension("helloworld", "1.0.0"); //定義接口 Php::Interface interface("MyInterface"); interface.method("myMethod", { Php::ByVal("value", Php::Type::String, true) }); // 註冊一個自定義類 Php::Class<MyClass> myClass("MyClass"); // 實現接口定義 myClass.implements(interface); myClass.method<&MyClass::myMethod>("myMethod", { Php::ByVal("value", Php::Type::String, true) }); Php::Class<MySubClass> mySubClass("MySubClass"); mySubClass.extends(myClass); extension.add(std::move(interface)); extension.add(std::move(mySubClass)); extension.add(std::move(myClass)); // 返回擴展對象指針 return extension; } }
注:註冊類(
extension.add
)須要放到extends方法的後面,也就是不能先註冊父類再使用extends,不然沒法繼承。建議實際編程的時候註冊統一放到最後面。
在PHP-CPP裏,僅__construct()
須要顯示的在get_module()
裏註冊,其餘的魔術方法像__get()
、__set()
、__call()
、__toString()
等都不須要註冊。
#include <phpcpp.h> /** * A sample class, that has some pseudo properties that map to native types */ class User : public Php::Base { private: /** * Name of the user * @var std::string */ std::string _name; /** * Email address of the user * @var std::string */ std::string _email; public: /** * C++ constructor and C++ destructpr */ User() = default; virtual ~User() = default; /** * Get access to a property * @param name Name of the property * @return Value Property value */ Php::Value __get(const Php::Value &name) { // check if the property name is supported if (name == "name") return _name; if (name == "email") return _email; // property not supported, fall back on default return Php::Base::__get(name); } /** * Overwrite a property * @param name Name of the property * @param value New property value */ void __set(const Php::Value &name, const Php::Value &value) { // check the property name if (name == "name") { // store member _name = value.stringValue(); } // we check emails for validity else if (name == "email") { // store the email in a string std::string email = value; // must have a '@' character in it if (email.find('@') == std::string::npos) { // email address is invalid, throw exception throw Php::Exception("Invalid email address"); } // store the member _email = email; } // other properties fall back to default else { // call default Php::Base::__set(name, value); } } /** * Check if a property is set * @param name Name of the property * @return bool */ bool __isset(const Php::Value &name) { // true for name and email address if (name == "name" || name == "email") return true; // fallback to default return Php::Base::__isset(name); } /** * Remove a property * @param name Name of the property to remove */ void __unset(const Php::Value &name) { // name and email can not be unset if (name == "name" || name == "email") { // warn the user with an exception that this is impossible throw Php::Exception("Name and email address can not be removed"); } // fallback to default Php::Base::__unset(name); } /** * Overriden __call() method to accept all method calls * @param name Name of the method that is called * @param params Parameters that were passed to the method * @return Value The return value */ Php::Value __call(const char *name, Php::Parameters ¶ms) { // the return value std::string retval = std::string("__call ") + name; // loop through the parameters for (auto ¶m : params) { // append parameter string value to return value retval += " " + param.stringValue(); } // done return retval; } /** * Overriden __callStatic() method to accept all static method calls * @param name Name of the method that is called * @param params Parameters that were passed to the method * @return Value The return value */ static Php::Value __callStatic(const char *name, Php::Parameters ¶ms) { // the return value std::string retval = std::string("__callStatic ") + name; // loop through the parameters for (auto ¶m : params) { // append parameter string value to return value retval += " " + param.stringValue(); } // done return retval; } /** * Overridden __invoke() method so that objects can be called directly * @param params Parameters that were passed to the method * @return Value The return value */ Php::Value __invoke(Php::Parameters ¶ms) { // the return value std::string retval = "invoke"; // loop through the parameters for (auto ¶m : params) { // append parameter string value to return value retval += " " + param.stringValue(); } // done return retval; } /** * Cast to a string * @return Value */ Php::Value __toString() { return "abcd"; } }; /** * 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<User> user("User"); // add the class to the extension myExtension.add(std::move(user)); // return the extension return myExtension; } }
測試:
<?php // initialize user and set its name and email address $user = new User(); $user->name = "John Doe"; $user->email = "john.doe@example.com"; // show the email address echo($user->email."\n"); // remove the email address (this will cause an exception) unset($user->email); ?>
(未完待續)
想第一時間獲取最新動態,歡迎關注關注飛鴻影的博客(fhyblog)
,不按期爲您呈現技術乾貨。