PHP-CPP開發擴展(五)

PHP-CPP是一個用於開發PHP擴展的C++庫。本節講解如何在C++中實現PHP類。php

類和對象

類和對象

怎樣在PHP-CPP裏寫出PHP的類呢?很簡單,看下面的例子:
main.cppios

/**
 * 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類

聲明類爲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 &params)
    {
        // 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));

繼承

implement 實現

咱們除了能夠在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 &params){
            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));

extends 繼承

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 &params){
            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 &params)
    {
        // the return value
        std::string retval = std::string("__call ") + name;

        // loop through the parameters
        for (auto &param : 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 &params)
    {
        // the return value
        std::string retval = std::string("__callStatic ") + name;

        // loop through the parameters
        for (auto &param : 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 &params)
    {
        // the return value
        std::string retval = "invoke";

        // loop through the parameters
        for (auto &param : 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),不按期爲您呈現技術乾貨。

相關文章
相關標籤/搜索