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. Redhat学习(此为草稿,正式笔记之后存放)

    系统组成 版本XX.XX.XX XX主版本,XX副版本(奇数为测试版,偶数为稳定版),修订版本号 , 表示修改的次数(debug) linux主流分支 硬盘分区 /dev/ 硬件设备所在分区 hd 表 ...

  2. python脚本批量复制文件

    1.拷贝一个目录下的所有文件及文件夹到另一个目录下(递归拷贝) # cat /home/test.py #!/usr/bin/python  import os  import shutil def ...

  3. Django 跨域请求 解决 axios 未完待续

    import django import os # os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled5. ...

  4. 文字创作类App分享-简书

    今天我用Mockplus做了一套简书App的原型,这是一款文字创作类的App,用户通过写文.点赞等互动行为,提高自己在社区的影响力,打造个人品牌.我运用了Mockplus基础组件.交互组件.移动组件等 ...

  5. zeromq学习记录(一)最初的简单示例使用ZMQ_REQ ZMQ_REP

    阅读zeromq guide的一些学习记录 zeromq官方例子 在VC下运行会有些跨平台的错误 我这里有做修改 稍后会发布出来 相关的代码与库  http://download.zeromq.org ...

  6. linux debian 9 / centos 7配置postgresSQL数据库

    #读者注意:本文可以选择不看解释,直接执行每段的0中的代码 (〇):一些概念(可以跳过直接使用(一)0的代码) 1. 客户端:psql.postgreSQL的命令行客户端程序,在终端输入psql进入p ...

  7. eclipse启动时出现无法创建java虚拟机

    最 近一直在用eclipse开发android程序,今天不知怎么的启动eclipse时就会出现Failed to create java virtual machine,无法打开eclipse程序,折 ...

  8. Alpha 冲刺 (7/10)

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 学习MSI.CUDA 试运行软件并调试 ...

  9. sas通过IMPORT过程读取外部文件数据

    SAS通过IMPORT过程读取外部文件数据 使用IMPORT过程导入带分隔符的文件外,Microsoft Access数据库文件.Miscrosft Excel工作簿. dBase文件.JMP文件.S ...

  10. fly插件飞向购物车

    首先载入jQuery库文件和jquery.fly.min.js插件. 插件官方: https://github.com/amibug/fly, 官方例子: http://codepen.io/hzxs ...