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

(未完待续)

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

PHP-CPP开发扩展(五)的更多相关文章

  1. PHP-CPP开发扩展(一)

    PHP-CPP是一个用于开发PHP扩展的C++库.PHP-CPP提供了一系列完善的文档.易于使用和扩展的类,让你可以相对快速的创建PHP的原生扩展. 为什么使用PHP-CPP 很快 用C++编写的代码 ...

  2. php开发扩展环境的搭建(Windows)

    php开发扩展环境的搭建(Windows) 前期准备: (1) 下载php-5.3.10源码包(php-5.3.10.tar.bz2)并解压到C:\php-5.3.10:下载二进制包php-5.3.1 ...

  3. openresty 前端开发入门五之Mysql篇

    openresty 前端开发入门五之Mysql篇 这章主要演示怎么通过lua连接mysql,并根据用户输入的name从mysql获取数据,并返回给用户 操作mysql主要用到了lua-resty-my ...

  4. Java学习-039-源码 jar 包的二次开发扩展实例(源码修改)

    最近在使用已有的一些 jar 包时,发现有些 jar 包中的一些方法无法满足自己的一些需求,例如返回固定的格式,字符串处理等等,因而需要对原有 jar 文件中对应的 class 文件进行二次开发扩展, ...

  5. EditPlus 配置 Java & C/CPP 开发环境

    0.1安装EditPlus 0.2安装Java 0.3安装MinGW 0.4配置Java和MinGW环境变量 1.配置Java开发环境 1.1 Tool-->Preferences 1.2 Ja ...

  6. ASP.NET自定义控件组件开发 第五章 模板控件开发

    原文:ASP.NET自定义控件组件开发 第五章 模板控件开发 第五章 模板控件开发 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接 ...

  7. [原创].NET 分布式架构开发实战五 Framework改进篇

    原文:[原创].NET 分布式架构开发实战五 Framework改进篇 .NET 分布式架构开发实战五 Framework改进篇 前言:本来打算这篇文章来写DAL的重构的,现在计划有点改变.之前的文章 ...

  8. 【前端工具】Chrome 扩展程序的开发与发布 -- 手把手教你开发扩展程序

    关于 chrome 扩展的文章,很久之前也写过一篇.清除页面广告?身为前端,自己做一款简易的chrome扩展吧. 本篇文章重在分享一些制作扩展的过程中比较重要的知识及难点. 什么是 chrome 扩展 ...

  9. Django开发笔记五

    Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.页面继承 定义base.html: <!DOC ...

  10. ----转载----【前端工具】Chrome 扩展程序的开发与发布 -- 手把手教你开发扩展程序

    关于 chrome 扩展的文章,很久之前也写过一篇.清除页面广告?身为前端,自己做一款简易的chrome扩展吧. 本篇文章重在分享一些制作扩展的过程中比较重要的知识及难点. 什么是 chrome 扩展 ...

随机推荐

  1. Android自定义View之上拉、下拉列表 头部元素跟随 缩放、平移效果的实现

    滑动ListView列表然后 listView上边的视图 跟随着上拉或者下拉的距离 自动放大或者缩小  视图里边元素自动平移的效果 思路很简单 根据listView 的滑动距离去计算图片和文字应该平移 ...

  2. VBA汇总同目录下的所有工作簿数据到另一个工作簿,并进行统计

    Sub clData() Dim ComputerCount As Object tms = Timer p = ThisWorkbook.Path & "\" f = D ...

  3. C++字符串结束标识

    用一个字符数组可以存放一个字符串中的字符.如: char str[12]={'I',' ','a','m',' ','h','a','p','p','y'}; 用一维字符数组str来存放一个字符串″I ...

  4. centos 7安装java开发环境

    https://jingyan.baidu.com/article/29697b91660672ab20de3c15.html 自带版本是有问题的~

  5. 学生管理系统(Java Swing JDBC MySQL)

    该系统使用 Java Swing.JDBC.MySQL 开发 开发环境 Eclipse.WindowBuilder JDK版本:1.8 代码在百度网盘中(176***5088) 目录结构如下 Data ...

  6. Spring MVC 的 Converter 和 Formatter

    Converter 和 Formatter 都可用于将一种对象类型转换成另一种对象类型. Converter 是通用元件,可以将一种类型转换成另一种类型,可以在应用程序中的任意层中使用: Format ...

  7. Android SDK Manager 无法打开

    环境变量已经设置(安装JDK8后 其实无需设置,之前记得Win7有个巧妙的地方是创建了3个快捷方式到某文件夹,现在Win10上直接将java.exe等放到System32目录下). 但是依然不行,网上 ...

  8. SpringBoot集成redis,使用@Cachexxxx

    一.引入相关依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId& ...

  9. 学习STM32,你不得不了解的五大嵌入式操作系统

    学习STM32,你不得不了解的五大嵌入式操作系统                                                  原标题:学习STM32,你不得不了解的五大嵌入式操作 ...

  10. Java流程控制语句

    流程控制语句 内容: if... if...else if...else if...else switch...case for while do...while 分支结构if 接下来要学习的if条件 ...