Zend Framework——动态加载、内存数据管理与注册

Zend_Loader

Zend_Loader vs require_once()

Zend_Loader 最适合于加载的文件名是变量的情况(例如你要加载的文件的名称来自于用户的输入或者某个方法的参数)。如果你加载的文件名或类名是一个常量(即确定的文件,如/lib/test.php),则使用 Zend_Loader 并不比传统的PHP函数 require_once()有什么优势。

加载文件
静态方法 Zend_Loader::loadFile() 加载PHP文件,被加载的文件可包含任何 PHP 代码。它其实是将PHP的include()函数封装而成的一个静态方法。

Zend_Loader::loadFile($filename, $dirs=null, $once=false)

$filename参数指定需要加载的文件,$filename不需要指定任何路径,只需要文件名即可。$filename 只能由字母,数字,连接符-,下划线_及英文句号.组成(半角)。

$dirs参数则不限,可以使用中文等。$dirs参数用来指定文件所在目录,可以是一个字符串或者数组。如果为 NULL,则程序将会到系统的 include_path 下寻找文件是否存在(include_path可在php.ini中设置),如果是字符串或数组,则会到指定的目录下去找,然后才是 include_path。

Zend_Loader::loadFile() 用来加载文件,所以类名中只能包含字母数字、连接符(‘-‘)、下划线(‘_’)和句点(‘.’)。

加载类
静态方法Zend_Loader::loadClass($class, $dirs)用来加载一个 PHP 类文件,该文件名格式为“$className.php”(也就是说加载的文件名称必须和文件中的类同名)。loadClass()会检查文件中的类是否存在。

Zend_Loader::loadClass('Container_Tree',
    array(
        '/home/production/mylib',
        '/home/production/myapp'
    )
);

类名将会根据下划线(作为目录分隔线)对应到相应目录下的PHP文件,并加上’.php’,比如Container_Tree会指向Container/Tree.php。

如果$dirs是一个字符串或数组,Zend_Loader::loadClass()会根据顺序查找相应目录,并加载第一个匹配的文件。如果文件不存在,则会查找 inculde_path 指定的目录。

如果文件不存在或者文件中相应的类不存在,那么 Zend_Loader::loadClass() 就会抛出一个 Zend_Exception 异常。

判定某个文件是否可读
静态方法Zend_Loader::isReadable($pathname)判定某个文件是否存在并可读,可读则返回 TRUE ,否则返回 FALSE。

if (Zend_Loader::isReadable($filename)) {
    // do something with $filename
}

$filename参数指定了要检查的文件名,包括路径信息。这个方法是将 PHP 函数is_readable()封装而成的,is_readable() 不会自动查找 include_path 下的文件,而 Zend::isReadable() 可以。

使用 Autoloader
registerAutoload()注册 autoload callback 后,可以不需要显式加载就可以从 Zend Framework 引用那些类。当应用一个类,autoload() 方法自动地使用 Zend_Loader::loadClass() 。如果 spl_autoload 扩展不在你的 PHP 环境中,那么 registerAutoload() 方法将抛出 Zend_Exception。

Zend_Loader::registerAutoload();

指定类的自动加载
如果继承 Zend_Loader 类,可以给 registerAutoload() 一个可选的参数通过注册一个 autoload() 方法来指定类。
因为在 PHP 中静态函数引用的语义,你必须实现 loadClass() 和 autoload(),autoload() 必须调用 self::loadClass()。如果 autoload() 方法代表它的父类调用 self::loadClass(),那么它调用在父类中的方法,而不是子类中。

class My_Loader extends Zend_Loader
{
    public static function loadClass($class, $dirs = null)
    {
        parent::loadClass($class, $dirs);
    }

    public static function autoload($class)
    {
        try {
            self::loadClass($class);
            return $class;
        } catch (Exception $e) {
            return false;
        }
    }
}

Zend_Loader::registerAutoload('My_Loader');

删除 autoload callback
你可以删除 autoload callback,registerAutoload() 有个可选的第二个参数,缺省为 true 。如果这个参数是 false, 那么 autoload callback 从 SPL autoload 栈里 unregistered 掉而不是注册。

