[转] 基于PHP Stream Wrapper开发有趣应用场景

简介: PHP Stream Wrapper原文:http://blog.sina.com.cn/s/blog_502c8cc40100k40e.html ,主要是基于SAE环境讲述相应的应用场景,本文经过一定的整理和少量补充。

PHP Stream Wrapper

原文:http://blog.sina.com.cn/s/blog_502c8cc40100k40e.html ,主要是基于SAE环境讲述相应的应用场景,本文经过一定的整理和少量补充。

一、PHP Wrapper是什么

自PHP 4.3开始,PHP开始允许用户通过stream_wrapper_register()自定义URL风格的协议。用户使用fopen(), copy()等文件系统函数对封装协议进行操作时,PHP会调用注册协议时所提供的类中相应的函数。

PHP手册中给了一个例子,它将VariableStream类注册为var://协议,通过这个协议,用户可以使用文件系统函数直接读写全局变量。例如,用户可以通过 “var://foo” 存取 $GLOBALS['foo']

二、为什么需要PHP Wrapper

以SAE为例。

出于性能和安全方面的考虑,SAE平台上禁用了本地文件读写和对外的数据抓取。相应的,我们提供了对应的服务来做同样的事情。

由于新服务的接口和PHP本身的接口不太一样,专门为SAE平台开发的程序当然不会存在问题,但是大量已有的程序和开源项目,就面临着繁杂的迁移工作。而使用PHP Wrapper对SAE的服务进行封装之后,用户就可以更方便地将程序迁移到SAE平台。

三、如何写PHP Wrapper

要通过PHP Wrapper封装一个协议,首先,我们需要编写 streamWrapper 类,类名可自定义,类的格式为:

streamWrapper {

    public resource $context ;

    __construct ( void )
    public bool dir_closedir ( void )
    public bool dir_opendir ( string $path , int $options )
    public string dir_readdir ( void )
    public bool dir_rewinddir ( void )
    public bool mkdir ( string $path , int $mode , int $options )
    public bool rename ( string $path_from , string $path_to )
    public bool rmdir ( string $path , int $options )
    public resource stream_cast ( int $cast_as )
    public void stream_close ( void )
    public bool stream_eof ( void )
    public bool stream_flush ( void )
    public bool stream_lock ( mode $operation )
    public bool stream_open ( string $path , string $mode , int $options , string &$opened_path )
    public string stream_read ( int $count )
    public bool stream_seek ( int $offset , int $whence = SEEK_SET )
    public bool stream_set_option ( int $option , int $arg1 , int $arg2 )
    public array stream_stat ( void )
    public int stream_tell ( void )
    public int stream_write ( string $data )
    public function stream_metadata($path, $option, $value)
    public function stream_set_option($option, $arg1, $arg2)
    public function stream_truncate($new_size)
    public bool unlink ( string $path )
    public array url_stat ( string $path , int $flags )
}

类中各方法说明:

  • streamWrapper::__construct — 构造函数,仅在stream_open前被调用
  • streamWrapper::dir_closedir — 关闭目录句柄,响应closedir()函数
  • streamWrapper::dir_opendir — 打开目录句柄,响应opendir()函数
  • streamWrapper::dir_readdir — 从目录句柄读取条目,响应readdir()函数
  • streamWrapper::dir_rewinddir — 倒回目录句柄,响应rewinddir()函数
  • streamWrapper::mkdir — 创建目录,响应mkdir()函数
  • streamWrapper::rename — 目录或文件重命名,响应rename()函数
  • streamWrapper::rmdir — 删除目录,响应rmdir()函数
  • streamWrapper::stream_cast — 检索基础资源,响应stream_select()函数
  • streamWrapper::stream_close — 关闭资源,响应fclose()函数
  • streamWrapper::stream_eof — 检查文件指针是否已经在文件末尾,响应feof()函数
  • streamWrapper::stream_flush — 清除输出缓存,响应fflush()函数
  • streamWrapper::stream_lock — 咨询文件锁定,响应flock()函数
  • streamWrapper::stream_open — 打开文件或URL为流,响应fopen()函数
  • streamWrapper::stream_read — 从流中读取内容,响应fread(), fgets()函数
  • streamWrapper::stream_seek — 在流中定位指针,响应fseek()函数
  • streamWrapper::stream_set_option — 改变流设置
  • streamWrapper::stream_stat — 检索文件资源的信息,响应fstat()函数
  • streamWrapper::stream_tell — 检索流中指针的位置,响应ftell()函数
  • streamWrapper::stream_write — 向流中写入内容,响应fwrite(), fputs()函数
  • streamWrapper::unlink — 删除文件,响应unlink()函数
  • streamWrapper::url_stat — 检索文件的信息,响应所有stat()相关的函数,例如file_exists(), is_dir(), is_file(), filesize(), fileinode()等等

详细说明请参考PHP手册:http://cn2.php.net/manual/en/class.streamwrapper.php

写好streamWrapper类之后,使用 stream_wrapper_register() 将这个类注册到Wrapper中,就可以开始使用了。

函数使用方法为:

