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),不定期为您呈现技术干货。

  • 相关阅读:
    教程:在 Visual Studio 中开始使用 Flask Web 框架
    教程:Visual Studio 中的 Django Web 框架入门
    vs2017下发现解决python运行出现‘No module named "XXX""的解决办法
    《sqlite权威指南》读书笔记 (一)
    SQL Server手工插入标识列
    hdu 3729 I'm Telling the Truth 二分图匹配
    HDU 3065 AC自动机 裸题
    hdu 3720 Arranging Your Team 枚举
    virtualbox 虚拟3台虚拟机搭建hadoop集群
    sqlserver 数据行统计,秒查语句
  • 原文地址:https://www.cnblogs.com/52fhy/p/9388764.html
Copyright © 2011-2022 走看看