加载插件
很多 Zend Framework 组件支持插件,允许通过指定类的名称和路径动态加载函数。Zend_Loader_PluginLoader 可以来完成这个工作。

PluginLoader 的基本用法遵循 Zend Framework 的命名约定(一个文件一个类),解析路径时,使用下划线作为路径分隔符。当决定是否加载特别的插件类,允许传递可选的类名来预处理。
23.2.1. 基本用例
首先,假定下面的目录结构和类文件,并且根(toplevel)目录和库目录在 include_path 中:

application/
    modules/
        foo/
            views/
                helpers/
                    FormLabel.php
                    FormSubmit.php
        bar/
            views/
                helpers/
                    FormSubmit.php
library/
    Zend/
        View/
            Helper/
                FormLabel.php
                FormSubmit.php
                FormText.php

现在,创建一个插件加载器来使各种各样的视图助手仓库可用:

<?php
//实例化不带参数
$loader = new Zend_Loader_PluginLoader();
$loader->addPrefixPath('Zend_View_Helper', 'Zend/View/Helper/')
       ->addPrefixPath('Foo_View_Helper', 'application/modules/foo/views/helpers')
       ->addPrefixPath('Bar_View_Helper', 'application/modules/bar/views/helpers');
?>
//实例化时带参数
<?php
$loader = new Zend_Loader_PluginLoader(array(
    'Zend_View_Helper' => 'Zend/View/Helper/',
    'Foo_View_Helper'  => 'application/modules/foo/views/helpers',
    'Bar_View_Helper'  => 'application/modules/bar/views/helpers'
));
?>

接着用类名中添加路径时定义的类名后面的部分来加载一个给定的视图助手:

<?php
// load 'FormText' helper:
$formTextClass = $loader->load('FormText'); // 'Zend_View_Helper_FormText';

// load 'FormLabel' helper:
$formLabelClass = $loader->load('FormLabel'); // 'Foo_View_Helper_FormLabel' 

// load 'FormSubmit' helper:
$formSubmitClass = $loader->load('FormSubmit'); // 'Bar_View_Helper_FormSubmit' 
?>

类加载后,就可以实例化了。

[注意] 为一个类名注册多个路径
有时候,多个路径使用相同的类名,Zend_Loader_PluginLoader 实际上为每个给定的类名注册一个路径数组;最后注册的被首先检查。

共享插件
Zend_Loader_PluginLoader 在不需要使用单态实例的情况下,也可选地允许共享插件,这是通过静态注册表来完成的,在实例化时需要注册表名作为构造器的第二个参数:

<?php
// Store plugins in static registry 'foobar':
$loader = new Zend_Loader_PluginLoader(array(), 'foobar');
?>

其它使用同名注册表来实例化 PluginLoader 的组件将可以访问已经加载的路径和插件。

处理插件路径
上节的例子示例如何给插件加载器添加路径,那么如何确定已经加载的路径或删除他们呢?

如果没有提供 $prefixgetPaths($prefix = null) 以“前缀/路径”对返回所有的路径;或者如果提供了 $prefixgetPaths($prefix = null) 返回为给定的前缀注册的路径。

clearPaths($prefix = null) 将缺省地清除所有的已注册路径,或者如果提供了 $prefix 并放在堆栈里,只清除和那些和给定前缀关联的路径。

removePrefixPath($prefix, $path = null) 允许有选择地清除和给定类名相关的特定的路径。如果没有提供 $path ,所有的和类名相关的路径被清除,如果提供了 $path 并且相应的前缀存在,只有这个相关的路径被清除。

测试插件和获取类的名字
有时候你想确定在执行一个动作之前是否插件类已经加载,isLoaded() 返回插件名的状态。

getClassName()确定已加载类的完全合格的插件类名。

<?php
if ($loader->isLoaded('Adapter')) {
    $class   = $loader->getClassName('Adapter');
    $adapter = call_user_func(array($class, 'getInstance'));
}
?>

