zoukankan      html  css  js  c++  java
  • PHP-CPP开发扩展(五)

    PHP-CPP是一个用于开发PHP扩展的C++库。本节讲解如何在C++中实现PHP类。

    类和对象

    类和对象

    怎样在PHP-CPP里写出PHP的类呢?很简单,看下面的例子:
    main.cpp

    /**
     * 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访问的所有方法;最后再注册导出类。

    我们先测试:

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

    输出:

    result of increment() = 1
    result of increment() = 2
    result of decrement() = 1
    result of value() = 1
    result of gettime() = 1531621728
    

    访问修饰符

    我们还可以对导出的方法添加访问修饰符:

    //初始化导出类
    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;第三个参数和普通函数一样,支持设置参数类型。

    支持的访问修饰符:

    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."
    ");
    
    // remove the email address (this will cause an exception)
    unset($user->email);
    ?>
    

    (未完待续)

    想第一时间获取最新动态,欢迎关注关注飞鸿影的博客(fhyblog),不定期为您呈现技术干货。

  • 相关阅读:
    P7276-送给好友的礼物【dp】
    P4831-Scarlet loves WenHuaKe【组合数学】
    CF461D-Appleman and Complicated Task【并查集】
    P6499-[COCI2016-2017#2]Burza【状压dp】
    CF757F-Team Rocket Rises Again【最短路,DAG支配树】
    Loj#6053-简单的函数【Min25筛】
    P5325-[模板]Min_25筛
    2019.10.6 机房训练赛
    [CSP校内集训]v(记忆化搜索+map优化状压)
    [CSP校内集训]ac(树上启发式合并)
  • 原文地址:https://www.cnblogs.com/52fhy/p/9388764.html
Copyright © 2011-2022 走看看