bool stream_wrapper_register ( string $protocol , string $classname [, int $flags = 0 ] )

例如:

stream_wrapper_register("saemc", "SaeMemcacheWrapper");

由于SAE平台不支持对本地文件的写操作,因此Smarty之类的一些需要在本地写文件的开源项目就没办法直接在SAE平台上使用,而有了saemc Wrapper,用户就可以将Smarty编译的模板保存在MC中,很方便的将Smarty迁移到SAE平台上来。

在附件中我们为大家提供了SAE上Memcache Wrapper的实现代码,大家可以下载此附件进行测试。

在测试之前,需要先在本地启动一个端口为22222的Memcached服务:

memcached -m 10 -p 22222 -u nobody -l 127.0.0.1

然后使用下面代码就可以测试了:

//包含附件代码,注册saemc Wrapper
include_once('wrapper.php');
//测试 saemc Wrapper
$fp = fopen( "saemc://test.txt", "w+" ) or die("fopen faild!");
fwrite( $fp, "line1\n" ) or die("fwrite line1 faild!");
fwrite( $fp, "line2\n" ) or die("fwrite line2 faild!");
fwrite( $fp, "line3\n" ) or die("fwrite line3 faild!");
var_dump(ftell($fp));
fseek( $fp, 0 );
while ( !feof( $fp ) ) {
        $c = fgets( $fp ) or die("fgets faild!");
            var_dump($c);
}
fclose( $fp );
var_dump(file_get_contents("saemc://test.txt"));
var_dump(file_put_contents("saemc://path/test.txt", "hello world!\n"));
var_dump(file_put_contents("saemc://path/test.txt", "hello world!\n", FILE_APPEND));
var_dump(file_get_contents("saemc://path/test.txt"));
var_dump(copy("saemc://path/test.txt", "saemc://path/test_new.txt"));
var_dump(file_get_contents("saemc://path/test_new.txt"));
var_dump(unlink("saemc://path/test.txt"));
var_dump(file_get_contents("saemc://path/test.txt"));
var_dump(rename("saemc://path/test_new.txt", "saemc://path/test.txt"));
var_dump(file_get_contents("saemc://path/test.txt"));
echo "====test include====\n";
include_once("saemc://path/test.txt");

测试页面的输出结果:

int(18)
string(6) "line1
"
string(6) "line2
"
string(6) "line3
"
string(18) "line1
line2
line3
"
int(13)
int(13)
string(26) "hello world!
hello world!
"
bool(true)
string(26) "hello world!
hello world!
"
bool(true)
bool(false)
bool(true)
string(26) "hello world!
hello world!
"
====test include====
hello world!
hello world!

我们提供的 Memcache Wrapper并没有实现目录操作的一些方法和Memcache的Timeout,大家可以参考PHP手册,尝试实现目录操作,或者通过context使这个Wrapper支持Memcache的Timeout。

另外,大家可以到下面这个地址查看SAE Stdlib中sae_include的源码,在其中还有我们为Storage服务封装的saestor Wrapper和为Fetchurl服务重新封装的http Wrapper的实现:
http://stdlib.sinaapp.com/?f=sae_include.function.php

四、写Wrapper时的一些注意事项

1、 构造函数

streamWrapper 类很特别,它的构造函数并不是每次都调用的。只有在你的操作触发了stream_open相关的操作时才会调用,比如你用 file_get_contents() 了。而当你的操作触发和stream无关的函数时,比如file_exists会触发url_stat方法,这个时候构造函数是不会被调用的。

2、 读实现

Wrapper里边有Position和Seek等概念,但是很多服务其实是一次性就读取全部数据的,这个可以在stream_open的时候一次性读回,放到一个属性中,以后seek和tell的时候直接操作属性里边存放的数据就可以了。

3、 追加写实现

有很多服务是一次性写入所有数据,不支持追加写的功能(比如Memcache),这就需要我们自己在Wrapper中来实现追加写。可以将整个value一次性读取出来,将需要追加写的数据追加在读取出来的内容后面之后,再一次性写回。

但是这种追加写的实现方式性能会比较差,尤其是内容体积较大之后,一次性读取所有内容会非常消耗资源,因此在某些服务中我们不得不舍弃对追加写的支持。

4、 url_stat的实现

在streamWrapper类的实现中,url_stat的实现是个难点。必须正确的实现url_stat才能使is_writable和is_readable等查询文件元信息的函数正常工作。

而我们需要为我们的虚设备伪造这些值。以mc为例,我们给大家一些参考数据:

url_stat应该返回一个数组,分13个项,内容如下:

字段名 说明 赋值
dev 设备号 写0即可
ino inode号 写0即可
mode 文件mode 这个是文件的权限控制符号,稍后详细说明
nlink link 写0即可
uid uid Linux上用posix_get_uid可以取到,windows上为0
gid gid Linux上用posix_get_gid可以取到,windows上为0
rdev 设备类型 当为inode设备时有值
size 文件大小
atime 最后读时间 格式为unix时间戳
mtime 最后写时间
ctime 创建时间
blksize blocksize of filesystem IO 写0即可
blocks number of 512-byte blocks allocated 写0即可