Registry

对象注册表(或称对象仓库)是一个用于在整个应用空间(application space)内存储对象和值的容器。通过把对象存储在其中,我们可以在整个项目的任何地方使用同一个对象。这种机制相当于一种全局存储。

我们可以通过Zend_Registry类的静态方法来使用对象注册表,另外,由于该类是一个数组对象,你可以使用数组形式来访问其中的类方法。

设置Registry中的值
要保存一项内容到注册表中,我们可以使用静态方法 set()。

Zend_Registry::set('index', $value);

$value可以是一个对象、数组或者标量。你可以再次使用set()来为注册表中已有的值设置一个新值。
index参数可以是一个标量,即字符串或整数,就像使用数组一样,类似于数组的索引/键名。

获取Registry中的值
可以使用 get()方法来获取Registry中某项内容的值。

$value = Zend_Registry::get('index');

getInstance()返回静态registry对象。

registry对象是可循环的(iterable)。

$registry = Zend_Registry::getInstance();

foreach ($registry as $index => $value) {
    echo "Registry index $index contains:\n";
    var_dump($value);
}

创建一个Registry对象
除了可以使用静态方法来访问Registry对象之外,你可以直接实例化它,就像使用普通的对象一样。

如果通过静态方法来访问registry对象的实例,它很方便进行静态存储,你可以在程序中的任何地方访问它。

如果使用传统的new 方法来创建registry的实例,则你可以使用数组一样的方式来初始化registry中的内容。

$registry = new Zend_Registry(array('index' => $value))

在创建这个对象实例之后,你可以使用数组对象方法来使用它,或者你可以把这个对象实例通过静态方法setInstance()设置为静态对象实例。

$registry = new Zend_Registry(array('index' => $value));

Zend_Registry::setInstance($registry);

如果静态的注册表对象已经被初始化过,则setInstance()方法会抛出一个Zend_Exception异常。

34.1.4. 像访问数组一样访问Registry对象
如果你要一次访问或设置多个值,你会发现使用数组方式是很方便的。

$registry = Zend_Registry::getInstance();

$registry['index'] = $value;

var_dump( $registry['index'] );

对象方式访问Registry
你会发现使用面向对象风格来访问registry对象也是方便的,对象中的属性名称作为索引。 要这样做,你需要使用ArrayObject::ARRAY_AS_PROPS选项来创建registry对象,并初始化静态实例。你要在静态的registry被第一次访问之前就完成这个工作。小心使用这个选项,因为某些版本的PHP在使用这个选项时会有bug。

对象形式的访问:

//在你的bootstrap代码中:
$registry = new Zend_Registry(array(), ArrayObject::ARRAY_AS_PROPS)
Zend_Registry::setInstance($registry);
$registry->tree = 'apple';
···
//在程序的任何其它地方:
$registry = Zend_Registry::getInstance();

echo $registry->tree; // echo's "apple"

$registry->index = $value;

var_dump($registry->index);

查询一个索引是否存在
可以使用静态方法isRegistered()来查询某个特定的单独索引是否已经设置了相应的值。

if (Zend_Registry::isRegistered($index)) {
    $value = Zend_Registry::get($index);
}

要确定一个数组对象中的某个特定索引的值是否设定,可以使用isset()函数,就像在普通的数组中那样使用。

$registry = Zend_Registry::getInstance();

// using array-access syntax
if (isset($registry['index'])) {
    var_dump( $registry['index'] );
}

// using object-access syntax, if enabled
if (isset($registry->index)) {
    var_dump( $registry->index );
}

扩展Registry对象
静态registry对象是类Zend_Registry的一个实例。如果你想给它增加功能,你可以继承Zend_Registry类,然后指定使用这个类来访问对象注册表。你可以使用静态方法setClassName()来指定使用这个类。注意这个类一定要是Zend_Registry的子类。

Zend_Registry::setClassName('My_Registry');

Zend_Registry::set('index', $value);

如果你在registry已经被访问过后尝试设定该类名,则registry抛出一个异常。建议你在boostrap代码(即index.php)中设置该类名。

删除静态注册表
你可以使用_unsetInstance()方法来删除registry的所有静态实例。例如你如果想在已经初始化过registry对象之后,再使用setInstance()或 setClassName(),那么你需要使用_unsetInstance()先把静态实例删除,然后才能使用那些方法。

Zend_Registry::set('index', $value);

Zend_Registry::_unsetInstance();

// 改变我们要使用的类
Zend_Registry::setClassName('My_Registry');

Zend_Registry::set('index', $value);

Zend_Memory

Zend_Memory组件用于在一个受限制的内存环境下管理数据.

内存对象(内存容器)是由内存管理器按照请求生成并在需要的时候透明地交换/加载的.

例如,如果由于受管理对象的创建或加载导致内存使用量超过你所指定的限制,一些管理对象将被复制到内存以外的缓存存储中. 用这种方法,受管理对象的总内存使用量不会超过强制的限制.

Zend_Memory 组件操作有如下的概念:

内存管理器
内存管理器按照用户应用程序的请求生成内存对象(锁定的或可移动的)并返回已交换到内存容器对象中的那些.

创建内存管理器
你可以使用Zend_Memory::factory($backendName [, $backendOprions]) 方法创建一个新的内存管理器(Zend_Memory_Manager 对象).

$memoryManager = Zend_Memory::factory('None');

第一个参数$backendName是一个字符串,他的名字是ZendCache提供的后端实现之一。

除了标准的Zend_Cache后端之外,你可以使用特殊名称’None’作为后端名称,’None’ 后端不需要任何特定的后端选项。
如你是使用’None’作为后端名称,内存管理绝不会交换数据块.这在你知道内存没有做限制, 或则对象的总体大小绝不会超过内存限制的情况下非常有用。

第二个参数$backendOptions是一个可选的后端选项参数.

内存对象
1.可移动内存
Zend_Memory_Manager::create([$data]) 方法创建可移动的对象 (对象可以被交换)。
当需要时,可移动内存对象由Zend_Memory透明的交换到缓存后端或则从缓存后端加载,使用”可移动”意思是这样的对象可以被交换并从内存中卸载,然后当应用程序代码访问该对象时再加载它:

$memObject = $memoryManager->create($data);

$data是可选的并且用于初始化对象的值.如果$data参数被省略,默认值为空字符串.

2.锁定内存
使用Zend_Memory_Manager::createLocked([$data])方法创建锁定的(对象不能被交换)对象。
锁定的内存对象总是存储在内存中.存储在锁定内存对象中的数据绝不会被交换到缓存后端中去,”锁定的”意思是这样的对象绝不会被交换和从内存中卸载,访问锁定的对象更快,因为内存管理器不需要追踪这些对象的变化:

$memObject = $memoryManager->createLocked($data);

$data是可选的并且用于初始化对象的值.如果$data参数被省略,默认值为空字符串.

锁定的对象和可移动的对象提供相同的接口(Zend_Memory_Container_Interface). 因此锁定的对象可以用于任何地方代替可移动对象.

3.内存容器 ‘值’ 属性.
使用内存容器(可移动或者锁定)’值’属性操作内存对象数据,你可以作为对象属性操作这个值属性:

$memObject = $memoryManager->create($data);

echo $memObject->value;

$memObject->value = $newValue;

$memObject->value[$index] = '_';

echo ord($memObject->value[$index1]);//返回ASCII值

$memObject->value = substr($memObject->value, $start, $length);

访问内存对象数据的一个替代的方法是使用getRef() 方法.该方法在必须用于PHP5.2之前的版本.由于性能原因,它还不建议用于其他情况

内存容器接口
内存容器提供下面的方法:

1.getRef() 方法
public function &getRef();
getRef() 方法返回对象值的引用.

如果此时对象不在内存中,可移动对象从缓存中加载. 如果对象从缓存中加载,并且受管理对象的内存使用量总和超过内存限制,将导致交换.

getRef() 方法 必须 用于访问PHP5.2版本以前的内存对象数据.