其中mode的值必须写对:

如果是文件,其值为:
0100000 + 文件权限,如 0100000 + 0777

如果是目录,其值为:
040000 + 目录权限,如 0400000 + 0777

5、 关于stat的缓存

PHP会在同一个页面的执行过程中对文件的元信息进行缓存。

根据PHP文档对 clearstatcache() 这个方法的说明得知:在使用 stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), 或 fileperms() 方法查询文件信息时,PHP会将文件的stat的缓存以提高性能。 clearstatcache()方法可以用来清除这个缓存,当unlink()会自动清除stat缓存。

而实际上,PHP只有在对本地文件进行unlink, rename和rmdir操作时会清除stat缓存,而在通过其他的wrapper进行unlink, rename和rmdir操作时,并不会清除stat缓存。因此在写wrapper时我们要自己在unlink等方法中通过clearstatcache()来清除stat缓存。

附件下载地址: http://apidoc-demo.stor.sinaapp.com/wrapper.php

相关实践学习
基于小程序Serverless开发个人相册小程序
本场景基于小程序云Serverless+小程序开发者工具(IDE),快速搭建个人相册小程序
SAE的功能与使用入门
欢迎来到《SAE的功能与使用入门》,本课程是“云原生Serverless Clouder认证“系列中的第三阶段。课程将向您介绍阿里云Serverless应用引擎(SAE)服务相关的概念、特性与使用方式。通过课程将带您逐步深入探索Serverless世界,借助SAE服务,即使没有丰富的云计算和IT经验,也能够让开发人员在实际业务场景中便捷的掌握如何构建和部署应用程序,快速拥抱Serverless架构,将精力聚焦在应用代码和业务逻辑的实现上。 学习完本课程后,您将能够: 掌握Serverless应用引擎(SAE)的基本概念与核心优势 了解Serverless应用引擎(SAE)的核心功能 掌握使用Serverless应用引擎(SAE)的开发和部署流程 了解Serverless应用引擎(SAE)的适用场景和最佳实践  
目录
相关文章
|
1天前
|
安全 编译器 PHP
PHP 8.1版本发布:引领Web开发新潮流
PHP编程语言一直是Web开发的主力军,而最新发布的PHP 8.1版本则为开发者们带来了更多创新和便利。本文将介绍PHP 8.1版本的主要特性,包括更快的性能、新的语言功能和增强的安全性,以及如何利用这些功能来提升Web应用程序的质量和效率。
|
4天前
|
PHP
web简易开发——通过php与HTML+css+mysql实现用户的登录,注册
web简易开发——通过php与HTML+css+mysql实现用户的登录,注册
|
8月前
|
IDE NoSQL 关系型数据库
开发php,需要安装哪些工具
开发php,需要安装哪些工具
|
9月前
|
移动开发 JavaScript 前端开发
数据可视化大屏百度地图手机端标注开发实战案例解析(jsAPI接口、标注分类图片、文本标签、分类筛选、自适应高度信息弹窗、PHP后端API)
数据可视化大屏百度地图手机端标注开发实战案例解析(jsAPI接口、标注分类图片、文本标签、分类筛选、自适应高度信息弹窗、PHP后端API)
147 0
|
9月前
|
关系型数据库 MySQL API
漏刻有时API接口实战开发系列(12):php-mysql语句开发
漏刻有时API接口实战开发系列(12):php-mysql语句开发
41 0
|
8月前
|
移动开发 前端开发 搜索推荐
基于PHP开发的拍卖直播系统源码分享
东莞梦幻网络科技最新推出的拍卖直播系统源码,安卓端使用Java语言开发,苹果端则采用Objective-C开发,前端H5则采用了Vue.js开发,后台管理系统基于PHP的ThinkPHP框架开发。
|
8月前
|
NoSQL 测试技术 PHP
CRM系统源码PHP开发
在竞争激烈的市场中,公司需要与客户保持积极的关系。一个好的CRM(客户关系管理)系统可以帮助公司满足客户,研究将探索建立有效的CRM以满足客户的不同方法和技术。目的是检查客户关系管理 (CRM) 在保留和满足客户方面的有效性。研究表明,客户关系管理对客户满意度有显着影响,且两个变量均呈正相关关系。公司使其 CRM 变得强大和可靠,客户将更加满意并留住公司。   
|
9月前
|
IDE 关系型数据库 MySQL
window下面安装phpstudy进行PHP开发
php的集成开发环境比较多,有wamp server,xmapp,phpstudy等。经过这些年的体验,发现相对来说个人还是比较喜欢phpstudy一点。主要原因有一下几点。维护的比较频繁,一直有更新。可以灵活切换多个php版本,这样针对于不同的php项目能够很好的适应。一件集成,傻瓜安装,让开发人员关注于开发本身。
|
9月前
|
SQL 安全 关系型数据库
php开发实战分析(8):mysql类库的操作高级使用API开发(字段操作、权限分配、日期格式、跨表查询,数据分表)
php开发实战分析(8):mysql类库的操作高级使用API开发(字段操作、权限分配、日期格式、跨表查询,数据分表)
299 0