$memObject = $memoryManager->create($data);

$value = &$memObject->getRef();

for ($count = 0; $count < strlen($value); $count++) {
    $char = $value[$count];
    ...
}

2.touch() 方法
public function touch();
touch() 方法应该和getRef()一起使用.当对象值改变时它会发出信号.

$memObject = $memoryManager->create($data);
...

$value = &$memObject->getRef();

for ($count = 0; $count < strlen($value); $count++) {
    ...
    if ($condition) {
        $value[$count] = $char;
    }
    ...
}

$memObject->touch();

3.lock() 方法
public function lock()

它用于阻止一些你选择的对象被交换.正常情况,这是不需要的,因为内存管理器使用智能的算法决定候选的交换数据. 但是你明确地知道,在代码的这一部分对象不应该被交换,你可以锁定它们.

在内存中锁定的对象还保证在解锁对象前getRef()方法返回的引用是有效的:

$memObject1 = $memoryManager->create($data1);
$memObject2 = $memoryManager->create($data2);
...

$memObject1->lock();
$memObject2->lock();

$value1 = &$memObject1->getRef();
$value2 = &$memObject2->getRef();

for ($count = 0; $count < strlen($value2); $count++) {
    $value1 .= $value2[$count];
}

$memObject1->touch();
$memObject1->unlock();
$memObject2->unlock();

4.unlock() 方法
public function unlock();
当不再需要锁定是unlock() 方法解锁一个内存对象。

5.isLocked() 方法
public function isLocked();
isLocked()方法用于检测一个对象是否被锁定。如果对象被锁定她返回 true,否则如果没有被锁定返回false. 对于”锁定的”对象这总是true,对于”可移动”对象可以使true或者false.

销毁对象
当内存对象超出作用域它们被从内存管理器中自动销毁和删除:

function foo()
{
    global $memoryManager, $memList;

    ...

    $memObject1 = $memoryManager->create($data1);
    $memObject2 = $memoryManager->create($data2);
    $memObject3 = $memoryManager->create($data3);

    ...

    $memList[] = $memObject3;

    ...

    unset($memObject2); // $memObject2 is destroyed here

    ...
    // $memObject1 is destroyed here
    // but $memObject3 object is still referenced by $memList and is not destroyed
}

内存限制
内存限制是可以被加载的可移动对象使用的一个字节数量.

如果加载和创建导致内存使用量超出了限制,内存管理将交换其他对象.

你可以使用getMemoryLimit() 和 setMemoryLimit($newLimit)方法 检索和设置内存限制:

$oldLimit = $memoryManager->getMemoryLimit();  // Get memory limit in bytes
$memoryManager->setMemoryLimit($newLimit);     // Set memory limit in bytes

负值表示’没有限制’.

默认值是在php.ini配置文件中’memory_limit’选项的2/3大小, 否则如果’memory_limit’没有在php.ini中设置则为’没有限制’(-1)

最小交换量
可以被内存管理器交换的最小对象大小.内存管理器不会交换小于此设置的对象.这是为了减少交换/加载操作的数量.

你可以分别使用getMinSize()setMinSize($newSize)方法 检索和设置对象的最小大小:

$oldMinSize = $memoryManager->getMinSize();  // Get MinSize in bytes
$memoryManager->setMinSize($newSize);        // Set MinSize limit in bytes

默认的最小大小是16KB(16384字节).

require_once 'Zend/Memory.php';

$backendOptions = array(
    'cache_dir' => './tmp/' // Directory where to put the swapped memory blocks
);

$memoryManager = Zend_Memory::factory('File', $backendOptions);

$loadedFiles = array();

for ($count = 0; $count < 10000; $count++) {
    $f = fopen($fileNames[$count], 'rb');
    $data = fread($f, filesize($fileNames[$count]));
    $fclose($f);

    $loadedFiles[] = $memoryManager->create($data);
}

echo $loadedFiles[$index1]->value;

$loadedFiles[$index2]->value = $newValue;

$loadedFiles[$index3]->value[$charIndex] = '_';
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值