diff --git a/application/Bootstrap.php b/application/Bootstrap.php
new file mode 100644
index 0000000..0f2b86c
--- /dev/null
+++ b/application/Bootstrap.php
@@ -0,0 +1,41 @@
+bootstrap('view');
+ $view = $this->getResource('view');
+ $view->setEncoding('UTF-8');
+ $view->doctype('XHTML1_STRICT');
+ $view->headMeta()
+ ->appendHttpEquiv('Content-Type', 'text/html; charset=UTF-8')
+ ->appendHttpEquiv('Content-Language', 'fr-FR');
+ $view->headTitle()->setSeparator(' - ');
+ $view->headTitle('Filer');
+ }
+
+ //Initialisation global des paramètres de log
+ protected function _initLogging()
+ {
+ require_once 'Zend/Log.php';
+ require_once 'Zend/Mail.php';
+ $WsLogger = new Zend_Log();
+ $WsFileWriter = new Zend_Log_Writer_Stream(LOG_PATH.'/wsentreprise.log');
+ $WsFileWriter->addFilter(Zend_Log::INFO);
+ $WsLogger->addWriter($WsFileWriter);
+ if (APPLICATION_ENV == 'production')
+ {
+ $WsMail = new Zend_Mail();
+ $WsMail->setFrom('production@scores-decisions.com')
+ ->addTo('mricois@scores-decisions.com');
+ $WsMailWriter = new Zend_Log_Writer_Mail($WsMail);
+ $WsMailWriter->setSubjectPrependText('ERREUR');
+ $WsMailWriter->addFilter(Zend_Log::ERR);
+ $WsLogger->addWriter($WsMailWriter);
+ }
+ Zend_Registry::set('WsLogger', $WsLogger);
+ }
+
+}
+
diff --git a/batch/cron.php b/batch/cron.php
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/batch/cron.php
@@ -0,0 +1 @@
+ array(
+ 'allRoles' => array(
+ 'allPrivileges' => array(
+ 'type' => self::TYPE_DENY,
+ 'assert' => null
+ ),
+ 'byPrivilegeId' => array()
+ ),
+ 'byRoleId' => array()
+ ),
+ 'byResourceId' => array()
+ );
+
+ /**
+ * Adds a Role having an identifier unique to the registry
+ *
+ * The $parents parameter may be a reference to, or the string identifier for,
+ * a Role existing in the registry, or $parents may be passed as an array of
+ * these - mixing string identifiers and objects is ok - to indicate the Roles
+ * from which the newly added Role will directly inherit.
+ *
+ * In order to resolve potential ambiguities with conflicting rules inherited
+ * from different parents, the most recently added parent takes precedence over
+ * parents that were previously added. In other words, the first parent added
+ * will have the least priority, and the last parent added will have the
+ * highest priority.
+ *
+ * @param Zend_Acl_Role_Interface $role
+ * @param Zend_Acl_Role_Interface|string|array $parents
+ * @uses Zend_Acl_Role_Registry::add()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function addRole($role, $parents = null)
+ {
+ if (is_string($role)) {
+ $role = new Zend_Acl_Role($role);
+ }
+
+ if (!$role instanceof Zend_Acl_Role_Interface) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception('addRole() expects $role to be of type Zend_Acl_Role_Interface');
+ }
+
+
+ $this->_getRoleRegistry()->add($role, $parents);
+
+ return $this;
+ }
+
+ /**
+ * Returns the identified Role
+ *
+ * The $role parameter can either be a Role or Role identifier.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @uses Zend_Acl_Role_Registry::get()
+ * @return Zend_Acl_Role_Interface
+ */
+ public function getRole($role)
+ {
+ return $this->_getRoleRegistry()->get($role);
+ }
+
+ /**
+ * Returns true if and only if the Role exists in the registry
+ *
+ * The $role parameter can either be a Role or a Role identifier.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @uses Zend_Acl_Role_Registry::has()
+ * @return boolean
+ */
+ public function hasRole($role)
+ {
+ return $this->_getRoleRegistry()->has($role);
+ }
+
+ /**
+ * Returns true if and only if $role inherits from $inherit
+ *
+ * Both parameters may be either a Role or a Role identifier. If
+ * $onlyParents is true, then $role must inherit directly from
+ * $inherit in order to return true. By default, this method looks
+ * through the entire inheritance DAG to determine whether $role
+ * inherits from $inherit through its ancestor Roles.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @param Zend_Acl_Role_Interface|string $inherit
+ * @param boolean $onlyParents
+ * @uses Zend_Acl_Role_Registry::inherits()
+ * @return boolean
+ */
+ public function inheritsRole($role, $inherit, $onlyParents = false)
+ {
+ return $this->_getRoleRegistry()->inherits($role, $inherit, $onlyParents);
+ }
+
+ /**
+ * Removes the Role from the registry
+ *
+ * The $role parameter can either be a Role or a Role identifier.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @uses Zend_Acl_Role_Registry::remove()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function removeRole($role)
+ {
+ $this->_getRoleRegistry()->remove($role);
+
+ if ($role instanceof Zend_Acl_Role_Interface) {
+ $roleId = $role->getRoleId();
+ } else {
+ $roleId = $role;
+ }
+
+ foreach ($this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) {
+ if ($roleId === $roleIdCurrent) {
+ unset($this->_rules['allResources']['byRoleId'][$roleIdCurrent]);
+ }
+ }
+ foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) {
+ if (array_key_exists('byRoleId', $visitor)) {
+ foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) {
+ if ($roleId === $roleIdCurrent) {
+ unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]);
+ }
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Removes all Roles from the registry
+ *
+ * @uses Zend_Acl_Role_Registry::removeAll()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function removeRoleAll()
+ {
+ $this->_getRoleRegistry()->removeAll();
+
+ foreach ($this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) {
+ unset($this->_rules['allResources']['byRoleId'][$roleIdCurrent]);
+ }
+ foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) {
+ foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) {
+ unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Adds a Resource having an identifier unique to the ACL
+ *
+ * The $parent parameter may be a reference to, or the string identifier for,
+ * the existing Resource from which the newly added Resource will inherit.
+ *
+ * @param Zend_Acl_Resource_Interface|string $resource
+ * @param Zend_Acl_Resource_Interface|string $parent
+ * @throws Zend_Acl_Exception
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function addResource($resource, $parent = null)
+ {
+ if (is_string($resource)) {
+ $resource = new Zend_Acl_Resource($resource);
+ }
+
+ if (!$resource instanceof Zend_Acl_Resource_Interface) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception('addResource() expects $resource to be of type Zend_Acl_Resource_Interface');
+ }
+
+ $resourceId = $resource->getResourceId();
+
+ if ($this->has($resourceId)) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception("Resource id '$resourceId' already exists in the ACL");
+ }
+
+ $resourceParent = null;
+
+ if (null !== $parent) {
+ try {
+ if ($parent instanceof Zend_Acl_Resource_Interface) {
+ $resourceParentId = $parent->getResourceId();
+ } else {
+ $resourceParentId = $parent;
+ }
+ $resourceParent = $this->get($resourceParentId);
+ } catch (Zend_Acl_Exception $e) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception("Parent Resource id '$resourceParentId' does not exist", 0, $e);
+ }
+ $this->_resources[$resourceParentId]['children'][$resourceId] = $resource;
+ }
+
+ $this->_resources[$resourceId] = array(
+ 'instance' => $resource,
+ 'parent' => $resourceParent,
+ 'children' => array()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Adds a Resource having an identifier unique to the ACL
+ *
+ * The $parent parameter may be a reference to, or the string identifier for,
+ * the existing Resource from which the newly added Resource will inherit.
+ *
+ * @deprecated in version 1.9.1 and will be available till 2.0. New code
+ * should use addResource() instead.
+ *
+ * @param Zend_Acl_Resource_Interface $resource
+ * @param Zend_Acl_Resource_Interface|string $parent
+ * @throws Zend_Acl_Exception
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function add(Zend_Acl_Resource_Interface $resource, $parent = null)
+ {
+ return $this->addResource($resource, $parent);
+ }
+
+ /**
+ * Returns the identified Resource
+ *
+ * The $resource parameter can either be a Resource or a Resource identifier.
+ *
+ * @param Zend_Acl_Resource_Interface|string $resource
+ * @throws Zend_Acl_Exception
+ * @return Zend_Acl_Resource_Interface
+ */
+ public function get($resource)
+ {
+ if ($resource instanceof Zend_Acl_Resource_Interface) {
+ $resourceId = $resource->getResourceId();
+ } else {
+ $resourceId = (string) $resource;
+ }
+
+ if (!$this->has($resource)) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception("Resource '$resourceId' not found");
+ }
+
+ return $this->_resources[$resourceId]['instance'];
+ }
+
+ /**
+ * Returns true if and only if the Resource exists in the ACL
+ *
+ * The $resource parameter can either be a Resource or a Resource identifier.
+ *
+ * @param Zend_Acl_Resource_Interface|string $resource
+ * @return boolean
+ */
+ public function has($resource)
+ {
+ if ($resource instanceof Zend_Acl_Resource_Interface) {
+ $resourceId = $resource->getResourceId();
+ } else {
+ $resourceId = (string) $resource;
+ }
+
+ return isset($this->_resources[$resourceId]);
+ }
+
+ /**
+ * Returns true if and only if $resource inherits from $inherit
+ *
+ * Both parameters may be either a Resource or a Resource identifier. If
+ * $onlyParent is true, then $resource must inherit directly from
+ * $inherit in order to return true. By default, this method looks
+ * through the entire inheritance tree to determine whether $resource
+ * inherits from $inherit through its ancestor Resources.
+ *
+ * @param Zend_Acl_Resource_Interface|string $resource
+ * @param Zend_Acl_Resource_Interface|string $inherit
+ * @param boolean $onlyParent
+ * @throws Zend_Acl_Resource_Registry_Exception
+ * @return boolean
+ */
+ public function inherits($resource, $inherit, $onlyParent = false)
+ {
+ try {
+ $resourceId = $this->get($resource)->getResourceId();
+ $inheritId = $this->get($inherit)->getResourceId();
+ } catch (Zend_Acl_Exception $e) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+
+ if (null !== $this->_resources[$resourceId]['parent']) {
+ $parentId = $this->_resources[$resourceId]['parent']->getResourceId();
+ if ($inheritId === $parentId) {
+ return true;
+ } else if ($onlyParent) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ while (null !== $this->_resources[$parentId]['parent']) {
+ $parentId = $this->_resources[$parentId]['parent']->getResourceId();
+ if ($inheritId === $parentId) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes a Resource and all of its children
+ *
+ * The $resource parameter can either be a Resource or a Resource identifier.
+ *
+ * @param Zend_Acl_Resource_Interface|string $resource
+ * @throws Zend_Acl_Exception
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function remove($resource)
+ {
+ try {
+ $resourceId = $this->get($resource)->getResourceId();
+ } catch (Zend_Acl_Exception $e) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+
+ $resourcesRemoved = array($resourceId);
+ if (null !== ($resourceParent = $this->_resources[$resourceId]['parent'])) {
+ unset($this->_resources[$resourceParent->getResourceId()]['children'][$resourceId]);
+ }
+ foreach ($this->_resources[$resourceId]['children'] as $childId => $child) {
+ $this->remove($childId);
+ $resourcesRemoved[] = $childId;
+ }
+
+ foreach ($resourcesRemoved as $resourceIdRemoved) {
+ foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $rules) {
+ if ($resourceIdRemoved === $resourceIdCurrent) {
+ unset($this->_rules['byResourceId'][$resourceIdCurrent]);
+ }
+ }
+ }
+
+ unset($this->_resources[$resourceId]);
+
+ return $this;
+ }
+
+ /**
+ * Removes all Resources
+ *
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function removeAll()
+ {
+ foreach ($this->_resources as $resourceId => $resource) {
+ foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $rules) {
+ if ($resourceId === $resourceIdCurrent) {
+ unset($this->_rules['byResourceId'][$resourceIdCurrent]);
+ }
+ }
+ }
+
+ $this->_resources = array();
+
+ return $this;
+ }
+
+ /**
+ * Adds an "allow" rule to the ACL
+ *
+ * @param Zend_Acl_Role_Interface|string|array $roles
+ * @param Zend_Acl_Resource_Interface|string|array $resources
+ * @param string|array $privileges
+ * @param Zend_Acl_Assert_Interface $assert
+ * @uses Zend_Acl::setRule()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function allow($roles = null, $resources = null, $privileges = null, Zend_Acl_Assert_Interface $assert = null)
+ {
+ return $this->setRule(self::OP_ADD, self::TYPE_ALLOW, $roles, $resources, $privileges, $assert);
+ }
+
+ /**
+ * Adds a "deny" rule to the ACL
+ *
+ * @param Zend_Acl_Role_Interface|string|array $roles
+ * @param Zend_Acl_Resource_Interface|string|array $resources
+ * @param string|array $privileges
+ * @param Zend_Acl_Assert_Interface $assert
+ * @uses Zend_Acl::setRule()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function deny($roles = null, $resources = null, $privileges = null, Zend_Acl_Assert_Interface $assert = null)
+ {
+ return $this->setRule(self::OP_ADD, self::TYPE_DENY, $roles, $resources, $privileges, $assert);
+ }
+
+ /**
+ * Removes "allow" permissions from the ACL
+ *
+ * @param Zend_Acl_Role_Interface|string|array $roles
+ * @param Zend_Acl_Resource_Interface|string|array $resources
+ * @param string|array $privileges
+ * @uses Zend_Acl::setRule()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function removeAllow($roles = null, $resources = null, $privileges = null)
+ {
+ return $this->setRule(self::OP_REMOVE, self::TYPE_ALLOW, $roles, $resources, $privileges);
+ }
+
+ /**
+ * Removes "deny" restrictions from the ACL
+ *
+ * @param Zend_Acl_Role_Interface|string|array $roles
+ * @param Zend_Acl_Resource_Interface|string|array $resources
+ * @param string|array $privileges
+ * @uses Zend_Acl::setRule()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function removeDeny($roles = null, $resources = null, $privileges = null)
+ {
+ return $this->setRule(self::OP_REMOVE, self::TYPE_DENY, $roles, $resources, $privileges);
+ }
+
+ /**
+ * Performs operations on ACL rules
+ *
+ * The $operation parameter may be either OP_ADD or OP_REMOVE, depending on whether the
+ * user wants to add or remove a rule, respectively:
+ *
+ * OP_ADD specifics:
+ *
+ * A rule is added that would allow one or more Roles access to [certain $privileges
+ * upon] the specified Resource(s).
+ *
+ * OP_REMOVE specifics:
+ *
+ * The rule is removed only in the context of the given Roles, Resources, and privileges.
+ * Existing rules to which the remove operation does not apply would remain in the
+ * ACL.
+ *
+ * The $type parameter may be either TYPE_ALLOW or TYPE_DENY, depending on whether the
+ * rule is intended to allow or deny permission, respectively.
+ *
+ * The $roles and $resources parameters may be references to, or the string identifiers for,
+ * existing Resources/Roles, or they may be passed as arrays of these - mixing string identifiers
+ * and objects is ok - to indicate the Resources and Roles to which the rule applies. If either
+ * $roles or $resources is null, then the rule applies to all Roles or all Resources, respectively.
+ * Both may be null in order to work with the default rule of the ACL.
+ *
+ * The $privileges parameter may be used to further specify that the rule applies only
+ * to certain privileges upon the Resource(s) in question. This may be specified to be a single
+ * privilege with a string, and multiple privileges may be specified as an array of strings.
+ *
+ * If $assert is provided, then its assert() method must return true in order for
+ * the rule to apply. If $assert is provided with $roles, $resources, and $privileges all
+ * equal to null, then a rule having a type of:
+ *
+ * TYPE_ALLOW will imply a type of TYPE_DENY, and
+ *
+ * TYPE_DENY will imply a type of TYPE_ALLOW
+ *
+ * when the rule's assertion fails. This is because the ACL needs to provide expected
+ * behavior when an assertion upon the default ACL rule fails.
+ *
+ * @param string $operation
+ * @param string $type
+ * @param Zend_Acl_Role_Interface|string|array $roles
+ * @param Zend_Acl_Resource_Interface|string|array $resources
+ * @param string|array $privileges
+ * @param Zend_Acl_Assert_Interface $assert
+ * @throws Zend_Acl_Exception
+ * @uses Zend_Acl_Role_Registry::get()
+ * @uses Zend_Acl::get()
+ * @return Zend_Acl Provides a fluent interface
+ */
+ public function setRule($operation, $type, $roles = null, $resources = null, $privileges = null,
+ Zend_Acl_Assert_Interface $assert = null)
+ {
+ // ensure that the rule type is valid; normalize input to uppercase
+ $type = strtoupper($type);
+ if (self::TYPE_ALLOW !== $type && self::TYPE_DENY !== $type) {
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception("Unsupported rule type; must be either '" . self::TYPE_ALLOW . "' or '"
+ . self::TYPE_DENY . "'");
+ }
+
+ // ensure that all specified Roles exist; normalize input to array of Role objects or null
+ if (!is_array($roles)) {
+ $roles = array($roles);
+ } else if (0 === count($roles)) {
+ $roles = array(null);
+ }
+ $rolesTemp = $roles;
+ $roles = array();
+ foreach ($rolesTemp as $role) {
+ if (null !== $role) {
+ $roles[] = $this->_getRoleRegistry()->get($role);
+ } else {
+ $roles[] = null;
+ }
+ }
+ unset($rolesTemp);
+
+ // ensure that all specified Resources exist; normalize input to array of Resource objects or null
+ if ($resources !== null) {
+ if (!is_array($resources)) {
+ $resources = array($resources);
+ } else if (0 === count($resources)) {
+ $resources = array(null);
+ }
+ $resourcesTemp = $resources;
+ $resources = array();
+ foreach ($resourcesTemp as $resource) {
+ if (null !== $resource) {
+ $resources[] = $this->get($resource);
+ } else {
+ $resources[] = null;
+ }
+ }
+ unset($resourcesTemp, $resource);
+ } else {
+ $allResources = array(); // this might be used later if resource iteration is required
+ foreach ($this->_resources as $rTarget) {
+ $allResources[] = $rTarget['instance'];
+ }
+ unset($rTarget);
+ }
+
+ // normalize privileges to array
+ if (null === $privileges) {
+ $privileges = array();
+ } else if (!is_array($privileges)) {
+ $privileges = array($privileges);
+ }
+
+ switch ($operation) {
+
+ // add to the rules
+ case self::OP_ADD:
+ if ($resources !== null) {
+ // this block will iterate the provided resources
+ foreach ($resources as $resource) {
+ foreach ($roles as $role) {
+ $rules =& $this->_getRules($resource, $role, true);
+ if (0 === count($privileges)) {
+ $rules['allPrivileges']['type'] = $type;
+ $rules['allPrivileges']['assert'] = $assert;
+ if (!isset($rules['byPrivilegeId'])) {
+ $rules['byPrivilegeId'] = array();
+ }
+ } else {
+ foreach ($privileges as $privilege) {
+ $rules['byPrivilegeId'][$privilege]['type'] = $type;
+ $rules['byPrivilegeId'][$privilege]['assert'] = $assert;
+ }
+ }
+ }
+ }
+ } else {
+ // this block will apply to all resources in a global rule
+ foreach ($roles as $role) {
+ $rules =& $this->_getRules(null, $role, true);
+ if (0 === count($privileges)) {
+ $rules['allPrivileges']['type'] = $type;
+ $rules['allPrivileges']['assert'] = $assert;
+ } else {
+ foreach ($privileges as $privilege) {
+ $rules['byPrivilegeId'][$privilege]['type'] = $type;
+ $rules['byPrivilegeId'][$privilege]['assert'] = $assert;
+ }
+ }
+ }
+ }
+ break;
+
+ // remove from the rules
+ case self::OP_REMOVE:
+ if ($resources !== null) {
+ // this block will iterate the provided resources
+ foreach ($resources as $resource) {
+ foreach ($roles as $role) {
+ $rules =& $this->_getRules($resource, $role);
+ if (null === $rules) {
+ continue;
+ }
+ if (0 === count($privileges)) {
+ if (null === $resource && null === $role) {
+ if ($type === $rules['allPrivileges']['type']) {
+ $rules = array(
+ 'allPrivileges' => array(
+ 'type' => self::TYPE_DENY,
+ 'assert' => null
+ ),
+ 'byPrivilegeId' => array()
+ );
+ }
+ continue;
+ }
+
+ if (isset($rules['allPrivileges']['type']) &&
+ $type === $rules['allPrivileges']['type'])
+ {
+ unset($rules['allPrivileges']);
+ }
+ } else {
+ foreach ($privileges as $privilege) {
+ if (isset($rules['byPrivilegeId'][$privilege]) &&
+ $type === $rules['byPrivilegeId'][$privilege]['type'])
+ {
+ unset($rules['byPrivilegeId'][$privilege]);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // this block will apply to all resources in a global rule
+ foreach ($roles as $role) {
+ /**
+ * since null (all resources) was passed to this setRule() call, we need
+ * clean up all the rules for the global allResources, as well as the indivually
+ * set resources (per privilege as well)
+ */
+ foreach (array_merge(array(null), $allResources) as $resource) {
+ $rules =& $this->_getRules($resource, $role, true);
+ if (null === $rules) {
+ continue;
+ }
+ if (0 === count($privileges)) {
+ if (null === $role) {
+ if ($type === $rules['allPrivileges']['type']) {
+ $rules = array(
+ 'allPrivileges' => array(
+ 'type' => self::TYPE_DENY,
+ 'assert' => null
+ ),
+ 'byPrivilegeId' => array()
+ );
+ }
+ continue;
+ }
+
+ if (isset($rules['allPrivileges']['type']) && $type === $rules['allPrivileges']['type']) {
+ unset($rules['allPrivileges']);
+ }
+ } else {
+ foreach ($privileges as $privilege) {
+ if (isset($rules['byPrivilegeId'][$privilege]) &&
+ $type === $rules['byPrivilegeId'][$privilege]['type'])
+ {
+ unset($rules['byPrivilegeId'][$privilege]);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception("Unsupported operation; must be either '" . self::OP_ADD . "' or '"
+ . self::OP_REMOVE . "'");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns true if and only if the Role has access to the Resource
+ *
+ * The $role and $resource parameters may be references to, or the string identifiers for,
+ * an existing Resource and Role combination.
+ *
+ * If either $role or $resource is null, then the query applies to all Roles or all Resources,
+ * respectively. Both may be null to query whether the ACL has a "blacklist" rule
+ * (allow everything to all). By default, Zend_Acl creates a "whitelist" rule (deny
+ * everything to all), and this method would return false unless this default has
+ * been overridden (i.e., by executing $acl->allow()).
+ *
+ * If a $privilege is not provided, then this method returns false if and only if the
+ * Role is denied access to at least one privilege upon the Resource. In other words, this
+ * method returns true if and only if the Role is allowed all privileges on the Resource.
+ *
+ * This method checks Role inheritance using a depth-first traversal of the Role registry.
+ * The highest priority parent (i.e., the parent most recently added) is checked first,
+ * and its respective parents are checked similarly before the lower-priority parents of
+ * the Role are checked.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @param Zend_Acl_Resource_Interface|string $resource
+ * @param string $privilege
+ * @uses Zend_Acl::get()
+ * @uses Zend_Acl_Role_Registry::get()
+ * @return boolean
+ */
+ public function isAllowed($role = null, $resource = null, $privilege = null)
+ {
+ // reset role & resource to null
+ $this->_isAllowedRole = null;
+ $this->_isAllowedResource = null;
+ $this->_isAllowedPrivilege = null;
+
+ if (null !== $role) {
+ // keep track of originally called role
+ $this->_isAllowedRole = $role;
+ $role = $this->_getRoleRegistry()->get($role);
+ if (!$this->_isAllowedRole instanceof Zend_Acl_Role_Interface) {
+ $this->_isAllowedRole = $role;
+ }
+ }
+
+ if (null !== $resource) {
+ // keep track of originally called resource
+ $this->_isAllowedResource = $resource;
+ $resource = $this->get($resource);
+ if (!$this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) {
+ $this->_isAllowedResource = $resource;
+ }
+ }
+
+ if (null === $privilege) {
+ // query on all privileges
+ do {
+ // depth-first search on $role if it is not 'allRoles' pseudo-parent
+ if (null !== $role && null !== ($result = $this->_roleDFSAllPrivileges($role, $resource, $privilege))) {
+ return $result;
+ }
+
+ // look for rule on 'allRoles' psuedo-parent
+ if (null !== ($rules = $this->_getRules($resource, null))) {
+ foreach ($rules['byPrivilegeId'] as $privilege => $rule) {
+ if (self::TYPE_DENY === ($ruleTypeOnePrivilege = $this->_getRuleType($resource, null, $privilege))) {
+ return false;
+ }
+ }
+ if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, null, null))) {
+ return self::TYPE_ALLOW === $ruleTypeAllPrivileges;
+ }
+ }
+
+ // try next Resource
+ $resource = $this->_resources[$resource->getResourceId()]['parent'];
+
+ } while (true); // loop terminates at 'allResources' pseudo-parent
+ } else {
+ $this->_isAllowedPrivilege = $privilege;
+ // query on one privilege
+ do {
+ // depth-first search on $role if it is not 'allRoles' pseudo-parent
+ if (null !== $role && null !== ($result = $this->_roleDFSOnePrivilege($role, $resource, $privilege))) {
+ return $result;
+ }
+
+ // look for rule on 'allRoles' pseudo-parent
+ if (null !== ($ruleType = $this->_getRuleType($resource, null, $privilege))) {
+ return self::TYPE_ALLOW === $ruleType;
+ } else if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, null, null))) {
+ return self::TYPE_ALLOW === $ruleTypeAllPrivileges;
+ }
+
+ // try next Resource
+ $resource = $this->_resources[$resource->getResourceId()]['parent'];
+
+ } while (true); // loop terminates at 'allResources' pseudo-parent
+ }
+ }
+
+ /**
+ * Returns the Role registry for this ACL
+ *
+ * If no Role registry has been created yet, a new default Role registry
+ * is created and returned.
+ *
+ * @return Zend_Acl_Role_Registry
+ */
+ protected function _getRoleRegistry()
+ {
+ if (null === $this->_roleRegistry) {
+ $this->_roleRegistry = new Zend_Acl_Role_Registry();
+ }
+ return $this->_roleRegistry;
+ }
+
+ /**
+ * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule
+ * allowing/denying $role access to all privileges upon $resource
+ *
+ * This method returns true if a rule is found and allows access. If a rule exists and denies access,
+ * then this method returns false. If no applicable rule is found, then this method returns null.
+ *
+ * @param Zend_Acl_Role_Interface $role
+ * @param Zend_Acl_Resource_Interface $resource
+ * @return boolean|null
+ */
+ protected function _roleDFSAllPrivileges(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null)
+ {
+ $dfs = array(
+ 'visited' => array(),
+ 'stack' => array()
+ );
+
+ if (null !== ($result = $this->_roleDFSVisitAllPrivileges($role, $resource, $dfs))) {
+ return $result;
+ }
+
+ while (null !== ($role = array_pop($dfs['stack']))) {
+ if (!isset($dfs['visited'][$role->getRoleId()])) {
+ if (null !== ($result = $this->_roleDFSVisitAllPrivileges($role, $resource, $dfs))) {
+ return $result;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Visits an $role in order to look for a rule allowing/denying $role access to all privileges upon $resource
+ *
+ * This method returns true if a rule is found and allows access. If a rule exists and denies access,
+ * then this method returns false. If no applicable rule is found, then this method returns null.
+ *
+ * This method is used by the internal depth-first search algorithm and may modify the DFS data structure.
+ *
+ * @param Zend_Acl_Role_Interface $role
+ * @param Zend_Acl_Resource_Interface $resource
+ * @param array $dfs
+ * @return boolean|null
+ * @throws Zend_Acl_Exception
+ */
+ protected function _roleDFSVisitAllPrivileges(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null,
+ &$dfs = null)
+ {
+ if (null === $dfs) {
+ /**
+ * @see Zend_Acl_Exception
+ */
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception('$dfs parameter may not be null');
+ }
+
+ if (null !== ($rules = $this->_getRules($resource, $role))) {
+ foreach ($rules['byPrivilegeId'] as $privilege => $rule) {
+ if (self::TYPE_DENY === ($ruleTypeOnePrivilege = $this->_getRuleType($resource, $role, $privilege))) {
+ return false;
+ }
+ }
+ if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, $role, null))) {
+ return self::TYPE_ALLOW === $ruleTypeAllPrivileges;
+ }
+ }
+
+ $dfs['visited'][$role->getRoleId()] = true;
+ foreach ($this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) {
+ $dfs['stack'][] = $roleParent;
+ }
+
+ return null;
+ }
+
+ /**
+ * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule
+ * allowing/denying $role access to a $privilege upon $resource
+ *
+ * This method returns true if a rule is found and allows access. If a rule exists and denies access,
+ * then this method returns false. If no applicable rule is found, then this method returns null.
+ *
+ * @param Zend_Acl_Role_Interface $role
+ * @param Zend_Acl_Resource_Interface $resource
+ * @param string $privilege
+ * @return boolean|null
+ * @throws Zend_Acl_Exception
+ */
+ protected function _roleDFSOnePrivilege(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null,
+ $privilege = null)
+ {
+ if (null === $privilege) {
+ /**
+ * @see Zend_Acl_Exception
+ */
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception('$privilege parameter may not be null');
+ }
+
+ $dfs = array(
+ 'visited' => array(),
+ 'stack' => array()
+ );
+
+ if (null !== ($result = $this->_roleDFSVisitOnePrivilege($role, $resource, $privilege, $dfs))) {
+ return $result;
+ }
+
+ while (null !== ($role = array_pop($dfs['stack']))) {
+ if (!isset($dfs['visited'][$role->getRoleId()])) {
+ if (null !== ($result = $this->_roleDFSVisitOnePrivilege($role, $resource, $privilege, $dfs))) {
+ return $result;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Visits an $role in order to look for a rule allowing/denying $role access to a $privilege upon $resource
+ *
+ * This method returns true if a rule is found and allows access. If a rule exists and denies access,
+ * then this method returns false. If no applicable rule is found, then this method returns null.
+ *
+ * This method is used by the internal depth-first search algorithm and may modify the DFS data structure.
+ *
+ * @param Zend_Acl_Role_Interface $role
+ * @param Zend_Acl_Resource_Interface $resource
+ * @param string $privilege
+ * @param array $dfs
+ * @return boolean|null
+ * @throws Zend_Acl_Exception
+ */
+ protected function _roleDFSVisitOnePrivilege(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null,
+ $privilege = null, &$dfs = null)
+ {
+ if (null === $privilege) {
+ /**
+ * @see Zend_Acl_Exception
+ */
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception('$privilege parameter may not be null');
+ }
+
+ if (null === $dfs) {
+ /**
+ * @see Zend_Acl_Exception
+ */
+ require_once 'Zend/Acl/Exception.php';
+ throw new Zend_Acl_Exception('$dfs parameter may not be null');
+ }
+
+ if (null !== ($ruleTypeOnePrivilege = $this->_getRuleType($resource, $role, $privilege))) {
+ return self::TYPE_ALLOW === $ruleTypeOnePrivilege;
+ } else if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, $role, null))) {
+ return self::TYPE_ALLOW === $ruleTypeAllPrivileges;
+ }
+
+ $dfs['visited'][$role->getRoleId()] = true;
+ foreach ($this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) {
+ $dfs['stack'][] = $roleParent;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the rule type associated with the specified Resource, Role, and privilege
+ * combination.
+ *
+ * If a rule does not exist or its attached assertion fails, which means that
+ * the rule is not applicable, then this method returns null. Otherwise, the
+ * rule type applies and is returned as either TYPE_ALLOW or TYPE_DENY.
+ *
+ * If $resource or $role is null, then this means that the rule must apply to
+ * all Resources or Roles, respectively.
+ *
+ * If $privilege is null, then the rule must apply to all privileges.
+ *
+ * If all three parameters are null, then the default ACL rule type is returned,
+ * based on whether its assertion method passes.
+ *
+ * @param Zend_Acl_Resource_Interface $resource
+ * @param Zend_Acl_Role_Interface $role
+ * @param string $privilege
+ * @return string|null
+ */
+ protected function _getRuleType(Zend_Acl_Resource_Interface $resource = null, Zend_Acl_Role_Interface $role = null,
+ $privilege = null)
+ {
+ // get the rules for the $resource and $role
+ if (null === ($rules = $this->_getRules($resource, $role))) {
+ return null;
+ }
+
+ // follow $privilege
+ if (null === $privilege) {
+ if (isset($rules['allPrivileges'])) {
+ $rule = $rules['allPrivileges'];
+ } else {
+ return null;
+ }
+ } else if (!isset($rules['byPrivilegeId'][$privilege])) {
+ return null;
+ } else {
+ $rule = $rules['byPrivilegeId'][$privilege];
+ }
+
+ // check assertion first
+ if ($rule['assert']) {
+ $assertion = $rule['assert'];
+ $assertionValue = $assertion->assert(
+ $this,
+ ($this->_isAllowedRole instanceof Zend_Acl_Role_Interface) ? $this->_isAllowedRole : $role,
+ ($this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) ? $this->_isAllowedResource : $resource,
+ $this->_isAllowedPrivilege
+ );
+ }
+
+ if (null === $rule['assert'] || $assertionValue) {
+ return $rule['type'];
+ } else if (null !== $resource || null !== $role || null !== $privilege) {
+ return null;
+ } else if (self::TYPE_ALLOW === $rule['type']) {
+ return self::TYPE_DENY;
+ } else {
+ return self::TYPE_ALLOW;
+ }
+ }
+
+ /**
+ * Returns the rules associated with a Resource and a Role, or null if no such rules exist
+ *
+ * If either $resource or $role is null, this means that the rules returned are for all Resources or all Roles,
+ * respectively. Both can be null to return the default rule set for all Resources and all Roles.
+ *
+ * If the $create parameter is true, then a rule set is first created and then returned to the caller.
+ *
+ * @param Zend_Acl_Resource_Interface $resource
+ * @param Zend_Acl_Role_Interface $role
+ * @param boolean $create
+ * @return array|null
+ */
+ protected function &_getRules(Zend_Acl_Resource_Interface $resource = null, Zend_Acl_Role_Interface $role = null,
+ $create = false)
+ {
+ // create a reference to null
+ $null = null;
+ $nullRef =& $null;
+
+ // follow $resource
+ do {
+ if (null === $resource) {
+ $visitor =& $this->_rules['allResources'];
+ break;
+ }
+ $resourceId = $resource->getResourceId();
+ if (!isset($this->_rules['byResourceId'][$resourceId])) {
+ if (!$create) {
+ return $nullRef;
+ }
+ $this->_rules['byResourceId'][$resourceId] = array();
+ }
+ $visitor =& $this->_rules['byResourceId'][$resourceId];
+ } while (false);
+
+
+ // follow $role
+ if (null === $role) {
+ if (!isset($visitor['allRoles'])) {
+ if (!$create) {
+ return $nullRef;
+ }
+ $visitor['allRoles']['byPrivilegeId'] = array();
+ }
+ return $visitor['allRoles'];
+ }
+ $roleId = $role->getRoleId();
+ if (!isset($visitor['byRoleId'][$roleId])) {
+ if (!$create) {
+ return $nullRef;
+ }
+ $visitor['byRoleId'][$roleId]['byPrivilegeId'] = array();
+ $visitor['byRoleId'][$roleId]['allPrivileges'] = array('type' => null, 'assert' => null);
+ }
+ return $visitor['byRoleId'][$roleId];
+ }
+
+
+ /**
+ * @return array of registered roles (Deprecated)
+ * @deprecated Deprecated since version 1.10 (December 2009)
+ */
+ public function getRegisteredRoles()
+ {
+ trigger_error('The method getRegisteredRoles() was deprecated as of '
+ . 'version 1.0, and may be removed. You\'re encouraged '
+ . 'to use getRoles() instead.');
+
+ return $this->_getRoleRegistry()->getRoles();
+ }
+
+ /**
+ * Returns an array of registered roles.
+ *
+ * Note that this method does not return instances of registered roles,
+ * but only the role identifiers.
+ *
+ * @return array of registered roles
+ */
+ public function getRoles()
+ {
+ return array_keys($this->_getRoleRegistry()->getRoles());
+ }
+
+ /**
+ * @return array of registered resources
+ */
+ public function getResources()
+ {
+ return array_keys($this->_resources);
+ }
+
+}
+
diff --git a/library/Zend/Acl/Assert/Interface.php b/library/Zend/Acl/Assert/Interface.php
new file mode 100644
index 0000000..71a8b15
--- /dev/null
+++ b/library/Zend/Acl/Assert/Interface.php
@@ -0,0 +1,64 @@
+_resourceId = (string) $resourceId;
+ }
+
+ /**
+ * Defined by Zend_Acl_Resource_Interface; returns the Resource identifier
+ *
+ * @return string
+ */
+ public function getResourceId()
+ {
+ return $this->_resourceId;
+ }
+
+ /**
+ * Defined by Zend_Acl_Resource_Interface; returns the Resource identifier
+ * Proxies to getResourceId()
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getResourceId();
+ }
+}
diff --git a/library/Zend/Acl/Resource/Interface.php b/library/Zend/Acl/Resource/Interface.php
new file mode 100644
index 0000000..33a6492
--- /dev/null
+++ b/library/Zend/Acl/Resource/Interface.php
@@ -0,0 +1,37 @@
+_roleId = (string) $roleId;
+ }
+
+ /**
+ * Defined by Zend_Acl_Role_Interface; returns the Role identifier
+ *
+ * @return string
+ */
+ public function getRoleId()
+ {
+ return $this->_roleId;
+ }
+
+ /**
+ * Defined by Zend_Acl_Role_Interface; returns the Role identifier
+ * Proxies to getRoleId()
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getRoleId();
+ }
+}
diff --git a/library/Zend/Acl/Role/Interface.php b/library/Zend/Acl/Role/Interface.php
new file mode 100644
index 0000000..d6d16ca
--- /dev/null
+++ b/library/Zend/Acl/Role/Interface.php
@@ -0,0 +1,37 @@
+getRoleId();
+
+ if ($this->has($roleId)) {
+ /**
+ * @see Zend_Acl_Role_Registry_Exception
+ */
+ require_once 'Zend/Acl/Role/Registry/Exception.php';
+ throw new Zend_Acl_Role_Registry_Exception("Role id '$roleId' already exists in the registry");
+ }
+
+ $roleParents = array();
+
+ if (null !== $parents) {
+ if (!is_array($parents)) {
+ $parents = array($parents);
+ }
+ /**
+ * @see Zend_Acl_Role_Registry_Exception
+ */
+ require_once 'Zend/Acl/Role/Registry/Exception.php';
+ foreach ($parents as $parent) {
+ try {
+ if ($parent instanceof Zend_Acl_Role_Interface) {
+ $roleParentId = $parent->getRoleId();
+ } else {
+ $roleParentId = $parent;
+ }
+ $roleParent = $this->get($roleParentId);
+ } catch (Zend_Acl_Role_Registry_Exception $e) {
+ throw new Zend_Acl_Role_Registry_Exception("Parent Role id '$roleParentId' does not exist", 0, $e);
+ }
+ $roleParents[$roleParentId] = $roleParent;
+ $this->_roles[$roleParentId]['children'][$roleId] = $role;
+ }
+ }
+
+ $this->_roles[$roleId] = array(
+ 'instance' => $role,
+ 'parents' => $roleParents,
+ 'children' => array()
+ );
+
+ return $this;
+ }
+
+ /**
+ * Returns the identified Role
+ *
+ * The $role parameter can either be a Role or a Role identifier.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @throws Zend_Acl_Role_Registry_Exception
+ * @return Zend_Acl_Role_Interface
+ */
+ public function get($role)
+ {
+ if ($role instanceof Zend_Acl_Role_Interface) {
+ $roleId = $role->getRoleId();
+ } else {
+ $roleId = (string) $role;
+ }
+
+ if (!$this->has($role)) {
+ /**
+ * @see Zend_Acl_Role_Registry_Exception
+ */
+ require_once 'Zend/Acl/Role/Registry/Exception.php';
+ throw new Zend_Acl_Role_Registry_Exception("Role '$roleId' not found");
+ }
+
+ return $this->_roles[$roleId]['instance'];
+ }
+
+ /**
+ * Returns true if and only if the Role exists in the registry
+ *
+ * The $role parameter can either be a Role or a Role identifier.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @return boolean
+ */
+ public function has($role)
+ {
+ if ($role instanceof Zend_Acl_Role_Interface) {
+ $roleId = $role->getRoleId();
+ } else {
+ $roleId = (string) $role;
+ }
+
+ return isset($this->_roles[$roleId]);
+ }
+
+ /**
+ * Returns an array of an existing Role's parents
+ *
+ * The array keys are the identifiers of the parent Roles, and the values are
+ * the parent Role instances. The parent Roles are ordered in this array by
+ * ascending priority. The highest priority parent Role, last in the array,
+ * corresponds with the parent Role most recently added.
+ *
+ * If the Role does not have any parents, then an empty array is returned.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @uses Zend_Acl_Role_Registry::get()
+ * @return array
+ */
+ public function getParents($role)
+ {
+ $roleId = $this->get($role)->getRoleId();
+
+ return $this->_roles[$roleId]['parents'];
+ }
+
+ /**
+ * Returns true if and only if $role inherits from $inherit
+ *
+ * Both parameters may be either a Role or a Role identifier. If
+ * $onlyParents is true, then $role must inherit directly from
+ * $inherit in order to return true. By default, this method looks
+ * through the entire inheritance DAG to determine whether $role
+ * inherits from $inherit through its ancestor Roles.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @param Zend_Acl_Role_Interface|string $inherit
+ * @param boolean $onlyParents
+ * @throws Zend_Acl_Role_Registry_Exception
+ * @return boolean
+ */
+ public function inherits($role, $inherit, $onlyParents = false)
+ {
+ /**
+ * @see Zend_Acl_Role_Registry_Exception
+ */
+ require_once 'Zend/Acl/Role/Registry/Exception.php';
+ try {
+ $roleId = $this->get($role)->getRoleId();
+ $inheritId = $this->get($inherit)->getRoleId();
+ } catch (Zend_Acl_Role_Registry_Exception $e) {
+ throw new Zend_Acl_Role_Registry_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+
+ $inherits = isset($this->_roles[$roleId]['parents'][$inheritId]);
+
+ if ($inherits || $onlyParents) {
+ return $inherits;
+ }
+
+ foreach ($this->_roles[$roleId]['parents'] as $parentId => $parent) {
+ if ($this->inherits($parentId, $inheritId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes the Role from the registry
+ *
+ * The $role parameter can either be a Role or a Role identifier.
+ *
+ * @param Zend_Acl_Role_Interface|string $role
+ * @throws Zend_Acl_Role_Registry_Exception
+ * @return Zend_Acl_Role_Registry Provides a fluent interface
+ */
+ public function remove($role)
+ {
+ /**
+ * @see Zend_Acl_Role_Registry_Exception
+ */
+ require_once 'Zend/Acl/Role/Registry/Exception.php';
+ try {
+ $roleId = $this->get($role)->getRoleId();
+ } catch (Zend_Acl_Role_Registry_Exception $e) {
+ throw new Zend_Acl_Role_Registry_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+
+ foreach ($this->_roles[$roleId]['children'] as $childId => $child) {
+ unset($this->_roles[$childId]['parents'][$roleId]);
+ }
+ foreach ($this->_roles[$roleId]['parents'] as $parentId => $parent) {
+ unset($this->_roles[$parentId]['children'][$roleId]);
+ }
+
+ unset($this->_roles[$roleId]);
+
+ return $this;
+ }
+
+ /**
+ * Removes all Roles from the registry
+ *
+ * @return Zend_Acl_Role_Registry Provides a fluent interface
+ */
+ public function removeAll()
+ {
+ $this->_roles = array();
+
+ return $this;
+ }
+
+ public function getRoles()
+ {
+ return $this->_roles;
+ }
+
+}
diff --git a/library/Zend/Acl/Role/Registry/Exception.php b/library/Zend/Acl/Role/Registry/Exception.php
new file mode 100644
index 0000000..f5b710b
--- /dev/null
+++ b/library/Zend/Acl/Role/Registry/Exception.php
@@ -0,0 +1,36 @@
+_acl = new Zend_Acl();
+ $xml = simplexml_load_file($rolefile);
+/*
+Roles file format:
+
+
+
+
+
+
+
+
+*/
+ foreach($xml->role as $role) {
+ $this->_acl->addRole(new Zend_Acl_Role((string)$role["id"]));
+ foreach($role->user as $user) {
+ $this->_users[(string)$user["name"]] = array("password" => (string)$user["password"],
+ "role" => (string)$role["id"]);
+ }
+ }
+ }
+
+ /**
+ * Get ACL with roles from XML file
+ *
+ * @return Zend_Acl
+ */
+ public function getAcl()
+ {
+ return $this->_acl;
+ }
+
+ /**
+ * Perform authentication
+ *
+ * @throws Zend_Auth_Adapter_Exception
+ * @return Zend_Auth_Result
+ * @see Zend_Auth_Adapter_Interface#authenticate()
+ */
+ public function authenticate()
+ {
+ if (empty($this->_username) ||
+ empty($this->_password)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Username/password should be set');
+ }
+
+ if(!isset($this->_users[$this->_username])) {
+ return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND,
+ null,
+ array('Username not found')
+ );
+ }
+
+ $user = $this->_users[$this->_username];
+ if($user["password"] != $this->_password) {
+ return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID,
+ null,
+ array('Authentication failed')
+ );
+ }
+
+ $id = new stdClass();
+ $id->role = $user["role"];
+ $id->name = $this->_username;
+ return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $id);
+ }
+}
diff --git a/library/Zend/Amf/Adobe/DbInspector.php b/library/Zend/Amf/Adobe/DbInspector.php
new file mode 100644
index 0000000..2047a5b
--- /dev/null
+++ b/library/Zend/Amf/Adobe/DbInspector.php
@@ -0,0 +1,103 @@
+describeTable('Pdo_Mysql',
+ * array(
+ * 'host' => '127.0.0.1',
+ * 'username' => 'webuser',
+ * 'password' => 'xxxxxxxx',
+ * 'dbname' => 'test'
+ * ),
+ * 'mytable'
+ * );
+ *
+ * @param string $dbType Database adapter type for Zend_Db
+ * @param array|object $dbDescription Adapter-specific connection settings
+ * @param string $tableName Table name
+ * @return array Table description
+ * @see Zend_Db::describeTable()
+ * @see Zend_Db::factory()
+ */
+ public function describeTable($dbType, $dbDescription, $tableName)
+ {
+ $db = $this->_connect($dbType, $dbDescription);
+ return $db->describeTable($tableName);
+ }
+
+ /**
+ * Test database connection
+ *
+ * @param string $dbType Database adapter type for Zend_Db
+ * @param array|object $dbDescription Adapter-specific connection settings
+ * @return bool
+ * @see Zend_Db::factory()
+ */
+ public function connect($dbType, $dbDescription)
+ {
+ $db = $this->_connect($dbType, $dbDescription);
+ $db->listTables();
+ return true;
+ }
+
+ /**
+ * Get the list of database tables
+ *
+ * @param string $dbType Database adapter type for Zend_Db
+ * @param array|object $dbDescription Adapter-specific connection settings
+ * @return array List of the tables
+ */
+ public function getTables($dbType, $dbDescription)
+ {
+ $db = $this->_connect($dbType, $dbDescription);
+ return $db->listTables();
+ }
+}
diff --git a/library/Zend/Amf/Adobe/Introspector.php b/library/Zend/Amf/Adobe/Introspector.php
new file mode 100644
index 0000000..d370c8f
--- /dev/null
+++ b/library/Zend/Amf/Adobe/Introspector.php
@@ -0,0 +1,318 @@
+_xml = new DOMDocument('1.0', 'utf-8');
+ }
+
+ /**
+ * Create XML definition on an AMF service class
+ *
+ * @param string $serviceClass Service class name
+ * @param array $options invocation options
+ * @return string XML with service class introspection
+ */
+ public function introspect($serviceClass, $options = array())
+ {
+ $this->_options = $options;
+
+ if (strpbrk($serviceClass, '\\/<>')) {
+ return $this->_returnError('Invalid service name');
+ }
+
+ // Transform com.foo.Bar into com_foo_Bar
+ $serviceClass = str_replace('.' , '_', $serviceClass);
+
+ // Introspect!
+ if (!class_exists($serviceClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($serviceClass, $this->_getServicePath());
+ }
+
+ $serv = $this->_xml->createElement('service-description');
+ $serv->setAttribute('xmlns', 'http://ns.adobe.com/flex/service-description/2008');
+
+ $this->_types = $this->_xml->createElement('types');
+ $this->_ops = $this->_xml->createElement('operations');
+
+ $r = Zend_Server_Reflection::reflectClass($serviceClass);
+ $this->_addService($r, $this->_ops);
+
+ $serv->appendChild($this->_types);
+ $serv->appendChild($this->_ops);
+ $this->_xml->appendChild($serv);
+
+ return $this->_xml->saveXML();
+ }
+
+ /**
+ * Authentication handler
+ *
+ * @param Zend_Acl $acl
+ * @return unknown_type
+ */
+ public function initAcl(Zend_Acl $acl)
+ {
+ return false; // we do not need auth for this class
+ }
+
+ /**
+ * Generate map of public class attributes
+ *
+ * @param string $typename type name
+ * @param DOMElement $typexml target XML element
+ * @return void
+ */
+ protected function _addClassAttributes($typename, DOMElement $typexml)
+ {
+ // Do not try to autoload here because _phpTypeToAS should
+ // have already attempted to load this class
+ if (!class_exists($typename, false)) {
+ return;
+ }
+
+ $rc = new Zend_Reflection_Class($typename);
+ foreach ($rc->getProperties() as $prop) {
+ if (!$prop->isPublic()) {
+ continue;
+ }
+
+ $propxml = $this->_xml->createElement('property');
+ $propxml->setAttribute('name', $prop->getName());
+
+ $type = $this->_registerType($this->_getPropertyType($prop));
+ $propxml->setAttribute('type', $type);
+
+ $typexml->appendChild($propxml);
+ }
+ }
+
+ /**
+ * Build XML service description from reflection class
+ *
+ * @param Zend_Server_Reflection_Class $refclass
+ * @param DOMElement $target target XML element
+ * @return void
+ */
+ protected function _addService(Zend_Server_Reflection_Class $refclass, DOMElement $target)
+ {
+ foreach ($refclass->getMethods() as $method) {
+ if (!$method->isPublic()
+ || $method->isConstructor()
+ || ('__' == substr($method->name, 0, 2))
+ ) {
+ continue;
+ }
+
+ foreach ($method->getPrototypes() as $proto) {
+ $op = $this->_xml->createElement('operation');
+ $op->setAttribute('name', $method->getName());
+
+ $rettype = $this->_registerType($proto->getReturnType());
+ $op->setAttribute('returnType', $rettype);
+
+ foreach ($proto->getParameters() as $param) {
+ $arg = $this->_xml->createElement('argument');
+ $arg->setAttribute('name', $param->getName());
+
+ $type = $param->getType();
+ if ($type == 'mixed' && ($pclass = $param->getClass())) {
+ $type = $pclass->getName();
+ }
+
+ $ptype = $this->_registerType($type);
+ $arg->setAttribute('type', $ptype);
+
+ if($param->isDefaultValueAvailable()) {
+ $arg->setAttribute('defaultvalue', $param->getDefaultValue());
+ }
+
+ $op->appendChild($arg);
+ }
+
+ $target->appendChild($op);
+ }
+ }
+ }
+
+ /**
+ * Extract type of the property from DocBlock
+ *
+ * @param Zend_Reflection_Property $prop reflection property object
+ * @return string Property type
+ */
+ protected function _getPropertyType(Zend_Reflection_Property $prop)
+ {
+ $docBlock = $prop->getDocComment();
+
+ if (!$docBlock) {
+ return 'Unknown';
+ }
+
+ if (!$docBlock->hasTag('var')) {
+ return 'Unknown';
+ }
+
+ $tag = $docBlock->getTag('var');
+ return trim($tag->getDescription());
+ }
+
+ /**
+ * Get the array of service directories
+ *
+ * @return array Service class directories
+ */
+ protected function _getServicePath()
+ {
+ if (isset($this->_options['server'])) {
+ return $this->_options['server']->getDirectory();
+ }
+
+ if (isset($this->_options['directories'])) {
+ return $this->_options['directories'];
+ }
+
+ return array();
+ }
+
+ /**
+ * Map from PHP type name to AS type name
+ *
+ * @param string $typename PHP type name
+ * @return string AS type name
+ */
+ protected function _phpTypeToAS($typename)
+ {
+ if (class_exists($typename)) {
+ $vars = get_class_vars($typename);
+
+ if (isset($vars['_explicitType'])) {
+ return $vars['_explicitType'];
+ }
+ }
+
+ if (false !== ($asname = Zend_Amf_Parse_TypeLoader::getMappedClassName($typename))) {
+ return $asname;
+ }
+
+ return $typename;
+ }
+
+ /**
+ * Register new type on the system
+ *
+ * @param string $typename type name
+ * @return string New type name
+ */
+ protected function _registerType($typename)
+ {
+ // Known type - return its AS name
+ if (isset($this->_typesMap[$typename])) {
+ return $this->_typesMap[$typename];
+ }
+
+ // Standard types
+ if (in_array($typename, array('void', 'null', 'mixed', 'unknown_type'))) {
+ return 'Unknown';
+ }
+
+ // Arrays
+ if ('array' == $typename) {
+ return 'Unknown[]';
+ }
+
+ if (in_array($typename, array('int', 'integer', 'bool', 'boolean', 'float', 'string', 'object', 'Unknown', 'stdClass'))) {
+ return $typename;
+ }
+
+ // Resolve and store AS name
+ $asTypeName = $this->_phpTypeToAS($typename);
+ $this->_typesMap[$typename] = $asTypeName;
+
+ // Create element for the name
+ $typeEl = $this->_xml->createElement('type');
+ $typeEl->setAttribute('name', $asTypeName);
+ $this->_addClassAttributes($typename, $typeEl);
+ $this->_types->appendChild($typeEl);
+
+ return $asTypeName;
+ }
+
+ /**
+ * Return error with error message
+ *
+ * @param string $msg Error message
+ * @return string
+ */
+ protected function _returnError($msg)
+ {
+ return 'ERROR: $msg';
+ }
+}
diff --git a/library/Zend/Amf/Auth/Abstract.php b/library/Zend/Amf/Auth/Abstract.php
new file mode 100644
index 0000000..b816b80
--- /dev/null
+++ b/library/Zend/Amf/Auth/Abstract.php
@@ -0,0 +1,42 @@
+_username = $username;
+ $this->_password = $password;
+ }
+}
diff --git a/library/Zend/Amf/Constants.php b/library/Zend/Amf/Constants.php
new file mode 100644
index 0000000..f92a325
--- /dev/null
+++ b/library/Zend/Amf/Constants.php
@@ -0,0 +1,87 @@
+_stream->readByte();
+ }
+
+ switch($typeMarker) {
+ // number
+ case Zend_Amf_Constants::AMF0_NUMBER:
+ return $this->_stream->readDouble();
+
+ // boolean
+ case Zend_Amf_Constants::AMF0_BOOLEAN:
+ return (boolean) $this->_stream->readByte();
+
+ // string
+ case Zend_Amf_Constants::AMF0_STRING:
+ return $this->_stream->readUTF();
+
+ // object
+ case Zend_Amf_Constants::AMF0_OBJECT:
+ return $this->readObject();
+
+ // null
+ case Zend_Amf_Constants::AMF0_NULL:
+ return null;
+
+ // undefined
+ case Zend_Amf_Constants::AMF0_UNDEFINED:
+ return null;
+
+ // Circular references are returned here
+ case Zend_Amf_Constants::AMF0_REFERENCE:
+ return $this->readReference();
+
+ // mixed array with numeric and string keys
+ case Zend_Amf_Constants::AMF0_MIXEDARRAY:
+ return $this->readMixedArray();
+
+ // array
+ case Zend_Amf_Constants::AMF0_ARRAY:
+ return $this->readArray();
+
+ // date
+ case Zend_Amf_Constants::AMF0_DATE:
+ return $this->readDate();
+
+ // longString strlen(string) > 2^16
+ case Zend_Amf_Constants::AMF0_LONGSTRING:
+ return $this->_stream->readLongUTF();
+
+ //internal AS object, not supported
+ case Zend_Amf_Constants::AMF0_UNSUPPORTED:
+ return null;
+
+ // XML
+ case Zend_Amf_Constants::AMF0_XML:
+ return $this->readXmlString();
+
+ // typed object ie Custom Class
+ case Zend_Amf_Constants::AMF0_TYPEDOBJECT:
+ return $this->readTypedObject();
+
+ //AMF3-specific
+ case Zend_Amf_Constants::AMF0_AMF3:
+ return $this->readAmf3TypeMarker();
+
+ default:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unsupported marker type: ' . $typeMarker);
+ }
+ }
+
+ /**
+ * Read AMF objects and convert to PHP objects
+ *
+ * Read the name value pair objects form the php message and convert them to
+ * a php object class.
+ *
+ * Called when the marker type is 3.
+ *
+ * @param array|null $object
+ * @return object
+ */
+ public function readObject($object = null)
+ {
+ if ($object === null) {
+ $object = array();
+ }
+
+ while (true) {
+ $key = $this->_stream->readUTF();
+ $typeMarker = $this->_stream->readByte();
+ if ($typeMarker != Zend_Amf_Constants::AMF0_OBJECTTERM ){
+ //Recursivly call readTypeMarker to get the types of properties in the object
+ $object[$key] = $this->readTypeMarker($typeMarker);
+ } else {
+ //encountered AMF object terminator
+ break;
+ }
+ }
+ $this->_reference[] = $object;
+ return (object) $object;
+ }
+
+ /**
+ * Read reference objects
+ *
+ * Used to gain access to the private array of reference objects.
+ * Called when marker type is 7.
+ *
+ * @return object
+ * @throws Zend_Amf_Exception for invalid reference keys
+ */
+ public function readReference()
+ {
+ $key = $this->_stream->readInt();
+ if (!array_key_exists($key, $this->_reference)) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Invalid reference key: '. $key);
+ }
+ return $this->_reference[$key];
+ }
+
+ /**
+ * Reads an array with numeric and string indexes.
+ *
+ * Called when marker type is 8
+ *
+ * @todo As of Flash Player 9 there is not support for mixed typed arrays
+ * so we handle this as an object. With the introduction of vectors
+ * in Flash Player 10 this may need to be reconsidered.
+ * @return array
+ */
+ public function readMixedArray()
+ {
+ $length = $this->_stream->readLong();
+ return $this->readObject();
+ }
+
+ /**
+ * Converts numerically indexed actiosncript arrays into php arrays.
+ *
+ * Called when marker type is 10
+ *
+ * @return array
+ */
+ public function readArray()
+ {
+ $length = $this->_stream->readLong();
+ $array = array();
+ while ($length--) {
+ $array[] = $this->readTypeMarker();
+ }
+ return $array;
+ }
+
+ /**
+ * Convert AS Date to Zend_Date
+ *
+ * @return Zend_Date
+ */
+ public function readDate()
+ {
+ // get the unix time stamp. Not sure why ActionScript does not use
+ // milliseconds
+ $timestamp = floor($this->_stream->readDouble() / 1000);
+
+ // The timezone offset is never returned to the server; it is always 0,
+ // so read and ignore.
+ $offset = $this->_stream->readInt();
+
+ require_once 'Zend/Date.php';
+ $date = new Zend_Date($timestamp);
+ return $date;
+ }
+
+ /**
+ * Convert XML to SimpleXml
+ * If user wants DomDocument they can use dom_import_simplexml
+ *
+ * @return SimpleXml Object
+ */
+ public function readXmlString()
+ {
+ $string = $this->_stream->readLongUTF();
+ return simplexml_load_string($string);
+ }
+
+ /**
+ * Read Class that is to be mapped to a server class.
+ *
+ * Commonly used for Value Objects on the server
+ *
+ * @todo implement Typed Class mapping
+ * @return object|array
+ * @throws Zend_Amf_Exception if unable to load type
+ */
+ public function readTypedObject()
+ {
+ require_once 'Zend/Amf/Parse/TypeLoader.php';
+ // get the remote class name
+ $className = $this->_stream->readUTF();
+ $loader = Zend_Amf_Parse_TypeLoader::loadType($className);
+ $returnObject = new $loader();
+ $properties = get_object_vars($this->readObject());
+ foreach($properties as $key=>$value) {
+ if($key) {
+ $returnObject->$key = $value;
+ }
+ }
+ if($returnObject instanceof Zend_Amf_Value_Messaging_ArrayCollection) {
+ $returnObject = get_object_vars($returnObject);
+ }
+ return $returnObject;
+ }
+
+ /**
+ * AMF3 data type encountered load AMF3 Deserializer to handle
+ * type markers.
+ *
+ * @return string
+ */
+ public function readAmf3TypeMarker()
+ {
+ require_once 'Zend/Amf/Parse/Amf3/Deserializer.php';
+ $deserializer = new Zend_Amf_Parse_Amf3_Deserializer($this->_stream);
+ $this->_objectEncoding = Zend_Amf_Constants::AMF3_OBJECT_ENCODING;
+ return $deserializer->readTypeMarker();
+ }
+
+ /**
+ * Return the object encoding to check if an AMF3 object
+ * is going to be return.
+ *
+ * @return int
+ */
+ public function getObjectEncoding()
+ {
+ return $this->_objectEncoding;
+ }
+}
diff --git a/library/Zend/Amf/Parse/Amf0/Serializer.php b/library/Zend/Amf/Parse/Amf0/Serializer.php
new file mode 100644
index 0000000..2c64a81
--- /dev/null
+++ b/library/Zend/Amf/Parse/Amf0/Serializer.php
@@ -0,0 +1,362 @@
+writeObjectReference($data, $markerType)) {
+ // Write the Type Marker to denote the following action script data type
+ $this->_stream->writeByte($markerType);
+ switch($markerType) {
+ case Zend_Amf_Constants::AMF0_NUMBER:
+ $this->_stream->writeDouble($data);
+ break;
+ case Zend_Amf_Constants::AMF0_BOOLEAN:
+ $this->_stream->writeByte($data);
+ break;
+ case Zend_Amf_Constants::AMF0_STRING:
+ $this->_stream->writeUTF($data);
+ break;
+ case Zend_Amf_Constants::AMF0_OBJECT:
+ $this->writeObject($data);
+ break;
+ case Zend_Amf_Constants::AMF0_NULL:
+ break;
+ case Zend_Amf_Constants::AMF0_REFERENCE:
+ $this->_stream->writeInt($data);
+ break;
+ case Zend_Amf_Constants::AMF0_MIXEDARRAY:
+ // Write length of numeric keys as zero.
+ $this->_stream->writeLong(0);
+ $this->writeObject($data);
+ break;
+ case Zend_Amf_Constants::AMF0_ARRAY:
+ $this->writeArray($data);
+ break;
+ case Zend_Amf_Constants::AMF0_DATE:
+ $this->writeDate($data);
+ break;
+ case Zend_Amf_Constants::AMF0_LONGSTRING:
+ $this->_stream->writeLongUTF($data);
+ break;
+ case Zend_Amf_Constants::AMF0_TYPEDOBJECT:
+ $this->writeTypedObject($data);
+ break;
+ case Zend_Amf_Constants::AMF0_AMF3:
+ $this->writeAmf3TypeMarker($data);
+ break;
+ default:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception("Unknown Type Marker: " . $markerType);
+ }
+ }
+ } else {
+ if (is_resource($data)) {
+ $data = Zend_Amf_Parse_TypeLoader::handleResource($data);
+ }
+ switch (true) {
+ case (is_int($data) || is_float($data)):
+ $markerType = Zend_Amf_Constants::AMF0_NUMBER;
+ break;
+ case (is_bool($data)):
+ $markerType = Zend_Amf_Constants::AMF0_BOOLEAN;
+ break;
+ case (is_string($data) && (strlen($data) > 65536)):
+ $markerType = Zend_Amf_Constants::AMF0_LONGSTRING;
+ break;
+ case (is_string($data)):
+ $markerType = Zend_Amf_Constants::AMF0_STRING;
+ break;
+ case (is_object($data)):
+ if (($data instanceof DateTime) || ($data instanceof Zend_Date)) {
+ $markerType = Zend_Amf_Constants::AMF0_DATE;
+ } else {
+
+ if($className = $this->getClassName($data)){
+ //Object is a Typed object set classname
+ $markerType = Zend_Amf_Constants::AMF0_TYPEDOBJECT;
+ $this->_className = $className;
+ } else {
+ // Object is a generic classname
+ $markerType = Zend_Amf_Constants::AMF0_OBJECT;
+ }
+ break;
+ }
+ break;
+ case (null === $data):
+ $markerType = Zend_Amf_Constants::AMF0_NULL;
+ break;
+ case (is_array($data)):
+ // check if it is an associative array
+ $i = 0;
+ foreach (array_keys($data) as $key) {
+ // check if it contains non-integer keys
+ if (!is_numeric($key) || intval($key) != $key) {
+ $markerType = Zend_Amf_Constants::AMF0_OBJECT;
+ break;
+ // check if it is a sparse indexed array
+ } else if ($key != $i) {
+ $markerType = Zend_Amf_Constants::AMF0_MIXEDARRAY;
+ break;
+ }
+ $i++;
+ }
+ // Dealing with a standard numeric array
+ if(!$markerType){
+ $markerType = Zend_Amf_Constants::AMF0_ARRAY;
+ break;
+ }
+ break;
+ default:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unsupported data type: ' . gettype($data));
+ }
+
+ $this->writeTypeMarker($data, $markerType);
+ }
+ return $this;
+ }
+
+ /**
+ * Check if the given object is in the reference table, write the reference if it exists,
+ * otherwise add the object to the reference table
+ *
+ * @param mixed $object object reference to check for reference
+ * @param string $markerType AMF type of the object to write
+ * @param mixed $objectByVal object to check for reference
+ * @return Boolean true, if the reference was written, false otherwise
+ */
+ protected function writeObjectReference(&$object, $markerType, $objectByVal = false)
+ {
+ // Workaround for PHP5 with E_STRICT enabled complaining about "Only
+ // variables should be passed by reference"
+ if ((null === $object) && ($objectByVal !== false)) {
+ $object = &$objectByVal;
+ }
+
+ if ($markerType == Zend_Amf_Constants::AMF0_OBJECT
+ || $markerType == Zend_Amf_Constants::AMF0_MIXEDARRAY
+ || $markerType == Zend_Amf_Constants::AMF0_ARRAY
+ || $markerType == Zend_Amf_Constants::AMF0_TYPEDOBJECT
+ ) {
+ $ref = array_search($object, $this->_referenceObjects, true);
+ //handle object reference
+ if($ref !== false){
+ $this->writeTypeMarker($ref,Zend_Amf_Constants::AMF0_REFERENCE);
+ return true;
+ }
+
+ $this->_referenceObjects[] = $object;
+ }
+
+ return false;
+ }
+
+ /**
+ * Write a PHP array with string or mixed keys.
+ *
+ * @param object $data
+ * @return Zend_Amf_Parse_Amf0_Serializer
+ */
+ public function writeObject($object)
+ {
+ // Loop each element and write the name of the property.
+ foreach ($object as $key => &$value) {
+ // skip variables starting with an _ private transient
+ if( $key[0] == "_") continue;
+ $this->_stream->writeUTF($key);
+ $this->writeTypeMarker($value);
+ }
+
+ // Write the end object flag
+ $this->_stream->writeInt(0);
+ $this->_stream->writeByte(Zend_Amf_Constants::AMF0_OBJECTTERM);
+ return $this;
+ }
+
+ /**
+ * Write a standard numeric array to the output stream. If a mixed array
+ * is encountered call writeTypeMarker with mixed array.
+ *
+ * @param array $array
+ * @return Zend_Amf_Parse_Amf0_Serializer
+ */
+ public function writeArray(&$array)
+ {
+ $length = count($array);
+ if (!$length < 0) {
+ // write the length of the array
+ $this->_stream->writeLong(0);
+ } else {
+ // Write the length of the numeric array
+ $this->_stream->writeLong($length);
+ for ($i=0; $i<$length; $i++) {
+ $value = isset($array[$i]) ? $array[$i] : null;
+ $this->writeTypeMarker($value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Convert the DateTime into an AMF Date
+ *
+ * @param DateTime|Zend_Date $data
+ * @return Zend_Amf_Parse_Amf0_Serializer
+ */
+ public function writeDate($data)
+ {
+ if ($data instanceof DateTime) {
+ $dateString = $data->format('U');
+ } elseif ($data instanceof Zend_Date) {
+ $dateString = $data->toString('U');
+ } else {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Invalid date specified; must be a DateTime or Zend_Date object');
+ }
+ $dateString *= 1000;
+
+ // Make the conversion and remove milliseconds.
+ $this->_stream->writeDouble($dateString);
+
+ // Flash does not respect timezone but requires it.
+ $this->_stream->writeInt(0);
+
+ return $this;
+ }
+
+ /**
+ * Write a class mapped object to the output stream.
+ *
+ * @param object $data
+ * @return Zend_Amf_Parse_Amf0_Serializer
+ */
+ public function writeTypedObject($data)
+ {
+ $this->_stream->writeUTF($this->_className);
+ $this->writeObject($data);
+ return $this;
+ }
+
+ /**
+ * Encountered and AMF3 Type Marker use AMF3 serializer. Once AMF3 is
+ * encountered it will not return to AMf0.
+ *
+ * @param string $data
+ * @return Zend_Amf_Parse_Amf0_Serializer
+ */
+ public function writeAmf3TypeMarker(&$data)
+ {
+ require_once 'Zend/Amf/Parse/Amf3/Serializer.php';
+ $serializer = new Zend_Amf_Parse_Amf3_Serializer($this->_stream);
+ $serializer->writeTypeMarker($data);
+ return $this;
+ }
+
+ /**
+ * Find if the class name is a class mapped name and return the
+ * respective classname if it is.
+ *
+ * @param object $object
+ * @return false|string $className
+ */
+ protected function getClassName($object)
+ {
+ require_once 'Zend/Amf/Parse/TypeLoader.php';
+ //Check to see if the object is a typed object and we need to change
+ $className = '';
+ switch (true) {
+ // the return class mapped name back to actionscript class name.
+ case Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object)):
+ $className = Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object));
+ break;
+ // Check to see if the user has defined an explicit Action Script type.
+ case isset($object->_explicitType):
+ $className = $object->_explicitType;
+ break;
+ // Check if user has defined a method for accessing the Action Script type
+ case method_exists($object, 'getASClassName'):
+ $className = $object->getASClassName();
+ break;
+ // No return class name is set make it a generic object
+ case ($object instanceof stdClass):
+ $className = '';
+ break;
+ // By default, use object's class name
+ default:
+ $className = get_class($object);
+ break;
+ }
+ if(!$className == '') {
+ return $className;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/library/Zend/Amf/Parse/Amf3/Deserializer.php b/library/Zend/Amf/Parse/Amf3/Deserializer.php
new file mode 100644
index 0000000..ac06c07
--- /dev/null
+++ b/library/Zend/Amf/Parse/Amf3/Deserializer.php
@@ -0,0 +1,421 @@
+_stream->readByte();
+ }
+
+ switch($typeMarker) {
+ case Zend_Amf_Constants::AMF3_UNDEFINED:
+ return null;
+ case Zend_Amf_Constants::AMF3_NULL:
+ return null;
+ case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE:
+ return false;
+ case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE:
+ return true;
+ case Zend_Amf_Constants::AMF3_INTEGER:
+ return $this->readInteger();
+ case Zend_Amf_Constants::AMF3_NUMBER:
+ return $this->_stream->readDouble();
+ case Zend_Amf_Constants::AMF3_STRING:
+ return $this->readString();
+ case Zend_Amf_Constants::AMF3_DATE:
+ return $this->readDate();
+ case Zend_Amf_Constants::AMF3_ARRAY:
+ return $this->readArray();
+ case Zend_Amf_Constants::AMF3_OBJECT:
+ return $this->readObject();
+ case Zend_Amf_Constants::AMF3_XML:
+ case Zend_Amf_Constants::AMF3_XMLSTRING:
+ return $this->readXmlString();
+ case Zend_Amf_Constants::AMF3_BYTEARRAY:
+ return $this->readString();
+ default:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unsupported type marker: ' . $typeMarker);
+ }
+ }
+
+ /**
+ * Read and deserialize an integer
+ *
+ * AMF 3 represents smaller integers with fewer bytes using the most
+ * significant bit of each byte. The worst case uses 32-bits
+ * to represent a 29-bit number, which is what we would have
+ * done with no compression.
+ * - 0x00000000 - 0x0000007F : 0xxxxxxx
+ * - 0x00000080 - 0x00003FFF : 1xxxxxxx 0xxxxxxx
+ * - 0x00004000 - 0x001FFFFF : 1xxxxxxx 1xxxxxxx 0xxxxxxx
+ * - 0x00200000 - 0x3FFFFFFF : 1xxxxxxx 1xxxxxxx 1xxxxxxx xxxxxxxx
+ * - 0x40000000 - 0xFFFFFFFF : throw range exception
+ *
+ * 0x04 -> integer type code, followed by up to 4 bytes of data.
+ *
+ * Parsing integers on OSFlash for the AMF3 integer data format:
+ * @link http://osflash.org/amf3/parsing_integers
+ * @return int|float
+ */
+ public function readInteger()
+ {
+ $count = 1;
+ $intReference = $this->_stream->readByte();
+ $result = 0;
+ while ((($intReference & 0x80) != 0) && $count < 4) {
+ $result <<= 7;
+ $result |= ($intReference & 0x7f);
+ $intReference = $this->_stream->readByte();
+ $count++;
+ }
+ if ($count < 4) {
+ $result <<= 7;
+ $result |= $intReference;
+ } else {
+ // Use all 8 bits from the 4th byte
+ $result <<= 8;
+ $result |= $intReference;
+
+ // Check if the integer should be negative
+ if (($result & 0x10000000) != 0) {
+ //and extend the sign bit
+ $result |= ~0xFFFFFFF;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Read and deserialize a string
+ *
+ * Strings can be sent as a reference to a previously
+ * occurring String by using an index to the implicit string reference table.
+ * Strings are encoding using UTF-8 - however the header may either
+ * describe a string literal or a string reference.
+ *
+ * - string = 0x06 string-data
+ * - string-data = integer-data [ modified-utf-8 ]
+ * - modified-utf-8 = *OCTET
+ *
+ * @return String
+ */
+ public function readString()
+ {
+ $stringReference = $this->readInteger();
+
+ //Check if this is a reference string
+ if (($stringReference & 0x01) == 0) {
+ // reference string
+ $stringReference = $stringReference >> 1;
+ if ($stringReference >= count($this->_referenceStrings)) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Undefined string reference: ' . $stringReference);
+ }
+ // reference string found
+ return $this->_referenceStrings[$stringReference];
+ }
+
+ $length = $stringReference >> 1;
+ if ($length) {
+ $string = $this->_stream->readBytes($length);
+ $this->_referenceStrings[] = $string;
+ } else {
+ $string = "";
+ }
+ return $string;
+ }
+
+ /**
+ * Read and deserialize a date
+ *
+ * Data is the number of milliseconds elapsed since the epoch
+ * of midnight, 1st Jan 1970 in the UTC time zone.
+ * Local time zone information is not sent to flash.
+ *
+ * - date = 0x08 integer-data [ number-data ]
+ *
+ * @return Zend_Date
+ */
+ public function readDate()
+ {
+ $dateReference = $this->readInteger();
+ if (($dateReference & 0x01) == 0) {
+ $dateReference = $dateReference >> 1;
+ if ($dateReference>=count($this->_referenceObjects)) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Undefined date reference: ' . $dateReference);
+ }
+ return $this->_referenceObjects[$dateReference];
+ }
+
+ $timestamp = floor($this->_stream->readDouble() / 1000);
+
+ require_once 'Zend/Date.php';
+ $dateTime = new Zend_Date($timestamp);
+ $this->_referenceObjects[] = $dateTime;
+ return $dateTime;
+ }
+
+ /**
+ * Read amf array to PHP array
+ *
+ * - array = 0x09 integer-data ( [ 1OCTET *amf3-data ] | [OCTET *amf3-data 1] | [ OCTET *amf-data ] )
+ *
+ * @return array
+ */
+ public function readArray()
+ {
+ $arrayReference = $this->readInteger();
+ if (($arrayReference & 0x01)==0){
+ $arrayReference = $arrayReference >> 1;
+ if ($arrayReference>=count($this->_referenceObjects)) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unknow array reference: ' . $arrayReference);
+ }
+ return $this->_referenceObjects[$arrayReference];
+ }
+
+ // Create a holder for the array in the reference list
+ $data = array();
+ $this->_referenceObjects[] =& $data;
+ $key = $this->readString();
+
+ // Iterating for string based keys.
+ while ($key != '') {
+ $data[$key] = $this->readTypeMarker();
+ $key = $this->readString();
+ }
+
+ $arrayReference = $arrayReference >>1;
+
+ //We have a dense array
+ for ($i=0; $i < $arrayReference; $i++) {
+ $data[] = $this->readTypeMarker();
+ }
+
+ return $data;
+ }
+
+ /**
+ * Read an object from the AMF stream and convert it into a PHP object
+ *
+ * @todo Rather than using an array of traitsInfo create Zend_Amf_Value_TraitsInfo
+ * @return object|array
+ */
+ public function readObject()
+ {
+ $traitsInfo = $this->readInteger();
+ $storedObject = ($traitsInfo & 0x01)==0;
+ $traitsInfo = $traitsInfo >> 1;
+
+ // Check if the Object is in the stored Objects reference table
+ if ($storedObject) {
+ $ref = $traitsInfo;
+ if (!isset($this->_referenceObjects[$ref])) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unknown Object reference: ' . $ref);
+ }
+ $returnObject = $this->_referenceObjects[$ref];
+ } else {
+ // Check if the Object is in the stored Definitions reference table
+ $storedClass = ($traitsInfo & 0x01) == 0;
+ $traitsInfo = $traitsInfo >> 1;
+ if ($storedClass) {
+ $ref = $traitsInfo;
+ if (!isset($this->_referenceDefinitions[$ref])) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unknows Definition reference: '. $ref);
+ }
+ // Populate the reference attributes
+ $className = $this->_referenceDefinitions[$ref]['className'];
+ $encoding = $this->_referenceDefinitions[$ref]['encoding'];
+ $propertyNames = $this->_referenceDefinitions[$ref]['propertyNames'];
+ } else {
+ // The class was not in the reference tables. Start reading rawdata to build traits.
+ // Create a traits table. Zend_Amf_Value_TraitsInfo would be ideal
+ $className = $this->readString();
+ $encoding = $traitsInfo & 0x03;
+ $propertyNames = array();
+ $traitsInfo = $traitsInfo >> 2;
+ }
+
+ // We now have the object traits defined in variables. Time to go to work:
+ if (!$className) {
+ // No class name generic object
+ $returnObject = new stdClass();
+ } else {
+ // Defined object
+ // Typed object lookup against registered classname maps
+ if ($loader = Zend_Amf_Parse_TypeLoader::loadType($className)) {
+ $returnObject = new $loader();
+ } else {
+ //user defined typed object
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Typed object not found: '. $className . ' ');
+ }
+ }
+
+ // Add the Object to the reference table
+ $this->_referenceObjects[] = $returnObject;
+
+ $properties = array(); // clear value
+ // Check encoding types for additional processing.
+ switch ($encoding) {
+ case (Zend_Amf_Constants::ET_EXTERNAL):
+ // Externalizable object such as {ArrayCollection} and {ObjectProxy}
+ if (!$storedClass) {
+ $this->_referenceDefinitions[] = array(
+ 'className' => $className,
+ 'encoding' => $encoding,
+ 'propertyNames' => $propertyNames,
+ );
+ }
+ $returnObject->externalizedData = $this->readTypeMarker();
+ break;
+ case (Zend_Amf_Constants::ET_DYNAMIC):
+ // used for Name-value encoding
+ if (!$storedClass) {
+ $this->_referenceDefinitions[] = array(
+ 'className' => $className,
+ 'encoding' => $encoding,
+ 'propertyNames' => $propertyNames,
+ );
+ }
+ // not a reference object read name value properties from byte stream
+ do {
+ $property = $this->readString();
+ if ($property != "") {
+ $propertyNames[] = $property;
+ $properties[$property] = $this->readTypeMarker();
+ }
+ } while ($property !="");
+ break;
+ default:
+ // basic property list object.
+ if (!$storedClass) {
+ $count = $traitsInfo; // Number of properties in the list
+ for($i=0; $i< $count; $i++) {
+ $propertyNames[] = $this->readString();
+ }
+ // Add a reference to the class.
+ $this->_referenceDefinitions[] = array(
+ 'className' => $className,
+ 'encoding' => $encoding,
+ 'propertyNames' => $propertyNames,
+ );
+ }
+ foreach ($propertyNames as $property) {
+ $properties[$property] = $this->readTypeMarker();
+ }
+ break;
+ }
+
+ // Add properties back to the return object.
+ foreach($properties as $key=>$value) {
+ if($key) {
+ $returnObject->$key = $value;
+ }
+ }
+
+
+ }
+
+ if ($returnObject instanceof Zend_Amf_Value_Messaging_ArrayCollection) {
+ if (isset($returnObject->externalizedData)) {
+ $returnObject = $returnObject->externalizedData;
+ } else {
+ $returnObject = get_object_vars($returnObject);
+ }
+ }
+
+ return $returnObject;
+ }
+
+ /**
+ * Convert XML to SimpleXml
+ * If user wants DomDocument they can use dom_import_simplexml
+ *
+ * @return SimpleXml Object
+ */
+ public function readXmlString()
+ {
+ $xmlReference = $this->readInteger();
+ $length = $xmlReference >> 1;
+ $string = $this->_stream->readBytes($length);
+ return simplexml_load_string($string);
+ }
+}
diff --git a/library/Zend/Amf/Parse/Amf3/Serializer.php b/library/Zend/Amf/Parse/Amf3/Serializer.php
new file mode 100644
index 0000000..9913a83
--- /dev/null
+++ b/library/Zend/Amf/Parse/Amf3/Serializer.php
@@ -0,0 +1,534 @@
+_stream->writeByte($markerType);
+
+ switch ($markerType) {
+ case Zend_Amf_Constants::AMF3_NULL:
+ break;
+ case Zend_Amf_Constants::AMF3_BOOLEAN_FALSE:
+ break;
+ case Zend_Amf_Constants::AMF3_BOOLEAN_TRUE:
+ break;
+ case Zend_Amf_Constants::AMF3_INTEGER:
+ $this->writeInteger($data);
+ break;
+ case Zend_Amf_Constants::AMF3_NUMBER:
+ $this->_stream->writeDouble($data);
+ break;
+ case Zend_Amf_Constants::AMF3_STRING:
+ $this->writeString($data);
+ break;
+ case Zend_Amf_Constants::AMF3_DATE:
+ $this->writeDate($data);
+ break;
+ case Zend_Amf_Constants::AMF3_ARRAY:
+ $this->writeArray($data);
+ break;
+ case Zend_Amf_Constants::AMF3_OBJECT:
+ $this->writeObject($data);
+ break;
+ case Zend_Amf_Constants::AMF3_BYTEARRAY:
+ $this->writeByteArray($data);
+ break;
+ case Zend_Amf_Constants::AMF3_XMLSTRING;
+ $this->writeXml($data);
+ break;
+ default:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unknown Type Marker: ' . $markerType);
+ }
+ } else {
+ // Detect Type Marker
+ if (is_resource($data)) {
+ $data = Zend_Amf_Parse_TypeLoader::handleResource($data);
+ }
+ switch (true) {
+ case (null === $data):
+ $markerType = Zend_Amf_Constants::AMF3_NULL;
+ break;
+ case (is_bool($data)):
+ if ($data){
+ $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_TRUE;
+ } else {
+ $markerType = Zend_Amf_Constants::AMF3_BOOLEAN_FALSE;
+ }
+ break;
+ case (is_int($data)):
+ if (($data > 0xFFFFFFF) || ($data < -268435456)) {
+ $markerType = Zend_Amf_Constants::AMF3_NUMBER;
+ } else {
+ $markerType = Zend_Amf_Constants::AMF3_INTEGER;
+ }
+ break;
+ case (is_float($data)):
+ $markerType = Zend_Amf_Constants::AMF3_NUMBER;
+ break;
+ case (is_string($data)):
+ $markerType = Zend_Amf_Constants::AMF3_STRING;
+ break;
+ case (is_array($data)):
+ $markerType = Zend_Amf_Constants::AMF3_ARRAY;
+ break;
+ case (is_object($data)):
+ // Handle object types.
+ if (($data instanceof DateTime) || ($data instanceof Zend_Date)) {
+ $markerType = Zend_Amf_Constants::AMF3_DATE;
+ } else if ($data instanceof Zend_Amf_Value_ByteArray) {
+ $markerType = Zend_Amf_Constants::AMF3_BYTEARRAY;
+ } else if (($data instanceof DOMDocument) || ($data instanceof SimpleXMLElement)) {
+ $markerType = Zend_Amf_Constants::AMF3_XMLSTRING;
+ } else {
+ $markerType = Zend_Amf_Constants::AMF3_OBJECT;
+ }
+ break;
+ default:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unsupported data type: ' . gettype($data));
+ }
+ $this->writeTypeMarker($data, $markerType);
+ }
+ }
+
+ /**
+ * Write an AMF3 integer
+ *
+ * @param int|float $data
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ public function writeInteger($int)
+ {
+ if (($int & 0xffffff80) == 0) {
+ $this->_stream->writeByte($int & 0x7f);
+ return $this;
+ }
+
+ if (($int & 0xffffc000) == 0 ) {
+ $this->_stream->writeByte(($int >> 7 ) | 0x80);
+ $this->_stream->writeByte($int & 0x7f);
+ return $this;
+ }
+
+ if (($int & 0xffe00000) == 0) {
+ $this->_stream->writeByte(($int >> 14 ) | 0x80);
+ $this->_stream->writeByte(($int >> 7 ) | 0x80);
+ $this->_stream->writeByte($int & 0x7f);
+ return $this;
+ }
+
+ $this->_stream->writeByte(($int >> 22 ) | 0x80);
+ $this->_stream->writeByte(($int >> 15 ) | 0x80);
+ $this->_stream->writeByte(($int >> 8 ) | 0x80);
+ $this->_stream->writeByte($int & 0xff);
+ return $this;
+ }
+
+ /**
+ * Send string to output stream, without trying to reference it.
+ * The string is prepended with strlen($string) << 1 | 0x01
+ *
+ * @param string $string
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ protected function writeBinaryString(&$string){
+ $ref = strlen($string) << 1 | 0x01;
+ $this->writeInteger($ref);
+ $this->_stream->writeBytes($string);
+
+ return $this;
+ }
+
+ /**
+ * Send string to output stream
+ *
+ * @param string $string
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ public function writeString(&$string)
+ {
+ $len = strlen($string);
+ if(!$len){
+ $this->writeInteger(0x01);
+ return $this;
+ }
+
+ $ref = array_key_exists($string, $this->_referenceStrings)
+ ? $this->_referenceStrings[$string]
+ : false;
+ if ($ref === false){
+ $this->_referenceStrings[$string] = count($this->_referenceStrings);
+ $this->writeBinaryString($string);
+ } else {
+ $ref <<= 1;
+ $this->writeInteger($ref);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Send ByteArray to output stream
+ *
+ * @param string|Zend_Amf_Value_ByteArray $data
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ public function writeByteArray(&$data)
+ {
+ if ($this->writeObjectReference($data)) {
+ return $this;
+ }
+
+ if (is_string($data)) {
+ //nothing to do
+ } else if ($data instanceof Zend_Amf_Value_ByteArray) {
+ $data = $data->getData();
+ } else {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Invalid ByteArray specified; must be a string or Zend_Amf_Value_ByteArray');
+ }
+
+ $this->writeBinaryString($data);
+
+ return $this;
+ }
+
+ /**
+ * Send xml to output stream
+ *
+ * @param DOMDocument|SimpleXMLElement $xml
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ public function writeXml($xml)
+ {
+ if ($this->writeObjectReference($xml)) {
+ return $this;
+ }
+
+ if(is_string($xml)) {
+ //nothing to do
+ } else if ($xml instanceof DOMDocument) {
+ $xml = $xml->saveXml();
+ } else if ($xml instanceof SimpleXMLElement) {
+ $xml = $xml->asXML();
+ } else {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Invalid xml specified; must be a DOMDocument or SimpleXMLElement');
+ }
+
+ $this->writeBinaryString($xml);
+
+ return $this;
+ }
+
+ /**
+ * Convert DateTime/Zend_Date to AMF date
+ *
+ * @param DateTime|Zend_Date $date
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ public function writeDate($date)
+ {
+ if ($this->writeObjectReference($date)) {
+ return $this;
+ }
+
+ if ($date instanceof DateTime) {
+ $dateString = $date->format('U') * 1000;
+ } elseif ($date instanceof Zend_Date) {
+ $dateString = $date->toString('U') * 1000;
+ } else {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Invalid date specified; must be a string DateTime or Zend_Date object');
+ }
+
+ $this->writeInteger(0x01);
+ // write time to stream minus milliseconds
+ $this->_stream->writeDouble($dateString);
+ return $this;
+ }
+
+ /**
+ * Write a PHP array back to the amf output stream
+ *
+ * @param array $array
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ public function writeArray(&$array)
+ {
+ // arrays aren't reference here but still counted
+ $this->_referenceObjects[] = $array;
+
+ // have to seperate mixed from numberic keys.
+ $numeric = array();
+ $string = array();
+ foreach ($array as $key => &$value) {
+ if (is_int($key)) {
+ $numeric[] = $value;
+ } else {
+ $string[$key] = $value;
+ }
+ }
+
+ // write the preamble id of the array
+ $length = count($numeric);
+ $id = ($length << 1) | 0x01;
+ $this->writeInteger($id);
+
+ //Write the mixed type array to the output stream
+ foreach($string as $key => &$value) {
+ $this->writeString($key)
+ ->writeTypeMarker($value);
+ }
+ $this->writeString($this->_strEmpty);
+
+ // Write the numeric array to ouput stream
+ foreach($numeric as &$value) {
+ $this->writeTypeMarker($value);
+ }
+ return $this;
+ }
+
+ /**
+ * Check if the given object is in the reference table, write the reference if it exists,
+ * otherwise add the object to the reference table
+ *
+ * @param mixed $object object reference to check for reference
+ * @param mixed $objectByVal object to check for reference
+ * @return Boolean true, if the reference was written, false otherwise
+ */
+ protected function writeObjectReference(&$object, $objectByVal = false)
+ {
+ // Workaround for PHP5 with E_STRICT enabled complaining about "Only
+ // variables should be passed by reference"
+ if ((null === $object) && ($objectByVal !== false)) {
+ $object = &$objectByVal;
+ }
+
+ $hash = spl_object_hash($object);
+ $ref = array_key_exists($hash, $this->_referenceObjects)
+ ? $this->_referenceObjects[$hash]
+ : false;
+
+ // quickly handle object references
+ if ($ref !== false){
+ $ref <<= 1;
+ $this->writeInteger($ref);
+ return true;
+ }
+ $this->_referenceObjects[$hash] = count($this->_referenceObjects);
+ return false;
+ }
+
+ /**
+ * Write object to ouput stream
+ *
+ * @param mixed $data
+ * @return Zend_Amf_Parse_Amf3_Serializer
+ */
+ public function writeObject($object)
+ {
+ if($this->writeObjectReference($object)){
+ return $this;
+ }
+
+ $className = '';
+
+ //Check to see if the object is a typed object and we need to change
+ switch (true) {
+ // the return class mapped name back to actionscript class name.
+ case ($className = Zend_Amf_Parse_TypeLoader::getMappedClassName(get_class($object))):
+ break;
+
+ // Check to see if the user has defined an explicit Action Script type.
+ case isset($object->_explicitType):
+ $className = $object->_explicitType;
+ break;
+
+ // Check if user has defined a method for accessing the Action Script type
+ case method_exists($object, 'getASClassName'):
+ $className = $object->getASClassName();
+ break;
+
+ // No return class name is set make it a generic object
+ case ($object instanceof stdClass):
+ $className = '';
+ break;
+
+ // By default, use object's class name
+ default:
+ $className = get_class($object);
+ break;
+ }
+
+ $writeTraits = true;
+
+ //check to see, if we have a corresponding definition
+ if(array_key_exists($className, $this->_referenceDefinitions)){
+ $traitsInfo = $this->_referenceDefinitions[$className]['id'];
+ $encoding = $this->_referenceDefinitions[$className]['encoding'];
+ $propertyNames = $this->_referenceDefinitions[$className]['propertyNames'];
+
+ $traitsInfo = ($traitsInfo << 2) | 0x01;
+
+ $writeTraits = false;
+ } else {
+ $propertyNames = array();
+
+ if($className == ''){
+ //if there is no className, we interpret the class as dynamic without any sealed members
+ $encoding = Zend_Amf_Constants::ET_DYNAMIC;
+ } else {
+ $encoding = Zend_Amf_Constants::ET_PROPLIST;
+
+ foreach($object as $key => $value) {
+ if( $key[0] != "_") {
+ $propertyNames[] = $key;
+ }
+ }
+ }
+
+ $this->_referenceDefinitions[$className] = array(
+ 'id' => count($this->_referenceDefinitions),
+ 'encoding' => $encoding,
+ 'propertyNames' => $propertyNames,
+ );
+
+ $traitsInfo = Zend_Amf_Constants::AMF3_OBJECT_ENCODING;
+ $traitsInfo |= $encoding << 2;
+ $traitsInfo |= (count($propertyNames) << 4);
+ }
+
+ $this->writeInteger($traitsInfo);
+
+ if($writeTraits){
+ $this->writeString($className);
+ foreach ($propertyNames as $value) {
+ $this->writeString($value);
+ }
+ }
+
+ try {
+ switch($encoding) {
+ case Zend_Amf_Constants::ET_PROPLIST:
+ //Write the sealed values to the output stream.
+ foreach ($propertyNames as $key) {
+ $this->writeTypeMarker($object->$key);
+ }
+ break;
+ case Zend_Amf_Constants::ET_DYNAMIC:
+ //Write the sealed values to the output stream.
+ foreach ($propertyNames as $key) {
+ $this->writeTypeMarker($object->$key);
+ }
+
+ //Write remaining properties
+ foreach($object as $key => $value){
+ if(!in_array($key,$propertyNames) && $key[0] != "_"){
+ $this->writeString($key);
+ $this->writeTypeMarker($value);
+ }
+ }
+
+ //Write an empty string to end the dynamic part
+ $this->writeString($this->_strEmpty);
+ break;
+ case Zend_Amf_Constants::ET_EXTERNAL:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('External Object Encoding not implemented');
+ break;
+ default:
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unknown Object Encoding type: ' . $encoding);
+ }
+ } catch (Exception $e) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unable to writeObject output: ' . $e->getMessage(), 0, $e);
+ }
+
+ return $this;
+ }
+}
diff --git a/library/Zend/Amf/Parse/Deserializer.php b/library/Zend/Amf/Parse/Deserializer.php
new file mode 100644
index 0000000..a9c5a76
--- /dev/null
+++ b/library/Zend/Amf/Parse/Deserializer.php
@@ -0,0 +1,65 @@
+_stream = $stream;
+ }
+
+ /**
+ * Checks for AMF marker types and calls the appropriate methods
+ * for deserializing those marker types. Markers are the data type of
+ * the following value.
+ *
+ * @param int $typeMarker
+ * @return mixed Whatever the data type is of the marker in php
+ */
+ public abstract function readTypeMarker($markerType = null);
+}
diff --git a/library/Zend/Amf/Parse/InputStream.php b/library/Zend/Amf/Parse/InputStream.php
new file mode 100644
index 0000000..190758e
--- /dev/null
+++ b/library/Zend/Amf/Parse/InputStream.php
@@ -0,0 +1,39 @@
+ Value is Mysql type (exact string) => PHP type
+ */
+ static public $fieldTypes = array(
+ "int" => "int",
+ "timestamp" => "int",
+ "year" => "int",
+ "real" => "float",
+ );
+ /**
+ * Parse resource into array
+ *
+ * @param resource $resource
+ * @return array
+ */
+ public function parse($resource) {
+ $result = array();
+ $fieldcnt = mysql_num_fields($resource);
+ $fields_transform = array();
+ for($i=0;$i<$fieldcnt;$i++) {
+ $type = mysql_field_type($resource, $i);
+ if(isset(self::$fieldTypes[$type])) {
+ $fields_transform[mysql_field_name($resource, $i)] = self::$fieldTypes[$type];
+ }
+ }
+
+ while($row = mysql_fetch_object($resource)) {
+ foreach($fields_transform as $fieldname => $fieldtype) {
+ settype($row->$fieldname, $fieldtype);
+ }
+ $result[] = $row;
+ }
+ return $result;
+ }
+}
diff --git a/library/Zend/Amf/Parse/Resource/MysqliResult.php b/library/Zend/Amf/Parse/Resource/MysqliResult.php
new file mode 100644
index 0000000..a5124b3
--- /dev/null
+++ b/library/Zend/Amf/Parse/Resource/MysqliResult.php
@@ -0,0 +1,128 @@
+ "MYSQLI_TYPE_DECIMAL",
+ 1 => "MYSQLI_TYPE_TINYINT",
+ 2 => "MYSQLI_TYPE_SMALLINT",
+ 3 => "MYSQLI_TYPE_INTEGER",
+ 4 => "MYSQLI_TYPE_FLOAT",
+ 5 => "MYSQLI_TYPE_DOUBLE",
+ 7 => "MYSQLI_TYPE_TIMESTAMP",
+ 8 => "MYSQLI_TYPE_BIGINT",
+ 9 => "MYSQLI_TYPE_MEDIUMINT",
+ 10 => "MYSQLI_TYPE_DATE",
+ 11 => "MYSQLI_TYPE_TIME",
+ 12 => "MYSQLI_TYPE_DATETIME",
+ 13 => "MYSQLI_TYPE_YEAR",
+ 14 => "MYSQLI_TYPE_DATE",
+ 16 => "MYSQLI_TYPE_BIT",
+ 246 => "MYSQLI_TYPE_DECIMAL",
+ 247 => "MYSQLI_TYPE_ENUM",
+ 248 => "MYSQLI_TYPE_SET",
+ 249 => "MYSQLI_TYPE_TINYBLOB",
+ 250 => "MYSQLI_TYPE_MEDIUMBLOB",
+ 251 => "MYSQLI_TYPE_LONGBLOB",
+ 252 => "MYSQLI_TYPE_BLOB",
+ 253 => "MYSQLI_TYPE_VARCHAR",
+ 254 => "MYSQLI_TYPE_CHAR",
+ 255 => "MYSQLI_TYPE_GEOMETRY",
+ );
+
+ // Build an associative array for a type look up
+ static $mysqli_to_php = array(
+ "MYSQLI_TYPE_DECIMAL" => 'float',
+ "MYSQLI_TYPE_NEWDECIMAL" => 'float',
+ "MYSQLI_TYPE_BIT" => 'integer',
+ "MYSQLI_TYPE_TINYINT" => 'integer',
+ "MYSQLI_TYPE_SMALLINT" => 'integer',
+ "MYSQLI_TYPE_MEDIUMINT" => 'integer',
+ "MYSQLI_TYPE_BIGINT" => 'integer',
+ "MYSQLI_TYPE_INTEGER" => 'integer',
+ "MYSQLI_TYPE_FLOAT" => 'float',
+ "MYSQLI_TYPE_DOUBLE" => 'float',
+ "MYSQLI_TYPE_NULL" => 'null',
+ "MYSQLI_TYPE_TIMESTAMP" => 'string',
+ "MYSQLI_TYPE_INT24" => 'integer',
+ "MYSQLI_TYPE_DATE" => 'string',
+ "MYSQLI_TYPE_TIME" => 'string',
+ "MYSQLI_TYPE_DATETIME" => 'string',
+ "MYSQLI_TYPE_YEAR" => 'string',
+ "MYSQLI_TYPE_NEWDATE" => 'string',
+ "MYSQLI_TYPE_ENUM" => 'string',
+ "MYSQLI_TYPE_SET" => 'string',
+ "MYSQLI_TYPE_TINYBLOB" => 'object',
+ "MYSQLI_TYPE_MEDIUMBLOB" => 'object',
+ "MYSQLI_TYPE_LONGBLOB" => 'object',
+ "MYSQLI_TYPE_BLOB" => 'object',
+ "MYSQLI_TYPE_CHAR" => 'string',
+ "MYSQLI_TYPE_VARCHAR" => 'string',
+ "MYSQLI_TYPE_GEOMETRY" => 'object',
+ "MYSQLI_TYPE_BIT" => 'integer',
+ );
+
+ /**
+ * Parse resource into array
+ *
+ * @param resource $resource
+ * @return array
+ */
+ public function parse($resource) {
+
+ $result = array();
+ $fieldcnt = mysqli_num_fields($resource);
+
+
+ $fields_transform = array();
+
+ for($i=0;$i<$fieldcnt;$i++) {
+ $finfo = mysqli_fetch_field_direct($resource, $i);
+
+ if(isset(self::$mysqli_type[$finfo->type])) {
+ $fields_transform[$finfo->name] = self::$mysqli_to_php[self::$mysqli_type[$finfo->type]];
+ }
+ }
+
+ while($row = mysqli_fetch_assoc($resource)) {
+ foreach($fields_transform as $fieldname => $fieldtype) {
+ settype($row[$fieldname], $fieldtype);
+ }
+ $result[] = $row;
+ }
+ return $result;
+ }
+}
diff --git a/library/Zend/Amf/Parse/Resource/Stream.php b/library/Zend/Amf/Parse/Resource/Stream.php
new file mode 100644
index 0000000..29daa0d
--- /dev/null
+++ b/library/Zend/Amf/Parse/Resource/Stream.php
@@ -0,0 +1,42 @@
+_stream = $stream;
+ }
+
+ /**
+ * Find the PHP object type and convert it into an AMF object type
+ *
+ * @param mixed $content
+ * @param int $markerType
+ * @param mixed $contentByVal
+ * @return void
+ */
+ public abstract function writeTypeMarker(&$content, $markerType = null, $contentByVal = false);
+}
diff --git a/library/Zend/Amf/Parse/TypeLoader.php b/library/Zend/Amf/Parse/TypeLoader.php
new file mode 100644
index 0000000..3991688
--- /dev/null
+++ b/library/Zend/Amf/Parse/TypeLoader.php
@@ -0,0 +1,231 @@
+ 'Zend_Amf_Value_Messaging_AcknowledgeMessage',
+ 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_AsyncMessage',
+ 'flex.messaging.messages.CommandMessage' => 'Zend_Amf_Value_Messaging_CommandMessage',
+ 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_ErrorMessage',
+ 'flex.messaging.messages.RemotingMessage' => 'Zend_Amf_Value_Messaging_RemotingMessage',
+ 'flex.messaging.io.ArrayCollection' => 'Zend_Amf_Value_Messaging_ArrayCollection',
+ );
+
+ /**
+ * @var array Default class map
+ */
+ protected static $_defaultClassMap = array(
+ 'flex.messaging.messages.AcknowledgeMessage' => 'Zend_Amf_Value_Messaging_AcknowledgeMessage',
+ 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_AsyncMessage',
+ 'flex.messaging.messages.CommandMessage' => 'Zend_Amf_Value_Messaging_CommandMessage',
+ 'flex.messaging.messages.ErrorMessage' => 'Zend_Amf_Value_Messaging_ErrorMessage',
+ 'flex.messaging.messages.RemotingMessage' => 'Zend_Amf_Value_Messaging_RemotingMessage',
+ 'flex.messaging.io.ArrayCollection' => 'Zend_Amf_Value_Messaging_ArrayCollection',
+ );
+
+ /**
+ * @var Zend_Loader_PluginLoader_Interface
+ */
+ protected static $_resourceLoader = null;
+
+
+ /**
+ * Load the mapped class type into a callback.
+ *
+ * @param string $className
+ * @return object|false
+ */
+ public static function loadType($className)
+ {
+ $class = self::getMappedClassName($className);
+ if(!$class) {
+ $class = str_replace('.', '_', $className);
+ }
+ if (!class_exists($class)) {
+ return "stdClass";
+ }
+ return $class;
+ }
+
+ /**
+ * Looks up the supplied call name to its mapped class name
+ *
+ * @param string $className
+ * @return string
+ */
+ public static function getMappedClassName($className)
+ {
+ $mappedName = array_search($className, self::$classMap);
+
+ if ($mappedName) {
+ return $mappedName;
+ }
+
+ $mappedName = array_search($className, array_flip(self::$classMap));
+
+ if ($mappedName) {
+ return $mappedName;
+ }
+
+ return false;
+ }
+
+ /**
+ * Map PHP class names to ActionScript class names
+ *
+ * Allows users to map the class names of there action script classes
+ * to the equivelent php class name. Used in deserialization to load a class
+ * and serialiation to set the class name of the returned object.
+ *
+ * @param string $asClassName
+ * @param string $phpClassName
+ * @return void
+ */
+ public static function setMapping($asClassName, $phpClassName)
+ {
+ self::$classMap[$asClassName] = $phpClassName;
+ }
+
+ /**
+ * Reset type map
+ *
+ * @return void
+ */
+ public static function resetMap()
+ {
+ self::$classMap = self::$_defaultClassMap;
+ }
+
+ /**
+ * Set loader for resource type handlers
+ *
+ * @param Zend_Loader_PluginLoader_Interface $loader
+ */
+ public static function setResourceLoader(Zend_Loader_PluginLoader_Interface $loader)
+ {
+ self::$_resourceLoader = $loader;
+ }
+
+ /**
+ * Add directory to the list of places where to look for resource handlers
+ *
+ * @param string $prefix
+ * @param string $dir
+ */
+ public static function addResourceDirectory($prefix, $dir)
+ {
+ if(self::$_resourceLoader) {
+ self::$_resourceLoader->addPrefixPath($prefix, $dir);
+ }
+ }
+
+ /**
+ * Get plugin class that handles this resource
+ *
+ * @param resource $resource Resource type
+ * @return string Class name
+ */
+ public static function getResourceParser($resource)
+ {
+ if(self::$_resourceLoader) {
+ $type = preg_replace("/[^A-Za-z0-9_]/", " ", get_resource_type($resource));
+ $type = str_replace(" ","", ucwords($type));
+ return self::$_resourceLoader->load($type);
+ }
+ return false;
+ }
+
+ /**
+ * Convert resource to a serializable object
+ *
+ * @param resource $resource
+ * @return mixed
+ */
+ public static function handleResource($resource)
+ {
+ if(!self::$_resourceLoader) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unable to handle resources - resource plugin loader not set');
+ }
+ try {
+ while(is_resource($resource)) {
+ $resclass = self::getResourceParser($resource);
+ if(!$resclass) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Can not serialize resource type: '. get_resource_type($resource));
+ }
+ $parser = new $resclass();
+ if(is_callable(array($parser, 'parse'))) {
+ $resource = $parser->parse($resource);
+ } else {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception("Could not call parse() method on class $resclass");
+ }
+ }
+ return $resource;
+ } catch(Zend_Amf_Exception $e) {
+ throw new Zend_Amf_Exception($e->getMessage(), $e->getCode(), $e);
+ } catch(Exception $e) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Can not serialize resource type: '. get_resource_type($resource), 0, $e);
+ }
+ }
+}
diff --git a/library/Zend/Amf/Request.php b/library/Zend/Amf/Request.php
new file mode 100644
index 0000000..e982efd
--- /dev/null
+++ b/library/Zend/Amf/Request.php
@@ -0,0 +1,251 @@
+_inputStream = new Zend_Amf_Parse_InputStream($request);
+ $this->_deserializer = new Zend_Amf_Parse_Amf0_Deserializer($this->_inputStream);
+ $this->readMessage($this->_inputStream);
+ return $this;
+ }
+
+ /**
+ * Takes the raw AMF input stream and converts it into valid PHP objects
+ *
+ * @param Zend_Amf_Parse_InputStream
+ * @return Zend_Amf_Request
+ */
+ public function readMessage(Zend_Amf_Parse_InputStream $stream)
+ {
+ $clientVersion = $stream->readUnsignedShort();
+ if (($clientVersion != Zend_Amf_Constants::AMF0_OBJECT_ENCODING)
+ && ($clientVersion != Zend_Amf_Constants::AMF3_OBJECT_ENCODING)
+ && ($clientVersion != Zend_Amf_Constants::FMS_OBJECT_ENCODING)
+ ) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unknown Player Version ' . $clientVersion);
+ }
+
+ $this->_bodies = array();
+ $this->_headers = array();
+ $headerCount = $stream->readInt();
+
+ // Iterate through the AMF envelope header
+ while ($headerCount--) {
+ $this->_headers[] = $this->readHeader();
+ }
+
+ // Iterate through the AMF envelope body
+ $bodyCount = $stream->readInt();
+ while ($bodyCount--) {
+ $this->_bodies[] = $this->readBody();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Deserialize a message header from the input stream.
+ *
+ * A message header is structured as:
+ * - NAME String
+ * - MUST UNDERSTAND Boolean
+ * - LENGTH Int
+ * - DATA Object
+ *
+ * @return Zend_Amf_Value_MessageHeader
+ */
+ public function readHeader()
+ {
+ $name = $this->_inputStream->readUTF();
+ $mustRead = (bool)$this->_inputStream->readByte();
+ $length = $this->_inputStream->readLong();
+
+ try {
+ $data = $this->_deserializer->readTypeMarker();
+ } catch (Exception $e) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unable to parse ' . $name . ' header data: ' . $e->getMessage() . ' '. $e->getLine(), 0, $e);
+ }
+
+ $header = new Zend_Amf_Value_MessageHeader($name, $mustRead, $data, $length);
+ return $header;
+ }
+
+ /**
+ * Deserialize a message body from the input stream
+ *
+ * @return Zend_Amf_Value_MessageBody
+ */
+ public function readBody()
+ {
+ $targetURI = $this->_inputStream->readUTF();
+ $responseURI = $this->_inputStream->readUTF();
+ $length = $this->_inputStream->readLong();
+
+ try {
+ $data = $this->_deserializer->readTypeMarker();
+ } catch (Exception $e) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Unable to parse ' . $targetURI . ' body data ' . $e->getMessage(), 0, $e);
+ }
+
+ // Check for AMF3 objectEncoding
+ if ($this->_deserializer->getObjectEncoding() == Zend_Amf_Constants::AMF3_OBJECT_ENCODING) {
+ /*
+ * When and AMF3 message is sent to the server it is nested inside
+ * an AMF0 array called Content. The following code gets the object
+ * out of the content array and sets it as the message data.
+ */
+ if(is_array($data) && $data[0] instanceof Zend_Amf_Value_Messaging_AbstractMessage){
+ $data = $data[0];
+ }
+
+ // set the encoding so we return our message in AMF3
+ $this->_objectEncoding = Zend_Amf_Constants::AMF3_OBJECT_ENCODING;
+ }
+
+ $body = new Zend_Amf_Value_MessageBody($targetURI, $responseURI, $data);
+ return $body;
+ }
+
+ /**
+ * Return an array of the body objects that were found in the amf request.
+ *
+ * @return array {target, response, length, content}
+ */
+ public function getAmfBodies()
+ {
+ return $this->_bodies;
+ }
+
+ /**
+ * Accessor to private array of message bodies.
+ *
+ * @param Zend_Amf_Value_MessageBody $message
+ * @return Zend_Amf_Request
+ */
+ public function addAmfBody(Zend_Amf_Value_MessageBody $message)
+ {
+ $this->_bodies[] = $message;
+ return $this;
+ }
+
+ /**
+ * Return an array of headers that were found in the amf request.
+ *
+ * @return array {operation, mustUnderstand, length, param}
+ */
+ public function getAmfHeaders()
+ {
+ return $this->_headers;
+ }
+
+ /**
+ * Return the either 0 or 3 for respect AMF version
+ *
+ * @return int
+ */
+ public function getObjectEncoding()
+ {
+ return $this->_objectEncoding;
+ }
+
+ /**
+ * Set the object response encoding
+ *
+ * @param mixed $int
+ * @return Zend_Amf_Request
+ */
+ public function setObjectEncoding($int)
+ {
+ $this->_objectEncoding = $int;
+ return $this;
+ }
+}
diff --git a/library/Zend/Amf/Request/Http.php b/library/Zend/Amf/Request/Http.php
new file mode 100644
index 0000000..ec98ccf
--- /dev/null
+++ b/library/Zend/Amf/Request/Http.php
@@ -0,0 +1,80 @@
+_rawRequest = $amfRequest;
+ $this->initialize($amfRequest);
+ } else {
+ echo '
Zend Amf Endpoint
' ;
+ }
+ }
+
+ /**
+ * Retrieve raw AMF Request
+ *
+ * @return string
+ */
+ public function getRawRequest()
+ {
+ return $this->_rawRequest;
+ }
+}
diff --git a/library/Zend/Amf/Response.php b/library/Zend/Amf/Response.php
new file mode 100644
index 0000000..ca6283b
--- /dev/null
+++ b/library/Zend/Amf/Response.php
@@ -0,0 +1,205 @@
+_outputStream = new Zend_Amf_Parse_OutputStream();
+ $this->writeMessage($this->_outputStream);
+ return $this;
+ }
+
+ /**
+ * Serialize the PHP data types back into Actionscript and
+ * create and AMF stream.
+ *
+ * @param Zend_Amf_Parse_OutputStream $stream
+ * @return Zend_Amf_Response
+ */
+ public function writeMessage(Zend_Amf_Parse_OutputStream $stream)
+ {
+ $objectEncoding = $this->_objectEncoding;
+
+ //Write encoding to start of stream. Preamble byte is written of two byte Unsigned Short
+ $stream->writeByte(0x00);
+ $stream->writeByte($objectEncoding);
+
+ // Loop through the AMF Headers that need to be returned.
+ $headerCount = count($this->_headers);
+ $stream->writeInt($headerCount);
+ foreach ($this->getAmfHeaders() as $header) {
+ $serializer = new Zend_Amf_Parse_Amf0_Serializer($stream);
+ $stream->writeUTF($header->name);
+ $stream->writeByte($header->mustRead);
+ $stream->writeLong(Zend_Amf_Constants::UNKNOWN_CONTENT_LENGTH);
+ if (is_object($header->data)) {
+ // Workaround for PHP5 with E_STRICT enabled complaining about
+ // "Only variables should be passed by reference"
+ $placeholder = null;
+ $serializer->writeTypeMarker($placeholder, null, $header->data);
+ } else {
+ $serializer->writeTypeMarker($header->data);
+ }
+ }
+
+ // loop through the AMF bodies that need to be returned.
+ $bodyCount = count($this->_bodies);
+ $stream->writeInt($bodyCount);
+ foreach ($this->_bodies as $body) {
+ $serializer = new Zend_Amf_Parse_Amf0_Serializer($stream);
+ $stream->writeUTF($body->getTargetURI());
+ $stream->writeUTF($body->getResponseURI());
+ $stream->writeLong(Zend_Amf_Constants::UNKNOWN_CONTENT_LENGTH);
+ $bodyData = $body->getData();
+ $markerType = ($this->_objectEncoding == Zend_Amf_Constants::AMF0_OBJECT_ENCODING) ? null : Zend_Amf_Constants::AMF0_AMF3;
+ if (is_object($bodyData)) {
+ // Workaround for PHP5 with E_STRICT enabled complaining about
+ // "Only variables should be passed by reference"
+ $placeholder = null;
+ $serializer->writeTypeMarker($placeholder, $markerType, $bodyData);
+ } else {
+ $serializer->writeTypeMarker($bodyData, $markerType);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return the output stream content
+ *
+ * @return string The contents of the output stream
+ */
+ public function getResponse()
+ {
+ return $this->_outputStream->getStream();
+ }
+
+ /**
+ * Return the output stream content
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->getResponse();
+ }
+
+ /**
+ * Add an AMF body to be sent to the Flash Player
+ *
+ * @param Zend_Amf_Value_MessageBody $body
+ * @return Zend_Amf_Response
+ */
+ public function addAmfBody(Zend_Amf_Value_MessageBody $body)
+ {
+ $this->_bodies[] = $body;
+ return $this;
+ }
+
+ /**
+ * Return an array of AMF bodies to be serialized
+ *
+ * @return array
+ */
+ public function getAmfBodies()
+ {
+ return $this->_bodies;
+ }
+
+ /**
+ * Add an AMF Header to be sent back to the flash player
+ *
+ * @param Zend_Amf_Value_MessageHeader $header
+ * @return Zend_Amf_Response
+ */
+ public function addAmfHeader(Zend_Amf_Value_MessageHeader $header)
+ {
+ $this->_headers[] = $header;
+ return $this;
+ }
+
+ /**
+ * Retrieve attached AMF message headers
+ *
+ * @return array Array of Zend_Amf_Value_MessageHeader objects
+ */
+ public function getAmfHeaders()
+ {
+ return $this->_headers;
+ }
+
+ /**
+ * Set the AMF encoding that will be used for serialization
+ *
+ * @param int $encoding
+ * @return Zend_Amf_Response
+ */
+ public function setObjectEncoding($encoding)
+ {
+ $this->_objectEncoding = $encoding;
+ return $this;
+ }
+}
diff --git a/library/Zend/Amf/Response/Http.php b/library/Zend/Amf/Response/Http.php
new file mode 100644
index 0000000..ce20a2e
--- /dev/null
+++ b/library/Zend/Amf/Response/Http.php
@@ -0,0 +1,51 @@
+ method pairs
+ * @var array
+ */
+ protected $_table = array();
+
+ /**
+ *
+ * @var bool session flag; whether or not to add a session to each response.
+ */
+ protected $_session = false;
+
+ /**
+ * Namespace allows all AMF calls to not clobber other PHP session variables
+ * @var Zend_Session_NameSpace default session namespace zend_amf
+ */
+ protected $_sesionNamespace = 'zend_amf';
+
+ /**
+ * Set the default session.name if php_
+ * @var string
+ */
+ protected $_sessionName = 'PHPSESSID';
+
+ /**
+ * Authentication handler object
+ *
+ * @var Zend_Amf_Auth_Abstract
+ */
+ protected $_auth;
+ /**
+ * ACL handler object
+ *
+ * @var Zend_Acl
+ */
+ protected $_acl;
+ /**
+ * The server constructor
+ */
+ public function __construct()
+ {
+ Zend_Amf_Parse_TypeLoader::setResourceLoader(new Zend_Loader_PluginLoader(array("Zend_Amf_Parse_Resource" => "Zend/Amf/Parse/Resource")));
+ }
+
+ /**
+ * Set authentication adapter
+ *
+ * If the authentication adapter implements a "getAcl()" method, populate
+ * the ACL of this instance with it (if none exists already).
+ *
+ * @param Zend_Amf_Auth_Abstract $auth
+ * @return Zend_Amf_Server
+ */
+ public function setAuth(Zend_Amf_Auth_Abstract $auth)
+ {
+ $this->_auth = $auth;
+ if ((null === $this->getAcl()) && method_exists($auth, 'getAcl')) {
+ $this->setAcl($auth->getAcl());
+ }
+ return $this;
+ }
+ /**
+ * Get authentication adapter
+ *
+ * @return Zend_Amf_Auth_Abstract
+ */
+ public function getAuth()
+ {
+ return $this->_auth;
+ }
+
+ /**
+ * Set ACL adapter
+ *
+ * @param Zend_Acl $acl
+ * @return Zend_Amf_Server
+ */
+ public function setAcl(Zend_Acl $acl)
+ {
+ $this->_acl = $acl;
+ return $this;
+ }
+ /**
+ * Get ACL adapter
+ *
+ * @return Zend_Acl
+ */
+ public function getAcl()
+ {
+ return $this->_acl;
+ }
+
+ /**
+ * Set production flag
+ *
+ * @param bool $flag
+ * @return Zend_Amf_Server
+ */
+ public function setProduction($flag)
+ {
+ $this->_production = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Whether or not the server is in production
+ *
+ * @return bool
+ */
+ public function isProduction()
+ {
+ return $this->_production;
+ }
+
+ /**
+ * @param namespace of all incoming sessions defaults to Zend_Amf
+ * @return Zend_Amf_Server
+ */
+ public function setSession($namespace = 'Zend_Amf')
+ {
+ require_once 'Zend/Session.php';
+ $this->_session = true;
+ $this->_sesionNamespace = new Zend_Session_Namespace($namespace);
+ return $this;
+ }
+
+ /**
+ * Whether of not the server is using sessions
+ * @return bool
+ */
+ public function isSession()
+ {
+ return $this->_session;
+ }
+
+ /**
+ * Check if the ACL allows accessing the function or method
+ *
+ * @param string|object $object Object or class being accessed
+ * @param string $function Function or method being accessed
+ * @return unknown_type
+ */
+ protected function _checkAcl($object, $function)
+ {
+ if(!$this->_acl) {
+ return true;
+ }
+ if($object) {
+ $class = is_object($object)?get_class($object):$object;
+ if(!$this->_acl->has($class)) {
+ require_once 'Zend/Acl/Resource.php';
+ $this->_acl->add(new Zend_Acl_Resource($class));
+ }
+ $call = array($object, "initAcl");
+ if(is_callable($call) && !call_user_func($call, $this->_acl)) {
+ // if initAcl returns false, no ACL check
+ return true;
+ }
+ } else {
+ $class = null;
+ }
+
+ $auth = Zend_Auth::getInstance();
+ if($auth->hasIdentity()) {
+ $role = $auth->getIdentity()->role;
+ } else {
+ if($this->_acl->hasRole(Zend_Amf_Constants::GUEST_ROLE)) {
+ $role = Zend_Amf_Constants::GUEST_ROLE;
+ } else {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception("Unauthenticated access not allowed");
+ }
+ }
+ if($this->_acl->isAllowed($role, $class, $function)) {
+ return true;
+ } else {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception("Access not allowed");
+ }
+ }
+
+ /**
+ * Get PluginLoader for the Server
+ *
+ * @return Zend_Loader_PluginLoader
+ */
+ protected function getLoader()
+ {
+ if(empty($this->_loader)) {
+ require_once 'Zend/Loader/PluginLoader.php';
+ $this->_loader = new Zend_Loader_PluginLoader();
+ }
+ return $this->_loader;
+ }
+
+ /**
+ * Loads a remote class or method and executes the function and returns
+ * the result
+ *
+ * @param string $method Is the method to execute
+ * @param mixed $param values for the method
+ * @return mixed $response the result of executing the method
+ * @throws Zend_Amf_Server_Exception
+ */
+ protected function _dispatch($method, $params = null, $source = null)
+ {
+ if($source) {
+ if(($mapped = Zend_Amf_Parse_TypeLoader::getMappedClassName($source)) !== false) {
+ $source = $mapped;
+ }
+ }
+ $qualifiedName = empty($source) ? $method : $source . '.' . $method;
+
+ if (!isset($this->_table[$qualifiedName])) {
+ // if source is null a method that was not defined was called.
+ if ($source) {
+ $className = str_replace('.', '_', $source);
+ if(class_exists($className, false) && !isset($this->_classAllowed[$className])) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Can not call "' . $className . '" - use setClass()');
+ }
+ try {
+ $this->getLoader()->load($className);
+ } catch (Exception $e) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Class "' . $className . '" does not exist: '.$e->getMessage(), 0, $e);
+ }
+ // Add the new loaded class to the server.
+ $this->setClass($className, $source);
+ }
+
+ if (!isset($this->_table[$qualifiedName])) {
+ // Source is null or doesn't contain specified method
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Method "' . $method . '" does not exist');
+ }
+ }
+
+ $info = $this->_table[$qualifiedName];
+ $argv = $info->getInvokeArguments();
+
+ if (0 < count($argv)) {
+ $params = array_merge($params, $argv);
+ }
+
+ if ($info instanceof Zend_Server_Reflection_Function) {
+ $func = $info->getName();
+ $this->_checkAcl(null, $func);
+ $return = call_user_func_array($func, $params);
+ } elseif ($info instanceof Zend_Server_Reflection_Method) {
+ // Get class
+ $class = $info->getDeclaringClass()->getName();
+ if ('static' == $info->isStatic()) {
+ // for some reason, invokeArgs() does not work the same as
+ // invoke(), and expects the first argument to be an object.
+ // So, using a callback if the method is static.
+ $this->_checkAcl($class, $info->getName());
+ $return = call_user_func_array(array($class, $info->getName()), $params);
+ } else {
+ // Object methods
+ try {
+ $object = $info->getDeclaringClass()->newInstance();
+ } catch (Exception $e) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Error instantiating class ' . $class . ' to invoke method ' . $info->getName() . ': '.$e->getMessage(), 621, $e);
+ }
+ $this->_checkAcl($object, $info->getName());
+ $return = $info->invokeArgs($object, $params);
+ }
+ } else {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Method missing implementation ' . get_class($info));
+ }
+
+ return $return;
+ }
+
+ /**
+ * Handles each of the 11 different command message types.
+ *
+ * A command message is a flex.messaging.messages.CommandMessage
+ *
+ * @see Zend_Amf_Value_Messaging_CommandMessage
+ * @param Zend_Amf_Value_Messaging_CommandMessage $message
+ * @return Zend_Amf_Value_Messaging_AcknowledgeMessage
+ */
+ protected function _loadCommandMessage(Zend_Amf_Value_Messaging_CommandMessage $message)
+ {
+ require_once 'Zend/Amf/Value/Messaging/AcknowledgeMessage.php';
+ switch($message->operation) {
+ case Zend_Amf_Value_Messaging_CommandMessage::DISCONNECT_OPERATION :
+ case Zend_Amf_Value_Messaging_CommandMessage::CLIENT_PING_OPERATION :
+ $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
+ break;
+ case Zend_Amf_Value_Messaging_CommandMessage::LOGIN_OPERATION :
+ $data = explode(':', base64_decode($message->body));
+ $userid = $data[0];
+ $password = isset($data[1])?$data[1]:"";
+ if(empty($userid)) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Login failed: username not supplied');
+ }
+ if(!$this->_handleAuth($userid, $password)) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Authentication failed');
+ }
+ $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
+ break;
+ case Zend_Amf_Value_Messaging_CommandMessage::LOGOUT_OPERATION :
+ if($this->_auth) {
+ Zend_Auth::getInstance()->clearIdentity();
+ }
+ $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
+ break;
+ default :
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('CommandMessage::' . $message->operation . ' not implemented');
+ break;
+ }
+ return $return;
+ }
+
+ /**
+ * Create appropriate error message
+ *
+ * @param int $objectEncoding Current AMF encoding
+ * @param string $message Message that was being processed when error happened
+ * @param string $description Error description
+ * @param mixed $detail Detailed data about the error
+ * @param int $code Error code
+ * @param int $line Error line
+ * @return Zend_Amf_Value_Messaging_ErrorMessage|array
+ */
+ protected function _errorMessage($objectEncoding, $message, $description, $detail, $code, $line)
+ {
+ $return = null;
+ switch ($objectEncoding) {
+ case Zend_Amf_Constants::AMF0_OBJECT_ENCODING :
+ return array (
+ 'description' => ($this->isProduction ()) ? '' : $description,
+ 'detail' => ($this->isProduction ()) ? '' : $detail,
+ 'line' => ($this->isProduction ()) ? 0 : $line,
+ 'code' => $code
+ );
+ case Zend_Amf_Constants::AMF3_OBJECT_ENCODING :
+ require_once 'Zend/Amf/Value/Messaging/ErrorMessage.php';
+ $return = new Zend_Amf_Value_Messaging_ErrorMessage ( $message );
+ $return->faultString = $this->isProduction () ? '' : $description;
+ $return->faultCode = $code;
+ $return->faultDetail = $this->isProduction () ? '' : $detail;
+ break;
+ }
+ return $return;
+ }
+
+ /**
+ * Handle AMF authentication
+ *
+ * @param string $userid
+ * @param string $password
+ * @return boolean
+ */
+ protected function _handleAuth( $userid, $password)
+ {
+ if (!$this->_auth) {
+ return true;
+ }
+ $this->_auth->setCredentials($userid, $password);
+ $auth = Zend_Auth::getInstance();
+ $result = $auth->authenticate($this->_auth);
+ if ($result->isValid()) {
+ if (!$this->isSession()) {
+ $this->setSession();
+ }
+ return true;
+ } else {
+ // authentication failed, good bye
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception(
+ "Authentication failed: " . join("\n",
+ $result->getMessages()), $result->getCode());
+ }
+
+ }
+
+ /**
+ * Takes the deserialized AMF request and performs any operations.
+ *
+ * @todo should implement and SPL observer pattern for custom AMF headers
+ * @todo DescribeService support
+ * @param Zend_Amf_Request $request
+ * @return Zend_Amf_Response
+ * @throws Zend_Amf_server_Exception|Exception
+ */
+ protected function _handle(Zend_Amf_Request $request)
+ {
+ // Get the object encoding of the request.
+ $objectEncoding = $request->getObjectEncoding();
+
+ // create a response object to place the output from the services.
+ $response = $this->getResponse();
+
+ // set response encoding
+ $response->setObjectEncoding($objectEncoding);
+
+ // Authenticate, if we have credential headers
+ $error = false;
+ $headers = $request->getAmfHeaders();
+ if (isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER])
+ && isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid)
+ && isset($headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password)
+ ) {
+ try {
+ if ($this->_handleAuth(
+ $headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->userid,
+ $headers[Zend_Amf_Constants::CREDENTIALS_HEADER]->password
+ )) {
+ // use RequestPersistentHeader to clear credentials
+ $response->addAmfHeader(
+ new Zend_Amf_Value_MessageHeader(
+ Zend_Amf_Constants::PERSISTENT_HEADER,
+ false,
+ new Zend_Amf_Value_MessageHeader(
+ Zend_Amf_Constants::CREDENTIALS_HEADER,
+ false, null
+ )
+ )
+ );
+ }
+ } catch (Exception $e) {
+ // Error during authentication; report it
+ $error = $this->_errorMessage(
+ $objectEncoding,
+ '',
+ $e->getMessage(),
+ $e->getTraceAsString(),
+ $e->getCode(),
+ $e->getLine()
+ );
+ $responseType = Zend_AMF_Constants::STATUS_METHOD;
+ }
+ }
+
+ // Iterate through each of the service calls in the AMF request
+ foreach($request->getAmfBodies() as $body)
+ {
+ if ($error) {
+ // Error during authentication; just report it and be done
+ $responseURI = $body->getResponseURI() . $responseType;
+ $newBody = new Zend_Amf_Value_MessageBody($responseURI, null, $error);
+ $response->addAmfBody($newBody);
+ continue;
+ }
+ try {
+ switch ($objectEncoding) {
+ case Zend_Amf_Constants::AMF0_OBJECT_ENCODING:
+ // AMF0 Object Encoding
+ $targetURI = $body->getTargetURI();
+ $message = '';
+
+ // Split the target string into its values.
+ $source = substr($targetURI, 0, strrpos($targetURI, '.'));
+
+ if ($source) {
+ // Break off method name from namespace into source
+ $method = substr(strrchr($targetURI, '.'), 1);
+ $return = $this->_dispatch($method, $body->getData(), $source);
+ } else {
+ // Just have a method name.
+ $return = $this->_dispatch($targetURI, $body->getData());
+ }
+ break;
+ case Zend_Amf_Constants::AMF3_OBJECT_ENCODING:
+ default:
+ // AMF3 read message type
+ $message = $body->getData();
+ if ($message instanceof Zend_Amf_Value_Messaging_CommandMessage) {
+ // async call with command message
+ $return = $this->_loadCommandMessage($message);
+ } elseif ($message instanceof Zend_Amf_Value_Messaging_RemotingMessage) {
+ require_once 'Zend/Amf/Value/Messaging/AcknowledgeMessage.php';
+ $return = new Zend_Amf_Value_Messaging_AcknowledgeMessage($message);
+ $return->body = $this->_dispatch($message->operation, $message->body, $message->source);
+ } else {
+ // Amf3 message sent with netConnection
+ $targetURI = $body->getTargetURI();
+
+ // Split the target string into its values.
+ $source = substr($targetURI, 0, strrpos($targetURI, '.'));
+
+ if ($source) {
+ // Break off method name from namespace into source
+ $method = substr(strrchr($targetURI, '.'), 1);
+ $return = $this->_dispatch($method, $body->getData(), $source);
+ } else {
+ // Just have a method name.
+ $return = $this->_dispatch($targetURI, $body->getData());
+ }
+ }
+ break;
+ }
+ $responseType = Zend_AMF_Constants::RESULT_METHOD;
+ } catch (Exception $e) {
+ $return = $this->_errorMessage($objectEncoding, $message,
+ $e->getMessage(), $e->getTraceAsString(),$e->getCode(), $e->getLine());
+ $responseType = Zend_AMF_Constants::STATUS_METHOD;
+ }
+
+ $responseURI = $body->getResponseURI() . $responseType;
+ $newBody = new Zend_Amf_Value_MessageBody($responseURI, null, $return);
+ $response->addAmfBody($newBody);
+ }
+ // Add a session header to the body if session is requested.
+ if($this->isSession()) {
+ $currentID = session_id();
+ $joint = "?";
+ if(isset($_SERVER['QUERY_STRING'])) {
+ if(!strpos($_SERVER['QUERY_STRING'], $currentID) !== FALSE) {
+ if(strrpos($_SERVER['QUERY_STRING'], "?") !== FALSE) {
+ $joint = "&";
+ }
+ }
+ }
+
+ // create a new AMF message header with the session id as a variable.
+ $sessionValue = $joint . $this->_sessionName . "=" . $currentID;
+ $sessionHeader = new Zend_Amf_Value_MessageHeader(Zend_Amf_Constants::URL_APPEND_HEADER, false, $sessionValue);
+ $response->addAmfHeader($sessionHeader);
+ }
+
+ // serialize the response and return serialized body.
+ $response->finalize();
+ }
+
+ /**
+ * Handle an AMF call from the gateway.
+ *
+ * @param null|Zend_Amf_Request $request Optional
+ * @return Zend_Amf_Response
+ */
+ public function handle($request = null)
+ {
+ // Check if request was passed otherwise get it from the server
+ if ($request === null || !$request instanceof Zend_Amf_Request) {
+ $request = $this->getRequest();
+ } else {
+ $this->setRequest($request);
+ }
+ if ($this->isSession()) {
+ // Check if a session is being sent from the amf call
+ if (isset($_COOKIE[$this->_sessionName])) {
+ session_id($_COOKIE[$this->_sessionName]);
+ }
+ }
+
+ // Check for errors that may have happend in deserialization of Request.
+ try {
+ // Take converted PHP objects and handle service call.
+ // Serialize to Zend_Amf_response for output stream
+ $this->_handle($request);
+ $response = $this->getResponse();
+ } catch (Exception $e) {
+ // Handle any errors in the serialization and service calls.
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Handle error: ' . $e->getMessage() . ' ' . $e->getLine(), 0, $e);
+ }
+
+ // Return the Amf serialized output string
+ return $response;
+ }
+
+ /**
+ * Set request object
+ *
+ * @param string|Zend_Amf_Request $request
+ * @return Zend_Amf_Server
+ */
+ public function setRequest($request)
+ {
+ if (is_string($request) && class_exists($request)) {
+ $request = new $request();
+ if (!$request instanceof Zend_Amf_Request) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Invalid request class');
+ }
+ } elseif (!$request instanceof Zend_Amf_Request) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Invalid request object');
+ }
+ $this->_request = $request;
+ return $this;
+ }
+
+ /**
+ * Return currently registered request object
+ *
+ * @return null|Zend_Amf_Request
+ */
+ public function getRequest()
+ {
+ if (null === $this->_request) {
+ require_once 'Zend/Amf/Request/Http.php';
+ $this->setRequest(new Zend_Amf_Request_Http());
+ }
+
+ return $this->_request;
+ }
+
+ /**
+ * Public access method to private Zend_Amf_Server_Response reference
+ *
+ * @param string|Zend_Amf_Server_Response $response
+ * @return Zend_Amf_Server
+ */
+ public function setResponse($response)
+ {
+ if (is_string($response) && class_exists($response)) {
+ $response = new $response();
+ if (!$response instanceof Zend_Amf_Response) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Invalid response class');
+ }
+ } elseif (!$response instanceof Zend_Amf_Response) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Invalid response object');
+ }
+ $this->_response = $response;
+ return $this;
+ }
+
+ /**
+ * get a reference to the Zend_Amf_response instance
+ *
+ * @return Zend_Amf_Server_Response
+ */
+ public function getResponse()
+ {
+ if (null === ($response = $this->_response)) {
+ require_once 'Zend/Amf/Response/Http.php';
+ $this->setResponse(new Zend_Amf_Response_Http());
+ }
+ return $this->_response;
+ }
+
+ /**
+ * Attach a class or object to the server
+ *
+ * Class may be either a class name or an instantiated object. Reflection
+ * is done on the class or object to determine the available public
+ * methods, and each is attached to the server as and available method. If
+ * a $namespace has been provided, that namespace is used to prefix
+ * AMF service call.
+ *
+ * @param string|object $class
+ * @param string $namespace Optional
+ * @param mixed $arg Optional arguments to pass to a method
+ * @return Zend_Amf_Server
+ * @throws Zend_Amf_Server_Exception on invalid input
+ */
+ public function setClass($class, $namespace = '', $argv = null)
+ {
+ if (is_string($class) && !class_exists($class)){
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Invalid method or class');
+ } elseif (!is_string($class) && !is_object($class)) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Invalid method or class; must be a classname or object');
+ }
+
+ $argv = null;
+ if (2 < func_num_args()) {
+ $argv = array_slice(func_get_args(), 2);
+ }
+
+ // Use the class name as the name space by default.
+
+ if ($namespace == '') {
+ $namespace = is_object($class) ? get_class($class) : $class;
+ }
+
+ $this->_classAllowed[is_object($class) ? get_class($class) : $class] = true;
+
+ $this->_methods[] = Zend_Server_Reflection::reflectClass($class, $argv, $namespace);
+ $this->_buildDispatchTable();
+
+ return $this;
+ }
+
+ /**
+ * Attach a function to the server
+ *
+ * Additional arguments to pass to the function at dispatch may be passed;
+ * any arguments following the namespace will be aggregated and passed at
+ * dispatch time.
+ *
+ * @param string|array $function Valid callback
+ * @param string $namespace Optional namespace prefix
+ * @return Zend_Amf_Server
+ * @throws Zend_Amf_Server_Exception
+ */
+ public function addFunction($function, $namespace = '')
+ {
+ if (!is_string($function) && !is_array($function)) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Unable to attach function');
+ }
+
+ $argv = null;
+ if (2 < func_num_args()) {
+ $argv = array_slice(func_get_args(), 2);
+ }
+
+ $function = (array) $function;
+ foreach ($function as $func) {
+ if (!is_string($func) || !function_exists($func)) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Unable to attach function');
+ }
+ $this->_methods[] = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace);
+ }
+
+ $this->_buildDispatchTable();
+ return $this;
+ }
+
+
+ /**
+ * Creates an array of directories in which services can reside.
+ * TODO: add support for prefixes?
+ *
+ * @param string $dir
+ */
+ public function addDirectory($dir)
+ {
+ $this->getLoader()->addPrefixPath("", $dir);
+ }
+
+ /**
+ * Returns an array of directories that can hold services.
+ *
+ * @return array
+ */
+ public function getDirectory()
+ {
+ return $this->getLoader()->getPaths("");
+ }
+
+ /**
+ * (Re)Build the dispatch table
+ *
+ * The dispatch table consists of a an array of method name =>
+ * Zend_Server_Reflection_Function_Abstract pairs
+ *
+ * @return void
+ */
+ protected function _buildDispatchTable()
+ {
+ $table = array();
+ foreach ($this->_methods as $key => $dispatchable) {
+ if ($dispatchable instanceof Zend_Server_Reflection_Function_Abstract) {
+ $ns = $dispatchable->getNamespace();
+ $name = $dispatchable->getName();
+ $name = empty($ns) ? $name : $ns . '.' . $name;
+
+ if (isset($table[$name])) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
+ }
+ $table[$name] = $dispatchable;
+ continue;
+ }
+
+ if ($dispatchable instanceof Zend_Server_Reflection_Class) {
+ foreach ($dispatchable->getMethods() as $method) {
+ $ns = $method->getNamespace();
+ $name = $method->getName();
+ $name = empty($ns) ? $name : $ns . '.' . $name;
+
+ if (isset($table[$name])) {
+ require_once 'Zend/Amf/Server/Exception.php';
+ throw new Zend_Amf_Server_Exception('Duplicate method registered: ' . $name);
+ }
+ $table[$name] = $method;
+ continue;
+ }
+ }
+ }
+ $this->_table = $table;
+ }
+
+
+
+ /**
+ * Raise a server fault
+ *
+ * Unimplemented
+ *
+ * @param string|Exception $fault
+ * @return void
+ */
+ public function fault($fault = null, $code = 404)
+ {
+ }
+
+ /**
+ * Returns a list of registered methods
+ *
+ * Returns an array of dispatchables (Zend_Server_Reflection_Function,
+ * _Method, and _Class items).
+ *
+ * @return array
+ */
+ public function getFunctions()
+ {
+ return $this->_table;
+ }
+
+ /**
+ * Set server persistence
+ *
+ * Unimplemented
+ *
+ * @param mixed $mode
+ * @return void
+ */
+ public function setPersistence($mode)
+ {
+ }
+
+ /**
+ * Load server definition
+ *
+ * Unimplemented
+ *
+ * @param array $definition
+ * @return void
+ */
+ public function loadFunctions($definition)
+ {
+ }
+
+ /**
+ * Map ActionScript classes to PHP classes
+ *
+ * @param string $asClass
+ * @param string $phpClass
+ * @return Zend_Amf_Server
+ */
+ public function setClassMap($asClass, $phpClass)
+ {
+ require_once 'Zend/Amf/Parse/TypeLoader.php';
+ Zend_Amf_Parse_TypeLoader::setMapping($asClass, $phpClass);
+ return $this;
+ }
+
+ /**
+ * List all available methods
+ *
+ * Returns an array of method names.
+ *
+ * @return array
+ */
+ public function listMethods()
+ {
+ return array_keys($this->_table);
+ }
+}
diff --git a/library/Zend/Amf/Server/Exception.php b/library/Zend/Amf/Server/Exception.php
new file mode 100644
index 0000000..0b6bbc3
--- /dev/null
+++ b/library/Zend/Amf/Server/Exception.php
@@ -0,0 +1,37 @@
+_stream = $stream;
+ $this->_needle = 0;
+ $this->_streamLength = strlen($stream);
+ $this->_bigEndian = (pack('l', 1) === "\x00\x00\x00\x01");
+ }
+
+ /**
+ * Returns the current stream
+ *
+ * @return string
+ */
+ public function getStream()
+ {
+ return $this->_stream;
+ }
+
+ /**
+ * Read the number of bytes in a row for the length supplied.
+ *
+ * @todo Should check that there are enough bytes left in the stream we are about to read.
+ * @param int $length
+ * @return string
+ * @throws Zend_Amf_Exception for buffer underrun
+ */
+ public function readBytes($length)
+ {
+ if (($length + $this->_needle) > $this->_streamLength) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Buffer underrun at needle position: ' . $this->_needle . ' while requesting length: ' . $length);
+ }
+ $bytes = substr($this->_stream, $this->_needle, $length);
+ $this->_needle+= $length;
+ return $bytes;
+ }
+
+ /**
+ * Write any length of bytes to the stream
+ *
+ * Usually a string.
+ *
+ * @param string $bytes
+ * @return Zend_Amf_Util_BinaryStream
+ */
+ public function writeBytes($bytes)
+ {
+ $this->_stream.= $bytes;
+ return $this;
+ }
+
+ /**
+ * Reads a signed byte
+ *
+ * @return int Value is in the range of -128 to 127.
+ */
+ public function readByte()
+ {
+ if (($this->_needle + 1) > $this->_streamLength) {
+ require_once 'Zend/Amf/Exception.php';
+ throw new Zend_Amf_Exception('Buffer underrun at needle position: ' . $this->_needle . ' while requesting length: ' . $length);
+ }
+
+ return ord($this->_stream{$this->_needle++});
+ }
+
+ /**
+ * Writes the passed string into a signed byte on the stream.
+ *
+ * @param string $stream
+ * @return Zend_Amf_Util_BinaryStream
+ */
+ public function writeByte($stream)
+ {
+ $this->_stream.= pack('c', $stream);
+ return $this;
+ }
+
+ /**
+ * Reads a signed 32-bit integer from the data stream.
+ *
+ * @return int Value is in the range of -2147483648 to 2147483647
+ */
+ public function readInt()
+ {
+ return ($this->readByte() << 8) + $this->readByte();
+ }
+
+ /**
+ * Write an the integer to the output stream as a 32 bit signed integer
+ *
+ * @param int $stream
+ * @return Zend_Amf_Util_BinaryStream
+ */
+ public function writeInt($stream)
+ {
+ $this->_stream.= pack('n', $stream);
+ return $this;
+ }
+
+ /**
+ * Reads a UTF-8 string from the data stream
+ *
+ * @return string A UTF-8 string produced by the byte representation of characters
+ */
+ public function readUtf()
+ {
+ $length = $this->readInt();
+ return $this->readBytes($length);
+ }
+
+ /**
+ * Wite a UTF-8 string to the outputstream
+ *
+ * @param string $stream
+ * @return Zend_Amf_Util_BinaryStream
+ */
+ public function writeUtf($stream)
+ {
+ $this->writeInt(strlen($stream));
+ $this->_stream.= $stream;
+ return $this;
+ }
+
+
+ /**
+ * Read a long UTF string
+ *
+ * @return string
+ */
+ public function readLongUtf()
+ {
+ $length = $this->readLong();
+ return $this->readBytes($length);
+ }
+
+ /**
+ * Write a long UTF string to the buffer
+ *
+ * @param string $stream
+ * @return Zend_Amf_Util_BinaryStream
+ */
+ public function writeLongUtf($stream)
+ {
+ $this->writeLong(strlen($stream));
+ $this->_stream.= $stream;
+ }
+
+ /**
+ * Read a long numeric value
+ *
+ * @return double
+ */
+ public function readLong()
+ {
+ return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
+ }
+
+ /**
+ * Write long numeric value to output stream
+ *
+ * @param int|string $stream
+ * @return Zend_Amf_Util_BinaryStream
+ */
+ public function writeLong($stream)
+ {
+ $this->_stream.= pack('N', $stream);
+ return $this;
+ }
+
+ /**
+ * Read a 16 bit unsigned short.
+ *
+ * @todo This could use the unpack() w/ S,n, or v
+ * @return double
+ */
+ public function readUnsignedShort()
+ {
+ $byte1 = $this->readByte();
+ $byte2 = $this->readByte();
+ return (($byte1 << 8) | $byte2);
+ }
+
+ /**
+ * Reads an IEEE 754 double-precision floating point number from the data stream.
+ *
+ * @return double Floating point number
+ */
+ public function readDouble()
+ {
+ $bytes = substr($this->_stream, $this->_needle, 8);
+ $this->_needle+= 8;
+
+ if (!$this->_bigEndian) {
+ $bytes = strrev($bytes);
+ }
+
+ $double = unpack('dflt', $bytes);
+ return $double['flt'];
+ }
+
+ /**
+ * Writes an IEEE 754 double-precision floating point number from the data stream.
+ *
+ * @param string|double $stream
+ * @return Zend_Amf_Util_BinaryStream
+ */
+ public function writeDouble($stream)
+ {
+ $stream = pack('d', $stream);
+ if (!$this->_bigEndian) {
+ $stream = strrev($stream);
+ }
+ $this->_stream.= $stream;
+ return $this;
+ }
+
+}
diff --git a/library/Zend/Amf/Value/ByteArray.php b/library/Zend/Amf/Value/ByteArray.php
new file mode 100644
index 0000000..1e6dead
--- /dev/null
+++ b/library/Zend/Amf/Value/ByteArray.php
@@ -0,0 +1,58 @@
+_data = $data;
+ }
+
+ /**
+ * Return the byte stream
+ *
+ * @return string
+ */
+ public function getData()
+ {
+ return $this->_data;
+ }
+}
diff --git a/library/Zend/Amf/Value/MessageBody.php b/library/Zend/Amf/Value/MessageBody.php
new file mode 100644
index 0000000..a835fd2
--- /dev/null
+++ b/library/Zend/Amf/Value/MessageBody.php
@@ -0,0 +1,182 @@
+
+ * This Message structure defines how a local client would
+ * invoke a method/operation on a remote server. Additionally,
+ * the response from the Server is structured identically.
+ *
+ * @package Zend_Amf
+ * @subpackage Value
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Amf_Value_MessageBody
+{
+ /**
+ * A string describing which operation, function, or method
+ * is to be remotley invoked.
+ * @var string
+ */
+ protected $_targetUri = "";
+
+ /**
+ * Universal Resource Identifier that uniquely targets the originator's
+ * Object that should receive the server's response. The server will
+ * use this path specification to target the "OnResult()" or "onStatus()"
+ * handlers within the client. For Flash, it specifies an ActionScript
+ * Object path only. The NetResponse object pointed to by the Response Uri
+ * contains the connection state information. Passing/specifying this
+ * provides a convenient mechanism for the client/server to share access
+ * to an object that is managing the state of the shared connection.
+ *
+ * Since the server will use this field in the event of an error,
+ * this field is required even if a successful server request would
+ * not be expected to return a value to the client.
+ *
+ * @var string
+ */
+ protected $_responseUri = "";
+
+ /**
+ * Contains the actual data associated with the operation. It contains
+ * the client's parameter data that is passed to the server's operation/method.
+ * When serializing a root level data type or a parameter list array, no
+ * name field is included. That is, the data is anonomously represented
+ * as "Type Marker"/"Value" pairs. When serializing member data, the data is
+ * represented as a series of "Name"/"Type"/"Value" combinations.
+ *
+ * For server generated responses, it may contain any ActionScript
+ * data/objects that the server was expected to provide.
+ *
+ * @var string
+ */
+ protected $_data;
+
+ /**
+ * Constructor
+ *
+ * @param string $targetUri
+ * @param string $responseUri
+ * @param string $data
+ * @return void
+ */
+ public function __construct($targetUri, $responseUri, $data)
+ {
+ $this->setTargetUri($targetUri);
+ $this->setResponseUri($responseUri);
+ $this->setData($data);
+ }
+
+ /**
+ * Retrieve target Uri
+ *
+ * @return string
+ */
+ public function getTargetUri()
+ {
+ return $this->_targetUri;
+ }
+
+ /**
+ * Set target Uri
+ *
+ * @param string $targetUri
+ * @return Zend_Amf_Value_MessageBody
+ */
+ public function setTargetUri($targetUri)
+ {
+ if (null === $targetUri) {
+ $targetUri = '';
+ }
+ $this->_targetUri = (string) $targetUri;
+ return $this;
+ }
+
+ /**
+ * Get target Uri
+ *
+ * @return string
+ */
+ public function getResponseUri()
+ {
+ return $this->_responseUri;
+ }
+
+ /**
+ * Set response Uri
+ *
+ * @param string $responseUri
+ * @return Zend_Amf_Value_MessageBody
+ */
+ public function setResponseUri($responseUri)
+ {
+ if (null === $responseUri) {
+ $responseUri = '';
+ }
+ $this->_responseUri = $responseUri;
+ return $this;
+ }
+
+ /**
+ * Retrieve response data
+ *
+ * @return string
+ */
+ public function getData()
+ {
+ return $this->_data;
+ }
+
+ /**
+ * Set response data
+ *
+ * @param mixed $data
+ * @return Zend_Amf_Value_MessageBody
+ */
+ public function setData($data)
+ {
+ $this->_data = $data;
+ return $this;
+ }
+
+ /**
+ * Set reply method
+ *
+ * @param string $methodName
+ * @return Zend_Amf_Value_MessageBody
+ */
+ public function setReplyMethod($methodName)
+ {
+ if (!preg_match('#^[/?]#', $methodName)) {
+ $this->_targetUri = rtrim($this->_targetUri, '/') . '/';
+ }
+ $this->_targetUri = $this->_targetUri . $methodName;
+ return $this;
+ }
+}
diff --git a/library/Zend/Amf/Value/MessageHeader.php b/library/Zend/Amf/Value/MessageHeader.php
new file mode 100644
index 0000000..655bf23
--- /dev/null
+++ b/library/Zend/Amf/Value/MessageHeader.php
@@ -0,0 +1,81 @@
+name = $name;
+ $this->mustRead = (bool) $mustRead;
+ $this->data = $data;
+ if (null !== $length) {
+ $this->length = (int) $length;
+ }
+ }
+}
diff --git a/library/Zend/Amf/Value/Messaging/AbstractMessage.php b/library/Zend/Amf/Value/Messaging/AbstractMessage.php
new file mode 100644
index 0000000..eeed4b2
--- /dev/null
+++ b/library/Zend/Amf/Value/Messaging/AbstractMessage.php
@@ -0,0 +1,92 @@
+clientId = $this->generateId();
+ $this->destination = null;
+ $this->messageId = $this->generateId();
+ $this->timestamp = time().'00';
+ $this->timeToLive = 0;
+ $this->headers = new STDClass();
+ $this->body = null;
+
+ // correleate the two messages
+ if ($message && isset($message->messageId)) {
+ $this->correlationId = $message->messageId;
+ }
+ }
+}
diff --git a/library/Zend/Amf/Value/Messaging/ArrayCollection.php b/library/Zend/Amf/Value/Messaging/ArrayCollection.php
new file mode 100644
index 0000000..d39ad81
--- /dev/null
+++ b/library/Zend/Amf/Value/Messaging/ArrayCollection.php
@@ -0,0 +1,35 @@
+body
+ * of the message.
+ */
+ const LOGIN_OPERATION = 8;
+
+ /**
+ * This operation is used to log the user out of the current channel, and
+ * will invalidate the server session if the channel is HTTP based.
+ */
+ const LOGOUT_OPERATION = 9;
+
+ /**
+ * This operation is used to indicate that the client's subscription to a
+ * remote destination has been invalidated.
+ */
+ const SESSION_INVALIDATE_OPERATION = 10;
+
+ /**
+ * This operation is used by the MultiTopicConsumer to subscribe/unsubscribe
+ * from multiple subtopics/selectors in the same message.
+ */
+ const MULTI_SUBSCRIBE_OPERATION = 11;
+
+ /**
+ * This operation is used to indicate that a channel has disconnected
+ */
+ const DISCONNECT_OPERATION = 12;
+
+ /**
+ * This is the default operation for new CommandMessage instances.
+ */
+ const UNKNOWN_OPERATION = 10000;
+
+ /**
+ * The operation to execute for messages of this type
+ * @var int
+ */
+ public $operation = self::UNKNOWN_OPERATION;
+}
diff --git a/library/Zend/Amf/Value/Messaging/ErrorMessage.php b/library/Zend/Amf/Value/Messaging/ErrorMessage.php
new file mode 100644
index 0000000..aeb3bed
--- /dev/null
+++ b/library/Zend/Amf/Value/Messaging/ErrorMessage.php
@@ -0,0 +1,67 @@
+clientId = $this->generateId();
+ $this->destination = null;
+ $this->messageId = $this->generateId();
+ $this->timestamp = time().'00';
+ $this->timeToLive = 0;
+ $this->headers = new stdClass();
+ $this->body = null;
+ }
+}
diff --git a/library/Zend/Amf/Value/TraitsInfo.php b/library/Zend/Amf/Value/TraitsInfo.php
new file mode 100644
index 0000000..10f4d44
--- /dev/null
+++ b/library/Zend/Amf/Value/TraitsInfo.php
@@ -0,0 +1,154 @@
+_className = $className;
+ $this->_dynamic = $dynamic;
+ $this->_externalizable = $externalizable;
+ $this->_properties = $properties;
+ }
+
+ /**
+ * Test if the class is a dynamic class
+ *
+ * @return boolean
+ */
+ public function isDynamic()
+ {
+ return $this->_dynamic;
+ }
+
+ /**
+ * Test if class is externalizable
+ *
+ * @return boolean
+ */
+ public function isExternalizable()
+ {
+ return $this->_externalizable;
+ }
+
+ /**
+ * Return the number of properties in the class
+ *
+ * @return int
+ */
+ public function length()
+ {
+ return count($this->_properties);
+ }
+
+ /**
+ * Return the class name
+ *
+ * @return string
+ */
+ public function getClassName()
+ {
+ return $this->_className;
+ }
+
+ /**
+ * Add an additional property
+ *
+ * @param string $name
+ * @return Zend_Amf_Value_TraitsInfo
+ */
+ public function addProperty($name)
+ {
+ $this->_properties[] = $name;
+ return $this;
+ }
+
+ /**
+ * Add all properties of the class.
+ *
+ * @param array $props
+ * @return Zend_Amf_Value_TraitsInfo
+ */
+ public function addAllProperties(array $props)
+ {
+ $this->_properties = $props;
+ return $this;
+ }
+
+ /**
+ * Get the property at a given index
+ *
+ * @param int $index
+ * @return string
+ */
+ public function getProperty($index)
+ {
+ return $this->_properties[(int) $index];
+ }
+
+ /**
+ * Return all properties of the class.
+ *
+ * @return array
+ */
+ public function getAllProperties()
+ {
+ return $this->_properties;
+ }
+}
diff --git a/library/Zend/Application.php b/library/Zend/Application.php
new file mode 100644
index 0000000..ecc18c6
--- /dev/null
+++ b/library/Zend/Application.php
@@ -0,0 +1,417 @@
+_environment = (string) $environment;
+
+ require_once 'Zend/Loader/Autoloader.php';
+ $this->_autoloader = Zend_Loader_Autoloader::getInstance();
+
+ if (null !== $options) {
+ if (is_string($options)) {
+ $options = $this->_loadConfig($options);
+ } elseif ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (!is_array($options)) {
+ throw new Zend_Application_Exception('Invalid options provided; must be location of config file, a config object, or an array');
+ }
+
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Retrieve current environment
+ *
+ * @return string
+ */
+ public function getEnvironment()
+ {
+ return $this->_environment;
+ }
+
+ /**
+ * Retrieve autoloader instance
+ *
+ * @return Zend_Loader_Autoloader
+ */
+ public function getAutoloader()
+ {
+ return $this->_autoloader;
+ }
+
+ /**
+ * Set application options
+ *
+ * @param array $options
+ * @throws Zend_Application_Exception When no bootstrap path is provided
+ * @throws Zend_Application_Exception When invalid bootstrap information are provided
+ * @return Zend_Application
+ */
+ public function setOptions(array $options)
+ {
+ if (!empty($options['config'])) {
+ if (is_array($options['config'])) {
+ $_options = array();
+ foreach ($options['config'] as $tmp) {
+ $_options = $this->mergeOptions($_options, $this->_loadConfig($tmp));
+ }
+ $options = $this->mergeOptions($_options, $options);
+ } else {
+ $options = $this->mergeOptions($this->_loadConfig($options['config']), $options);
+ }
+ }
+
+ $this->_options = $options;
+
+ $options = array_change_key_case($options, CASE_LOWER);
+
+ $this->_optionKeys = array_keys($options);
+
+ if (!empty($options['phpsettings'])) {
+ $this->setPhpSettings($options['phpsettings']);
+ }
+
+ if (!empty($options['includepaths'])) {
+ $this->setIncludePaths($options['includepaths']);
+ }
+
+ if (!empty($options['autoloadernamespaces'])) {
+ $this->setAutoloaderNamespaces($options['autoloadernamespaces']);
+ }
+
+ if (!empty($options['autoloaderzfpath'])) {
+ $autoloader = $this->getAutoloader();
+ if (method_exists($autoloader, 'setZfPath')) {
+ $zfPath = $options['autoloaderzfpath'];
+ $zfVersion = !empty($options['autoloaderzfversion'])
+ ? $options['autoloaderzfversion']
+ : 'latest';
+ $autoloader->setZfPath($zfPath, $zfVersion);
+ }
+ }
+
+ if (!empty($options['bootstrap'])) {
+ $bootstrap = $options['bootstrap'];
+
+ if (is_string($bootstrap)) {
+ $this->setBootstrap($bootstrap);
+ } elseif (is_array($bootstrap)) {
+ if (empty($bootstrap['path'])) {
+ throw new Zend_Application_Exception('No bootstrap path provided');
+ }
+
+ $path = $bootstrap['path'];
+ $class = null;
+
+ if (!empty($bootstrap['class'])) {
+ $class = $bootstrap['class'];
+ }
+
+ $this->setBootstrap($path, $class);
+ } else {
+ throw new Zend_Application_Exception('Invalid bootstrap information provided');
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve application options (for caching)
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
+ /**
+ * Is an option present?
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function hasOption($key)
+ {
+ return in_array(strtolower($key), $this->_optionKeys);
+ }
+
+ /**
+ * Retrieve a single option
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function getOption($key)
+ {
+ if ($this->hasOption($key)) {
+ $options = $this->getOptions();
+ $options = array_change_key_case($options, CASE_LOWER);
+ return $options[strtolower($key)];
+ }
+ return null;
+ }
+
+ /**
+ * Merge options recursively
+ *
+ * @param array $array1
+ * @param mixed $array2
+ * @return array
+ */
+ public function mergeOptions(array $array1, $array2 = null)
+ {
+ if (is_array($array2)) {
+ foreach ($array2 as $key => $val) {
+ if (is_array($array2[$key])) {
+ $array1[$key] = (array_key_exists($key, $array1) && is_array($array1[$key]))
+ ? $this->mergeOptions($array1[$key], $array2[$key])
+ : $array2[$key];
+ } else {
+ $array1[$key] = $val;
+ }
+ }
+ }
+ return $array1;
+ }
+
+ /**
+ * Set PHP configuration settings
+ *
+ * @param array $settings
+ * @param string $prefix Key prefix to prepend to array values (used to map . separated INI values)
+ * @return Zend_Application
+ */
+ public function setPhpSettings(array $settings, $prefix = '')
+ {
+ foreach ($settings as $key => $value) {
+ $key = empty($prefix) ? $key : $prefix . $key;
+ if (is_scalar($value)) {
+ ini_set($key, $value);
+ } elseif (is_array($value)) {
+ $this->setPhpSettings($value, $key . '.');
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set include path
+ *
+ * @param array $paths
+ * @return Zend_Application
+ */
+ public function setIncludePaths(array $paths)
+ {
+ $path = implode(PATH_SEPARATOR, $paths);
+ set_include_path($path . PATH_SEPARATOR . get_include_path());
+ return $this;
+ }
+
+ /**
+ * Set autoloader namespaces
+ *
+ * @param array $namespaces
+ * @return Zend_Application
+ */
+ public function setAutoloaderNamespaces(array $namespaces)
+ {
+ $autoloader = $this->getAutoloader();
+
+ foreach ($namespaces as $namespace) {
+ $autoloader->registerNamespace($namespace);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set bootstrap path/class
+ *
+ * @param string $path
+ * @param string $class
+ * @return Zend_Application
+ */
+ public function setBootstrap($path, $class = null)
+ {
+ // setOptions() can potentially send a null value; specify default
+ // here
+ if (null === $class) {
+ $class = 'Bootstrap';
+ }
+
+ if (!class_exists($class, false)) {
+ require_once $path;
+ if (!class_exists($class, false)) {
+ throw new Zend_Application_Exception('Bootstrap class not found');
+ }
+ }
+ $this->_bootstrap = new $class($this);
+
+ if (!$this->_bootstrap instanceof Zend_Application_Bootstrap_Bootstrapper) {
+ throw new Zend_Application_Exception('Bootstrap class does not implement Zend_Application_Bootstrap_Bootstrapper');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get bootstrap object
+ *
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ */
+ public function getBootstrap()
+ {
+ if (null === $this->_bootstrap) {
+ $this->_bootstrap = new Zend_Application_Bootstrap_Bootstrap($this);
+ }
+ return $this->_bootstrap;
+ }
+
+ /**
+ * Bootstrap application
+ *
+ * @param null|string|array $resource
+ * @return Zend_Application
+ */
+ public function bootstrap($resource = null)
+ {
+ $this->getBootstrap()->bootstrap($resource);
+ return $this;
+ }
+
+ /**
+ * Run the application
+ *
+ * @return void
+ */
+ public function run()
+ {
+ $this->getBootstrap()->run();
+ }
+
+ /**
+ * Load configuration file of options
+ *
+ * @param string $file
+ * @throws Zend_Application_Exception When invalid configuration file is provided
+ * @return array
+ */
+ protected function _loadConfig($file)
+ {
+ $environment = $this->getEnvironment();
+ $suffix = pathinfo($file, PATHINFO_EXTENSION);
+ $suffix = ($suffix === 'dist')
+ ? pathinfo(basename($file, ".$suffix"), PATHINFO_EXTENSION)
+ : $suffix;
+
+ switch (strtolower($suffix)) {
+ case 'ini':
+ $config = new Zend_Config_Ini($file, $environment);
+ break;
+
+ case 'xml':
+ $config = new Zend_Config_Xml($file, $environment);
+ break;
+
+ case 'json':
+ $config = new Zend_Config_Json($file, $environment);
+ break;
+
+ case 'yaml':
+ case 'yml':
+ $config = new Zend_Config_Yaml($file, $environment);
+ break;
+
+ case 'php':
+ case 'inc':
+ $config = include $file;
+ if (!is_array($config)) {
+ throw new Zend_Application_Exception('Invalid configuration file provided; PHP file does not return array value');
+ }
+ return $config;
+ break;
+
+ default:
+ throw new Zend_Application_Exception('Invalid configuration file provided; unknown config type');
+ }
+
+ return $config->toArray();
+ }
+}
diff --git a/library/Zend/Application/Bootstrap/Bootstrap.php b/library/Zend/Application/Bootstrap/Bootstrap.php
new file mode 100644
index 0000000..9d69419
--- /dev/null
+++ b/library/Zend/Application/Bootstrap/Bootstrap.php
@@ -0,0 +1,156 @@
+hasOption('resourceloader')) {
+ $this->setOptions(array(
+ 'resourceloader' => $application->getOption('resourceloader')
+ ));
+ }
+ $this->getResourceLoader();
+
+ if (!$this->hasPluginResource('FrontController')) {
+ $this->registerPluginResource('FrontController');
+ }
+ }
+
+ /**
+ * Run the application
+ *
+ * Checks to see that we have a default controller directory. If not, an
+ * exception is thrown.
+ *
+ * If so, it registers the bootstrap with the 'bootstrap' parameter of
+ * the front controller, and dispatches the front controller.
+ *
+ * @return mixed
+ * @throws Zend_Application_Bootstrap_Exception
+ */
+ public function run()
+ {
+ $front = $this->getResource('FrontController');
+ $default = $front->getDefaultModule();
+ if (null === $front->getControllerDirectory($default)) {
+ throw new Zend_Application_Bootstrap_Exception(
+ 'No default controller directory registered with front controller'
+ );
+ }
+
+ $front->setParam('bootstrap', $this);
+ $response = $front->dispatch();
+ if ($front->returnResponse()) {
+ return $response;
+ }
+ }
+
+ /**
+ * Set module resource loader
+ *
+ * @param Zend_Loader_Autoloader_Resource $loader
+ * @return Zend_Application_Module_Bootstrap
+ */
+ public function setResourceLoader(Zend_Loader_Autoloader_Resource $loader)
+ {
+ $this->_resourceLoader = $loader;
+ return $this;
+ }
+
+ /**
+ * Retrieve module resource loader
+ *
+ * @return Zend_Loader_Autoloader_Resource
+ */
+ public function getResourceLoader()
+ {
+ if ((null === $this->_resourceLoader)
+ && (false !== ($namespace = $this->getAppNamespace()))
+ ) {
+ $r = new ReflectionClass($this);
+ $path = $r->getFileName();
+ $this->setResourceLoader(new Zend_Application_Module_Autoloader(array(
+ 'namespace' => $namespace,
+ 'basePath' => dirname($path),
+ )));
+ }
+ return $this->_resourceLoader;
+ }
+
+ /**
+ * Get application namespace (used for module autoloading)
+ *
+ * @return string
+ */
+ public function getAppNamespace()
+ {
+ return $this->_appNamespace;
+ }
+
+ /**
+ * Set application namespace (for module autoloading)
+ *
+ * @param string
+ * @return Zend_Application_Bootstrap_Bootstrap
+ */
+ public function setAppNamespace($value)
+ {
+ $this->_appNamespace = (string) $value;
+ return $this;
+ }
+}
diff --git a/library/Zend/Application/Bootstrap/BootstrapAbstract.php b/library/Zend/Application/Bootstrap/BootstrapAbstract.php
new file mode 100644
index 0000000..14679f9
--- /dev/null
+++ b/library/Zend/Application/Bootstrap/BootstrapAbstract.php
@@ -0,0 +1,770 @@
+setApplication($application);
+ $options = $application->getOptions();
+ $this->setOptions($options);
+ }
+
+ /**
+ * Set class state
+ *
+ * @param array $options
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ */
+ public function setOptions(array $options)
+ {
+ $this->_options = $this->mergeOptions($this->_options, $options);
+
+ $options = array_change_key_case($options, CASE_LOWER);
+ $this->_optionKeys = array_merge($this->_optionKeys, array_keys($options));
+
+ $methods = get_class_methods($this);
+ foreach ($methods as $key => $method) {
+ $methods[$key] = strtolower($method);
+ }
+
+ if (array_key_exists('pluginpaths', $options)) {
+ $pluginLoader = $this->getPluginLoader();
+
+ foreach ($options['pluginpaths'] as $prefix => $path) {
+ $pluginLoader->addPrefixPath($prefix, $path);
+ }
+ unset($options['pluginpaths']);
+ }
+
+ foreach ($options as $key => $value) {
+ $method = 'set' . strtolower($key);
+
+ if (in_array($method, $methods)) {
+ $this->$method($value);
+ } elseif ('resources' == $key) {
+ foreach ($value as $resource => $resourceOptions) {
+ $this->registerPluginResource($resource, $resourceOptions);
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Get current options from bootstrap
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
+ /**
+ * Is an option present?
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function hasOption($key)
+ {
+ return in_array(strtolower($key), $this->_optionKeys);
+ }
+
+ /**
+ * Retrieve a single option
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function getOption($key)
+ {
+ if ($this->hasOption($key)) {
+ $options = $this->getOptions();
+ $options = array_change_key_case($options, CASE_LOWER);
+ return $options[strtolower($key)];
+ }
+ return null;
+ }
+
+ /**
+ * Merge options recursively
+ *
+ * @param array $array1
+ * @param mixed $array2
+ * @return array
+ */
+ public function mergeOptions(array $array1, $array2 = null)
+ {
+ if (is_array($array2)) {
+ foreach ($array2 as $key => $val) {
+ if (is_array($array2[$key])) {
+ $array1[$key] = (array_key_exists($key, $array1) && is_array($array1[$key]))
+ ? $this->mergeOptions($array1[$key], $array2[$key])
+ : $array2[$key];
+ } else {
+ $array1[$key] = $val;
+ }
+ }
+ }
+ return $array1;
+ }
+
+ /**
+ * Get class resources (as resource/method pairs)
+ *
+ * Uses get_class_methods() by default, reflection on prior to 5.2.6,
+ * as a bug prevents the usage of get_class_methods() there.
+ *
+ * @return array
+ */
+ public function getClassResources()
+ {
+ if (null === $this->_classResources) {
+ if (version_compare(PHP_VERSION, '5.2.6') === -1) {
+ $class = new ReflectionObject($this);
+ $classMethods = $class->getMethods();
+ $methodNames = array();
+
+ foreach ($classMethods as $method) {
+ $methodNames[] = $method->getName();
+ }
+ } else {
+ $methodNames = get_class_methods($this);
+ }
+
+ $this->_classResources = array();
+ foreach ($methodNames as $method) {
+ if (5 < strlen($method) && '_init' === substr($method, 0, 5)) {
+ $this->_classResources[strtolower(substr($method, 5))] = $method;
+ }
+ }
+ }
+
+ return $this->_classResources;
+ }
+
+ /**
+ * Get class resource names
+ *
+ * @return array
+ */
+ public function getClassResourceNames()
+ {
+ $resources = $this->getClassResources();
+ return array_keys($resources);
+ }
+
+ /**
+ * Register a new resource plugin
+ *
+ * @param string|Zend_Application_Resource_Resource $resource
+ * @param mixed $options
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ * @throws Zend_Application_Bootstrap_Exception When invalid resource is provided
+ */
+ public function registerPluginResource($resource, $options = null)
+ {
+ if ($resource instanceof Zend_Application_Resource_Resource) {
+ $resource->setBootstrap($this);
+ $pluginName = $this->_resolvePluginResourceName($resource);
+ $this->_pluginResources[$pluginName] = $resource;
+ return $this;
+ }
+
+ if (!is_string($resource)) {
+ throw new Zend_Application_Bootstrap_Exception('Invalid resource provided to ' . __METHOD__);
+ }
+
+ $this->_pluginResources[$resource] = $options;
+ return $this;
+ }
+
+ /**
+ * Unregister a resource from the bootstrap
+ *
+ * @param string|Zend_Application_Resource_Resource $resource
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ * @throws Zend_Application_Bootstrap_Exception When unknown resource type is provided
+ */
+ public function unregisterPluginResource($resource)
+ {
+ if ($resource instanceof Zend_Application_Resource_Resource) {
+ if ($index = array_search($resource, $this->_pluginResources, true)) {
+ unset($this->_pluginResources[$index]);
+ }
+ return $this;
+ }
+
+ if (!is_string($resource)) {
+ throw new Zend_Application_Bootstrap_Exception('Unknown resource type provided to ' . __METHOD__);
+ }
+
+ $resource = strtolower($resource);
+ if (array_key_exists($resource, $this->_pluginResources)) {
+ unset($this->_pluginResources[$resource]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Is the requested plugin resource registered?
+ *
+ * @param string $resource
+ * @return bool
+ */
+ public function hasPluginResource($resource)
+ {
+ return (null !== $this->getPluginResource($resource));
+ }
+
+ /**
+ * Get a registered plugin resource
+ *
+ * @param string $resourceName
+ * @return Zend_Application_Resource_Resource
+ */
+ public function getPluginResource($resource)
+ {
+ if (array_key_exists(strtolower($resource), $this->_pluginResources)) {
+ $resource = strtolower($resource);
+ if (!$this->_pluginResources[$resource] instanceof Zend_Application_Resource_Resource) {
+ $resourceName = $this->_loadPluginResource($resource, $this->_pluginResources[$resource]);
+ if (!$resourceName) {
+ throw new Zend_Application_Bootstrap_Exception(sprintf('Unable to resolve plugin "%s"; no corresponding plugin with that name', $resource));
+ }
+ $resource = $resourceName;
+ }
+ return $this->_pluginResources[$resource];
+ }
+
+ foreach ($this->_pluginResources as $plugin => $spec) {
+ if ($spec instanceof Zend_Application_Resource_Resource) {
+ $pluginName = $this->_resolvePluginResourceName($spec);
+ if (0 === strcasecmp($resource, $pluginName)) {
+ unset($this->_pluginResources[$plugin]);
+ $this->_pluginResources[$pluginName] = $spec;
+ return $spec;
+ }
+ continue;
+ }
+
+ if (false !== $pluginName = $this->_loadPluginResource($plugin, $spec)) {
+ if (0 === strcasecmp($resource, $pluginName)) {
+ return $this->_pluginResources[$pluginName];
+ }
+ continue;
+ }
+
+ if (class_exists($plugin)) { //@SEE ZF-7550
+ $spec = (array) $spec;
+ $spec['bootstrap'] = $this;
+ $instance = new $plugin($spec);
+ $pluginName = $this->_resolvePluginResourceName($instance);
+ unset($this->_pluginResources[$plugin]);
+ $this->_pluginResources[$pluginName] = $instance;
+
+ if (0 === strcasecmp($resource, $pluginName)) {
+ return $instance;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve all plugin resources
+ *
+ * @return array
+ */
+ public function getPluginResources()
+ {
+ foreach (array_keys($this->_pluginResources) as $resource) {
+ $this->getPluginResource($resource);
+ }
+ return $this->_pluginResources;
+ }
+
+ /**
+ * Retrieve plugin resource names
+ *
+ * @return array
+ */
+ public function getPluginResourceNames()
+ {
+ $this->getPluginResources();
+ return array_keys($this->_pluginResources);
+ }
+
+ /**
+ * Set plugin loader for loading resources
+ *
+ * @param Zend_Loader_PluginLoader_Interface $loader
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ */
+ public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader)
+ {
+ $this->_pluginLoader = $loader;
+ return $this;
+ }
+
+ /**
+ * Get the plugin loader for resources
+ *
+ * @return Zend_Loader_PluginLoader_Interface
+ */
+ public function getPluginLoader()
+ {
+ if ($this->_pluginLoader === null) {
+ $options = array(
+ 'Zend_Application_Resource' => 'Zend/Application/Resource',
+ 'ZendX_Application_Resource' => 'ZendX/Application/Resource'
+ );
+
+ $this->_pluginLoader = new Zend_Loader_PluginLoader($options);
+ }
+
+ return $this->_pluginLoader;
+ }
+
+ /**
+ * Set application/parent bootstrap
+ *
+ * @param Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ */
+ public function setApplication($application)
+ {
+ if (($application instanceof Zend_Application)
+ || ($application instanceof Zend_Application_Bootstrap_Bootstrapper)
+ ) {
+ if ($application === $this) {
+ throw new Zend_Application_Bootstrap_Exception('Cannot set application to same object; creates recursion');
+ }
+ $this->_application = $application;
+ } else {
+ throw new Zend_Application_Bootstrap_Exception('Invalid application provided to bootstrap constructor (received "' . get_class($application) . '" instance)');
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve parent application instance
+ *
+ * @return Zend_Application|Zend_Application_Bootstrap_Bootstrapper
+ */
+ public function getApplication()
+ {
+ return $this->_application;
+ }
+
+ /**
+ * Retrieve application environment
+ *
+ * @return string
+ */
+ public function getEnvironment()
+ {
+ if (null === $this->_environment) {
+ $this->_environment = $this->getApplication()->getEnvironment();
+ }
+ return $this->_environment;
+ }
+
+ /**
+ * Set resource container
+ *
+ * By default, if a resource callback has a non-null return value, this
+ * value will be stored in a container using the resource name as the
+ * key.
+ *
+ * Containers must be objects, and must allow setting public properties.
+ *
+ * @param object $container
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ */
+ public function setContainer($container)
+ {
+ if (!is_object($container)) {
+ throw new Zend_Application_Bootstrap_Exception('Resource containers must be objects');
+ }
+ $this->_container = $container;
+ return $this;
+ }
+
+ /**
+ * Retrieve resource container
+ *
+ * @return object
+ */
+ public function getContainer()
+ {
+ if (null === $this->_container) {
+ $this->setContainer(new Zend_Registry());
+ }
+ return $this->_container;
+ }
+
+ /**
+ * Determine if a resource has been stored in the container
+ *
+ * During bootstrap resource initialization, you may return a value. If
+ * you do, it will be stored in the {@link setContainer() container}.
+ * You can use this method to determine if a value was stored.
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function hasResource($name)
+ {
+ $resource = strtolower($name);
+ $container = $this->getContainer();
+ return isset($container->{$resource});
+ }
+
+ /**
+ * Retrieve a resource from the container
+ *
+ * During bootstrap resource initialization, you may return a value. If
+ * you do, it will be stored in the {@link setContainer() container}.
+ * You can use this method to retrieve that value.
+ *
+ * If no value was returned, this will return a null value.
+ *
+ * @param string $name
+ * @return null|mixed
+ */
+ public function getResource($name)
+ {
+ $resource = strtolower($name);
+ $container = $this->getContainer();
+ if ($this->hasResource($resource)) {
+ return $container->{$resource};
+ }
+ return null;
+ }
+
+ /**
+ * Implement PHP's magic to retrieve a ressource
+ * in the bootstrap
+ *
+ * @param string $prop
+ * @return null|mixed
+ */
+ public function __get($prop)
+ {
+ return $this->getResource($prop);
+ }
+
+ /**
+ * Implement PHP's magic to ask for the
+ * existence of a ressource in the bootstrap
+ *
+ * @param string $prop
+ * @return bool
+ */
+ public function __isset($prop)
+ {
+ return $this->hasResource($prop);
+ }
+
+ /**
+ * Bootstrap individual, all, or multiple resources
+ *
+ * Marked as final to prevent issues when subclassing and naming the
+ * child class 'Bootstrap' (in which case, overriding this method
+ * would result in it being treated as a constructor).
+ *
+ * If you need to override this functionality, override the
+ * {@link _bootstrap()} method.
+ *
+ * @param null|string|array $resource
+ * @return Zend_Application_Bootstrap_BootstrapAbstract
+ * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
+ */
+ final public function bootstrap($resource = null)
+ {
+ $this->_bootstrap($resource);
+ return $this;
+ }
+
+ /**
+ * Overloading: intercept calls to bootstrap() methods
+ *
+ * @param string $method
+ * @param array $args
+ * @return void
+ * @throws Zend_Application_Bootstrap_Exception On invalid method name
+ */
+ public function __call($method, $args)
+ {
+ if (9 < strlen($method) && 'bootstrap' === substr($method, 0, 9)) {
+ $resource = substr($method, 9);
+ return $this->bootstrap($resource);
+ }
+
+ throw new Zend_Application_Bootstrap_Exception('Invalid method "' . $method . '"');
+ }
+
+ /**
+ * Bootstrap implementation
+ *
+ * This method may be overridden to provide custom bootstrapping logic.
+ * It is the sole method called by {@link bootstrap()}.
+ *
+ * @param null|string|array $resource
+ * @return void
+ * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
+ */
+ protected function _bootstrap($resource = null)
+ {
+ if (null === $resource) {
+ foreach ($this->getClassResourceNames() as $resource) {
+ $this->_executeResource($resource);
+ }
+
+ foreach ($this->getPluginResourceNames() as $resource) {
+ $this->_executeResource($resource);
+ }
+ } elseif (is_string($resource)) {
+ $this->_executeResource($resource);
+ } elseif (is_array($resource)) {
+ foreach ($resource as $r) {
+ $this->_executeResource($r);
+ }
+ } else {
+ throw new Zend_Application_Bootstrap_Exception('Invalid argument passed to ' . __METHOD__);
+ }
+ }
+
+ /**
+ * Execute a resource
+ *
+ * Checks to see if the resource has already been run. If not, it searches
+ * first to see if a local method matches the resource, and executes that.
+ * If not, it checks to see if a plugin resource matches, and executes that
+ * if found.
+ *
+ * Finally, if not found, it throws an exception.
+ *
+ * @param string $resource
+ * @return void
+ * @throws Zend_Application_Bootstrap_Exception When resource not found
+ */
+ protected function _executeResource($resource)
+ {
+ $resourceName = strtolower($resource);
+
+ if (in_array($resourceName, $this->_run)) {
+ return;
+ }
+
+ if (isset($this->_started[$resourceName]) && $this->_started[$resourceName]) {
+ throw new Zend_Application_Bootstrap_Exception('Circular resource dependency detected');
+ }
+
+ $classResources = $this->getClassResources();
+ if (array_key_exists($resourceName, $classResources)) {
+ $this->_started[$resourceName] = true;
+ $method = $classResources[$resourceName];
+ $return = $this->$method();
+ unset($this->_started[$resourceName]);
+ $this->_markRun($resourceName);
+
+ if (null !== $return) {
+ $this->getContainer()->{$resourceName} = $return;
+ }
+
+ return;
+ }
+
+ if ($this->hasPluginResource($resource)) {
+ $this->_started[$resourceName] = true;
+ $plugin = $this->getPluginResource($resource);
+ $return = $plugin->init();
+ unset($this->_started[$resourceName]);
+ $this->_markRun($resourceName);
+
+ if (null !== $return) {
+ $this->getContainer()->{$resourceName} = $return;
+ }
+
+ return;
+ }
+
+ throw new Zend_Application_Bootstrap_Exception('Resource matching "' . $resource . '" not found');
+ }
+
+ /**
+ * Load a plugin resource
+ *
+ * @param string $resource
+ * @param array|object|null $options
+ * @return string|false
+ */
+ protected function _loadPluginResource($resource, $options)
+ {
+ $options = (array) $options;
+ $options['bootstrap'] = $this;
+ $className = $this->getPluginLoader()->load(strtolower($resource), false);
+
+ if (!$className) {
+ return false;
+ }
+
+ $instance = new $className($options);
+
+ unset($this->_pluginResources[$resource]);
+
+ if (isset($instance->_explicitType)) {
+ $resource = $instance->_explicitType;
+ }
+ $resource = strtolower($resource);
+ $this->_pluginResources[$resource] = $instance;
+
+ return $resource;
+ }
+
+ /**
+ * Mark a resource as having run
+ *
+ * @param string $resource
+ * @return void
+ */
+ protected function _markRun($resource)
+ {
+ if (!in_array($resource, $this->_run)) {
+ $this->_run[] = $resource;
+ }
+ }
+
+ /**
+ * Resolve a plugin resource name
+ *
+ * Uses, in order of preference
+ * - $_explicitType property of resource
+ * - Short name of resource (if a matching prefix path is found)
+ * - class name (if none of the above are true)
+ *
+ * The name is then cast to lowercase.
+ *
+ * @param Zend_Application_Resource_Resource $resource
+ * @return string
+ */
+ protected function _resolvePluginResourceName($resource)
+ {
+ if (isset($resource->_explicitType)) {
+ $pluginName = $resource->_explicitType;
+ } else {
+ $className = get_class($resource);
+ $pluginName = $className;
+ $loader = $this->getPluginLoader();
+ foreach ($loader->getPaths() as $prefix => $paths) {
+ if (0 === strpos($className, $prefix)) {
+ $pluginName = substr($className, strlen($prefix));
+ $pluginName = trim($pluginName, '_');
+ break;
+ }
+ }
+ }
+ $pluginName = strtolower($pluginName);
+ return $pluginName;
+ }
+}
diff --git a/library/Zend/Application/Bootstrap/Bootstrapper.php b/library/Zend/Application/Bootstrap/Bootstrapper.php
new file mode 100644
index 0000000..dbaf0a7
--- /dev/null
+++ b/library/Zend/Application/Bootstrap/Bootstrapper.php
@@ -0,0 +1,94 @@
+initDefaultResourceTypes();
+ }
+
+ /**
+ * Initialize default resource types for module resource classes
+ *
+ * @return void
+ */
+ public function initDefaultResourceTypes()
+ {
+ $basePath = $this->getBasePath();
+ $this->addResourceTypes(array(
+ 'dbtable' => array(
+ 'namespace' => 'Model_DbTable',
+ 'path' => 'models/DbTable',
+ ),
+ 'mappers' => array(
+ 'namespace' => 'Model_Mapper',
+ 'path' => 'models/mappers',
+ ),
+ 'form' => array(
+ 'namespace' => 'Form',
+ 'path' => 'forms',
+ ),
+ 'model' => array(
+ 'namespace' => 'Model',
+ 'path' => 'models',
+ ),
+ 'plugin' => array(
+ 'namespace' => 'Plugin',
+ 'path' => 'plugins',
+ ),
+ 'service' => array(
+ 'namespace' => 'Service',
+ 'path' => 'services',
+ ),
+ 'viewhelper' => array(
+ 'namespace' => 'View_Helper',
+ 'path' => 'views/helpers',
+ ),
+ 'viewfilter' => array(
+ 'namespace' => 'View_Filter',
+ 'path' => 'views/filters',
+ ),
+ ));
+ $this->setDefaultResourceType('model');
+ }
+}
diff --git a/library/Zend/Application/Module/Bootstrap.php b/library/Zend/Application/Module/Bootstrap.php
new file mode 100644
index 0000000..97bb04b
--- /dev/null
+++ b/library/Zend/Application/Module/Bootstrap.php
@@ -0,0 +1,128 @@
+setApplication($application);
+
+ // Use same plugin loader as parent bootstrap
+ if ($application instanceof Zend_Application_Bootstrap_ResourceBootstrapper) {
+ $this->setPluginLoader($application->getPluginLoader());
+ }
+
+ $key = strtolower($this->getModuleName());
+ if ($application->hasOption($key)) {
+ // Don't run via setOptions() to prevent duplicate initialization
+ $this->setOptions($application->getOption($key));
+ }
+
+ if ($application->hasOption('resourceloader')) {
+ $this->setOptions(array(
+ 'resourceloader' => $application->getOption('resourceloader')
+ ));
+ }
+ $this->initResourceLoader();
+
+ // ZF-6545: ensure front controller resource is loaded
+ if (!$this->hasPluginResource('FrontController')) {
+ $this->registerPluginResource('FrontController');
+ }
+
+ // ZF-6545: prevent recursive registration of modules
+ if ($this->hasPluginResource('modules')) {
+ $this->unregisterPluginResource('modules');
+ }
+ }
+
+ /**
+ * Ensure resource loader is loaded
+ *
+ * @return void
+ */
+ public function initResourceLoader()
+ {
+ $this->getResourceLoader();
+ }
+
+ /**
+ * Get default application namespace
+ *
+ * Proxies to {@link getModuleName()}, and returns the current module
+ * name
+ *
+ * @return string
+ */
+ public function getAppNamespace()
+ {
+ return $this->getModuleName();
+ }
+
+ /**
+ * Retrieve module name
+ *
+ * @return string
+ */
+ public function getModuleName()
+ {
+ if (empty($this->_moduleName)) {
+ $class = get_class($this);
+ if (preg_match('/^([a-z][a-z0-9]*)_/i', $class, $matches)) {
+ $prefix = $matches[1];
+ } else {
+ $prefix = $class;
+ }
+ $this->_moduleName = $prefix;
+ }
+ return $this->_moduleName;
+ }
+}
diff --git a/library/Zend/Application/Resource/Cachemanager.php b/library/Zend/Application/Resource/Cachemanager.php
new file mode 100644
index 0000000..161abba
--- /dev/null
+++ b/library/Zend/Application/Resource/Cachemanager.php
@@ -0,0 +1,73 @@
+getCacheManager();
+ }
+
+ /**
+ * Retrieve Zend_Cache_Manager instance
+ *
+ * @return Zend_Cache_Manager
+ */
+ public function getCacheManager()
+ {
+ if (null === $this->_manager) {
+ $this->_manager = new Zend_Cache_Manager;
+
+ $options = $this->getOptions();
+ foreach ($options as $key => $value) {
+ if ($this->_manager->hasCacheTemplate($key)) {
+ $this->_manager->setTemplateOptions($key, $value);
+ } else {
+ $this->_manager->setCacheTemplate($key, $value);
+ }
+ }
+ }
+
+ return $this->_manager;
+ }
+}
diff --git a/library/Zend/Application/Resource/Db.php b/library/Zend/Application/Resource/Db.php
new file mode 100644
index 0000000..e950f13
--- /dev/null
+++ b/library/Zend/Application/Resource/Db.php
@@ -0,0 +1,193 @@
+_adapter = $adapter;
+ return $this;
+ }
+
+ /**
+ * Adapter type to use
+ *
+ * @return string
+ */
+ public function getAdapter()
+ {
+ return $this->_adapter;
+ }
+
+ /**
+ * Set the adapter params
+ *
+ * @param string $adapter
+ * @return Zend_Application_Resource_Db
+ */
+ public function setParams(array $params)
+ {
+ $this->_params = $params;
+ return $this;
+ }
+
+ /**
+ * Adapter parameters
+ *
+ * @return array
+ */
+ public function getParams()
+ {
+ return $this->_params;
+ }
+
+ /**
+ * Set whether to use this as default table adapter
+ *
+ * @param boolean $defaultTableAdapter
+ * @return Zend_Application_Resource_Db
+ */
+ public function setIsDefaultTableAdapter($isDefaultTableAdapter)
+ {
+ $this->_isDefaultTableAdapter = $isDefaultTableAdapter;
+ return $this;
+ }
+
+ /**
+ * Is this adapter the default table adapter?
+ *
+ * @return void
+ */
+ public function isDefaultTableAdapter()
+ {
+ return $this->_isDefaultTableAdapter;
+ }
+
+ /**
+ * Retrieve initialized DB connection
+ *
+ * @return null|Zend_Db_Adapter_Abstract
+ */
+ public function getDbAdapter()
+ {
+ if ((null === $this->_db)
+ && (null !== ($adapter = $this->getAdapter()))
+ ) {
+ $this->_db = Zend_Db::factory($adapter, $this->getParams());
+ }
+ return $this->_db;
+ }
+
+ /**
+ * Defined by Zend_Application_Resource_Resource
+ *
+ * @return Zend_Db_Adapter_Abstract|null
+ */
+ public function init()
+ {
+ if (null !== ($db = $this->getDbAdapter())) {
+ if ($this->isDefaultTableAdapter()) {
+ Zend_Db_Table::setDefaultAdapter($db);
+ }
+ return $db;
+ }
+ }
+
+ /**
+ * Set the default metadata cache
+ *
+ * @param string|Zend_Cache_Core $cache
+ * @return Zend_Application_Resource_Db
+ */
+ public function setDefaultMetadataCache($cache)
+ {
+ $metadataCache = null;
+
+ if (is_string($cache)) {
+ $bootstrap = $this->getBootstrap();
+ if ($bootstrap instanceof Zend_Application_Bootstrap_ResourceBootstrapper
+ && $bootstrap->hasPluginResource('CacheManager')
+ ) {
+ $cacheManager = $bootstrap->bootstrap('CacheManager')
+ ->getResource('CacheManager');
+ if (null !== $cacheManager && $cacheManager->hasCache($cache)) {
+ $metadataCache = $cacheManager->getCache($cache);
+ }
+ }
+ } else if ($cache instanceof Zend_Cache_Core) {
+ $metadataCache = $cache;
+ }
+
+ if ($metadataCache instanceof Zend_Cache_Core) {
+ Zend_Db_Table::setDefaultMetadataCache($metadataCache);
+ }
+
+ return $this;
+ }
+}
diff --git a/library/Zend/Application/Resource/Dojo.php b/library/Zend/Application/Resource/Dojo.php
new file mode 100644
index 0000000..d604abf
--- /dev/null
+++ b/library/Zend/Application/Resource/Dojo.php
@@ -0,0 +1,76 @@
+getDojo();
+ }
+
+ /**
+ * Retrieve Dojo View Helper
+ *
+ * @return Zend_Dojo_View_Dojo_Container
+ */
+ public function getDojo()
+ {
+ if (null === $this->_dojo) {
+ $this->getBootstrap()->bootstrap('view');
+ $view = $this->getBootstrap()->view;
+
+ Zend_Dojo::enableView($view);
+ $view->dojo()->setOptions($this->getOptions());
+
+ $this->_dojo = $view->dojo();
+ }
+
+ return $this->_dojo;
+ }
+}
diff --git a/library/Zend/Application/Resource/Exception.php b/library/Zend/Application/Resource/Exception.php
new file mode 100644
index 0000000..fa77c48
--- /dev/null
+++ b/library/Zend/Application/Resource/Exception.php
@@ -0,0 +1,40 @@
+getFrontController();
+
+ foreach ($this->getOptions() as $key => $value) {
+ switch (strtolower($key)) {
+ case 'controllerdirectory':
+ if (is_string($value)) {
+ $front->setControllerDirectory($value);
+ } elseif (is_array($value)) {
+ foreach ($value as $module => $directory) {
+ $front->addControllerDirectory($directory, $module);
+ }
+ }
+ break;
+
+ case 'modulecontrollerdirectoryname':
+ $front->setModuleControllerDirectoryName($value);
+ break;
+
+ case 'moduledirectory':
+ if (is_string($value)) {
+ $front->addModuleDirectory($value);
+ } elseif (is_array($value)) {
+ foreach($value as $moduleDir) {
+ $front->addModuleDirectory($moduleDir);
+ }
+ }
+ break;
+
+ case 'defaultcontrollername':
+ $front->setDefaultControllerName($value);
+ break;
+
+ case 'defaultaction':
+ $front->setDefaultAction($value);
+ break;
+
+ case 'defaultmodule':
+ $front->setDefaultModule($value);
+ break;
+
+ case 'baseurl':
+ if (!empty($value)) {
+ $front->setBaseUrl($value);
+ }
+ break;
+
+ case 'params':
+ $front->setParams($value);
+ break;
+
+ case 'plugins':
+ foreach ((array) $value as $pluginClass) {
+ $stackIndex = null;
+ if(is_array($pluginClass)) {
+ $pluginClass = array_change_key_case($pluginClass, CASE_LOWER);
+ if(isset($pluginClass['class']))
+ {
+ if(isset($pluginClass['stackindex'])) {
+ $stackIndex = $pluginClass['stackindex'];
+ }
+
+ $pluginClass = $pluginClass['class'];
+ }
+ }
+
+ $plugin = new $pluginClass();
+ $front->registerPlugin($plugin, $stackIndex);
+ }
+ break;
+
+ case 'returnresponse':
+ $front->returnResponse((bool) $value);
+ break;
+
+ case 'throwexceptions':
+ $front->throwExceptions((bool) $value);
+ break;
+
+ case 'actionhelperpaths':
+ if (is_array($value)) {
+ foreach ($value as $helperPrefix => $helperPath) {
+ Zend_Controller_Action_HelperBroker::addPath($helperPath, $helperPrefix);
+ }
+ }
+ break;
+
+ default:
+ $front->setParam($key, $value);
+ break;
+ }
+ }
+
+ if (null !== ($bootstrap = $this->getBootstrap())) {
+ $this->getBootstrap()->frontController = $front;
+ }
+
+ return $front;
+ }
+
+ /**
+ * Retrieve front controller instance
+ *
+ * @return Zend_Controller_Front
+ */
+ public function getFrontController()
+ {
+ if (null === $this->_front) {
+ $this->_front = Zend_Controller_Front::getInstance();
+ }
+ return $this->_front;
+ }
+}
diff --git a/library/Zend/Application/Resource/Layout.php b/library/Zend/Application/Resource/Layout.php
new file mode 100644
index 0000000..d4249c2
--- /dev/null
+++ b/library/Zend/Application/Resource/Layout.php
@@ -0,0 +1,70 @@
+getBootstrap()->bootstrap('FrontController');
+ return $this->getLayout();
+ }
+
+ /**
+ * Retrieve layout object
+ *
+ * @return Zend_Layout
+ */
+ public function getLayout()
+ {
+ if (null === $this->_layout) {
+ $this->_layout = Zend_Layout::startMvc($this->getOptions());
+ }
+ return $this->_layout;
+ }
+}
diff --git a/library/Zend/Application/Resource/Locale.php b/library/Zend/Application/Resource/Locale.php
new file mode 100644
index 0000000..87d29e7
--- /dev/null
+++ b/library/Zend/Application/Resource/Locale.php
@@ -0,0 +1,117 @@
+getLocale();
+ }
+
+ /**
+ * Retrieve locale object
+ *
+ * @return Zend_Locale
+ */
+ public function getLocale()
+ {
+ if (null === $this->_locale) {
+ $options = $this->getOptions();
+
+ if (!isset($options['default'])) {
+ $this->_locale = new Zend_Locale();
+ } elseif(!isset($options['force']) ||
+ (bool) $options['force'] == false)
+ {
+ // Don't force any locale, just go for auto detection
+ Zend_Locale::setDefault($options['default']);
+ $this->_locale = new Zend_Locale();
+ } else {
+ $this->_locale = new Zend_Locale($options['default']);
+ }
+
+ $key = (isset($options['registry_key']) && !is_numeric($options['registry_key']))
+ ? $options['registry_key']
+ : self::DEFAULT_REGISTRY_KEY;
+ Zend_Registry::set($key, $this->_locale);
+ }
+
+ return $this->_locale;
+ }
+
+ /**
+ * Set the cache
+ *
+ * @param string|Zend_Cache_Core $cache
+ * @return Zend_Application_Resource_Locale
+ */
+ public function setCache($cache)
+ {
+ if (is_string($cache)) {
+ $bootstrap = $this->getBootstrap();
+ if ($bootstrap instanceof Zend_Application_Bootstrap_ResourceBootstrapper
+ && $bootstrap->hasPluginResource('CacheManager')
+ ) {
+ $cacheManager = $bootstrap->bootstrap('CacheManager')
+ ->getResource('CacheManager');
+ if (null !== $cacheManager && $cacheManager->hasCache($cache)) {
+ $cache = $cacheManager->getCache($cache);
+ }
+ }
+ }
+
+ if ($cache instanceof Zend_Cache_Core) {
+ Zend_Locale::setCache($cache);
+ }
+
+ return $this;
+ }
+}
diff --git a/library/Zend/Application/Resource/Log.php b/library/Zend/Application/Resource/Log.php
new file mode 100644
index 0000000..8a61297
--- /dev/null
+++ b/library/Zend/Application/Resource/Log.php
@@ -0,0 +1,78 @@
+getLog();
+ }
+
+ /**
+ * Attach logger
+ *
+ * @param Zend_Log $log
+ * @return Zend_Application_Resource_Log
+ */
+ public function setLog(Zend_Log $log)
+ {
+ $this->_log = $log;
+ return $this;
+ }
+
+ public function getLog()
+ {
+ if (null === $this->_log) {
+ $options = $this->getOptions();
+ $log = Zend_Log::factory($options);
+ $this->setLog($log);
+ }
+ return $this->_log;
+ }
+}
diff --git a/library/Zend/Application/Resource/Mail.php b/library/Zend/Application/Resource/Mail.php
new file mode 100644
index 0000000..ff31153
--- /dev/null
+++ b/library/Zend/Application/Resource/Mail.php
@@ -0,0 +1,147 @@
+getMail();
+ }
+
+ /**
+ *
+ * @return Zend_Mail_Transport_Abstract|null
+ */
+ public function getMail()
+ {
+ if (null === $this->_transport) {
+ $options = $this->getOptions();
+ foreach($options as $key => $option) {
+ $options[strtolower($key)] = $option;
+ }
+ $this->setOptions($options);
+
+ if(isset($options['transport']) &&
+ !is_numeric($options['transport']))
+ {
+ $this->_transport = $this->_setupTransport($options['transport']);
+ if(!isset($options['transport']['register']) ||
+ $options['transport']['register'] == '1' ||
+ (isset($options['transport']['register']) &&
+ !is_numeric($options['transport']['register']) &&
+ (bool) $options['transport']['register'] == true))
+ {
+ Zend_Mail::setDefaultTransport($this->_transport);
+ }
+ }
+
+ $this->_setDefaults('from');
+ $this->_setDefaults('replyTo');
+ }
+
+ return $this->_transport;
+ }
+
+ protected function _setDefaults($type) {
+ $key = strtolower('default' . $type);
+ $options = $this->getOptions();
+
+ if(isset($options[$key]['email']) &&
+ !is_numeric($options[$key]['email']))
+ {
+ $method = array('Zend_Mail', 'setDefault' . ucfirst($type));
+ if(isset($options[$key]['name']) &&
+ !is_numeric($options[$key]['name']))
+ {
+ call_user_func($method, $options[$key]['email'],
+ $options[$key]['name']);
+ } else {
+ call_user_func($method, $options[$key]['email']);
+ }
+ }
+ }
+
+ protected function _setupTransport($options)
+ {
+ if(!isset($options['type'])) {
+ $options['type'] = 'sendmail';
+ }
+
+ $transportName = $options['type'];
+ if(!Zend_Loader_Autoloader::autoload($transportName))
+ {
+ $transportName = ucfirst(strtolower($transportName));
+
+ if(!Zend_Loader_Autoloader::autoload($transportName))
+ {
+ $transportName = 'Zend_Mail_Transport_' . $transportName;
+ if(!Zend_Loader_Autoloader::autoload($transportName)) {
+ throw new Zend_Application_Resource_Exception(
+ "Specified Mail Transport '{$transportName}'"
+ . 'could not be found'
+ );
+ }
+ }
+ }
+
+ unset($options['type']);
+ unset($options['register']); //@see ZF-11022
+
+ switch($transportName) {
+ case 'Zend_Mail_Transport_Smtp':
+ if(!isset($options['host'])) {
+ throw new Zend_Application_Resource_Exception(
+ 'A host is necessary for smtp transport,'
+ .' but none was given');
+ }
+
+ $transport = new $transportName($options['host'], $options);
+ break;
+ case 'Zend_Mail_Transport_Sendmail':
+ default:
+ $transport = new $transportName($options);
+ break;
+ }
+
+ return $transport;
+ }
+}
diff --git a/library/Zend/Application/Resource/Modules.php b/library/Zend/Application/Resource/Modules.php
new file mode 100644
index 0000000..c01e943
--- /dev/null
+++ b/library/Zend/Application/Resource/Modules.php
@@ -0,0 +1,155 @@
+_bootstraps = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS);
+ parent::__construct($options);
+ }
+
+ /**
+ * Initialize modules
+ *
+ * @return array
+ * @throws Zend_Application_Resource_Exception When bootstrap class was not found
+ */
+ public function init()
+ {
+ $bootstraps = array();
+ $bootstrap = $this->getBootstrap();
+ $bootstrap->bootstrap('FrontController');
+ $front = $bootstrap->getResource('FrontController');
+
+ $modules = $front->getControllerDirectory();
+ $default = $front->getDefaultModule();
+ $curBootstrapClass = get_class($bootstrap);
+ foreach ($modules as $module => $moduleDirectory) {
+ $bootstrapClass = $this->_formatModuleName($module) . '_Bootstrap';
+ if (!class_exists($bootstrapClass, false)) {
+ $bootstrapPath = dirname($moduleDirectory) . '/Bootstrap.php';
+ if (file_exists($bootstrapPath)) {
+ $eMsgTpl = 'Bootstrap file found for module "%s" but bootstrap class "%s" not found';
+ include_once $bootstrapPath;
+ if (($default != $module)
+ && !class_exists($bootstrapClass, false)
+ ) {
+ throw new Zend_Application_Resource_Exception(sprintf(
+ $eMsgTpl, $module, $bootstrapClass
+ ));
+ } elseif ($default == $module) {
+ if (!class_exists($bootstrapClass, false)) {
+ $bootstrapClass = 'Bootstrap';
+ if (!class_exists($bootstrapClass, false)) {
+ throw new Zend_Application_Resource_Exception(sprintf(
+ $eMsgTpl, $module, $bootstrapClass
+ ));
+ }
+ }
+ }
+ } else {
+ continue;
+ }
+ }
+
+ if ($bootstrapClass == $curBootstrapClass) {
+ // If the found bootstrap class matches the one calling this
+ // resource, don't re-execute.
+ continue;
+ }
+
+ $bootstraps[$module] = $bootstrapClass;
+ }
+
+ return $this->_bootstraps = $this->bootstrapBootstraps($bootstraps);
+ }
+
+ /*
+ * Bootstraps the bootstraps found. Allows for easy extension.
+ * @param array $bootstraps Array containing the bootstraps to instantiate
+ */
+ protected function bootstrapBootstraps($bootstraps)
+ {
+ $bootstrap = $this->getBootstrap();
+ $out = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS);
+
+ foreach($bootstraps as $module => $bootstrapClass) {
+ $moduleBootstrap = new $bootstrapClass($bootstrap);
+ $moduleBootstrap->bootstrap();
+ $out[$module] = $moduleBootstrap;
+ }
+
+ return $out;
+ }
+
+ /**
+ * Get bootstraps that have been run
+ *
+ * @return ArrayObject
+ */
+ public function getExecutedBootstraps()
+ {
+ return $this->_bootstraps;
+ }
+
+ /**
+ * Format a module name to the module class prefix
+ *
+ * @param string $name
+ * @return string
+ */
+ protected function _formatModuleName($name)
+ {
+ $name = strtolower($name);
+ $name = str_replace(array('-', '.'), ' ', $name);
+ $name = ucwords($name);
+ $name = str_replace(' ', '', $name);
+ return $name;
+ }
+}
diff --git a/library/Zend/Application/Resource/Multidb.php b/library/Zend/Application/Resource/Multidb.php
new file mode 100644
index 0000000..20ba661
--- /dev/null
+++ b/library/Zend/Application/Resource/Multidb.php
@@ -0,0 +1,210 @@
+
+ * resources.multidb.defaultMetadataCache = "database"
+ *
+ * resources.multidb.db1.adapter = "pdo_mysql"
+ * resources.multidb.db1.host = "localhost"
+ * resources.multidb.db1.username = "webuser"
+ * resources.multidb.db1.password = "XXXX"
+ * resources.multidb.db1.dbname = "db1"
+ * resources.multidb.db1.default = true
+ *
+ * resources.multidb.db2.adapter = "pdo_pgsql"
+ * resources.multidb.db2.host = "example.com"
+ * resources.multidb.db2.username = "dba"
+ * resources.multidb.db2.password = "notthatpublic"
+ * resources.multidb.db2.dbname = "db2"
+ *
+ *
+ * @category Zend
+ * @package Zend_Application
+ * @subpackage Resource
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Application_Resource_Multidb extends Zend_Application_Resource_ResourceAbstract
+{
+ /**
+ * Associative array containing all configured db's
+ *
+ * @var array
+ */
+ protected $_dbs = array();
+
+ /**
+ * An instance of the default db, if set
+ *
+ * @var null|Zend_Db_Adapter_Abstract
+ */
+ protected $_defaultDb;
+
+ /**
+ * Initialize the Database Connections (instances of Zend_Db_Table_Abstract)
+ *
+ * @return Zend_Application_Resource_Multidb
+ */
+ public function init()
+ {
+ $options = $this->getOptions();
+
+ if (isset($options['defaultMetadataCache'])) {
+ $this->_setDefaultMetadataCache($options['defaultMetadataCache']);
+ unset($options['defaultMetadataCache']);
+ }
+
+ foreach ($options as $id => $params) {
+ $adapter = $params['adapter'];
+ $default = (int) (
+ isset($params['isDefaultTableAdapter']) && $params['isDefaultTableAdapter']
+ || isset($params['default']) && $params['default']
+ );
+ unset(
+ $params['adapter'],
+ $params['default'],
+ $params['isDefaultTableAdapter']
+ );
+
+ $this->_dbs[$id] = Zend_Db::factory($adapter, $params);
+
+ if ($default) {
+ $this->_setDefault($this->_dbs[$id]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Determine if the given db(identifier) is the default db.
+ *
+ * @param string|Zend_Db_Adapter_Abstract $db The db to determine whether it's set as default
+ * @return boolean True if the given parameter is configured as default. False otherwise
+ */
+ public function isDefault($db)
+ {
+ if(!$db instanceof Zend_Db_Adapter_Abstract) {
+ $db = $this->getDb($db);
+ }
+
+ return $db === $this->_defaultDb;
+ }
+
+ /**
+ * Retrieve the specified database connection
+ *
+ * @param null|string|Zend_Db_Adapter_Abstract $db The adapter to retrieve.
+ * Null to retrieve the default connection
+ * @return Zend_Db_Adapter_Abstract
+ * @throws Zend_Application_Resource_Exception if the given parameter could not be found
+ */
+ public function getDb($db = null)
+ {
+ if ($db === null) {
+ return $this->getDefaultDb();
+ }
+
+ if (isset($this->_dbs[$db])) {
+ return $this->_dbs[$db];
+ }
+
+ throw new Zend_Application_Resource_Exception(
+ 'A DB adapter was tried to retrieve, but was not configured'
+ );
+ }
+
+ /**
+ * Get the default db connection
+ *
+ * @param boolean $justPickOne If true, a random (the first one in the stack)
+ * connection is returned if no default was set.
+ * If false, null is returned if no default was set.
+ * @return null|Zend_Db_Adapter_Abstract
+ */
+ public function getDefaultDb($justPickOne = true)
+ {
+ if ($this->_defaultDb !== null) {
+ return $this->_defaultDb;
+ }
+
+ if ($justPickOne) {
+ return reset($this->_dbs); // Return first db in db pool
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the default db adapter
+ *
+ * @var Zend_Db_Adapter_Abstract $adapter Adapter to set as default
+ */
+ protected function _setDefault(Zend_Db_Adapter_Abstract $adapter)
+ {
+ Zend_Db_Table::setDefaultAdapter($adapter);
+ $this->_defaultDb = $adapter;
+ }
+
+ /**
+ * Set the default metadata cache
+ *
+ * @param string|Zend_Cache_Core $cache
+ * @return Zend_Application_Resource_Multidb
+ */
+ protected function _setDefaultMetadataCache($cache)
+ {
+ $metadataCache = null;
+
+ if (is_string($cache)) {
+ $bootstrap = $this->getBootstrap();
+ if ($bootstrap instanceof Zend_Application_Bootstrap_ResourceBootstrapper &&
+ $bootstrap->hasPluginResource('CacheManager')
+ ) {
+ $cacheManager = $bootstrap->bootstrap('CacheManager')
+ ->getResource('CacheManager');
+ if (null !== $cacheManager && $cacheManager->hasCache($cache)) {
+ $metadataCache = $cacheManager->getCache($cache);
+ }
+ }
+ } else if ($cache instanceof Zend_Cache_Core) {
+ $metadataCache = $cache;
+ }
+
+ if ($metadataCache instanceof Zend_Cache_Core) {
+ Zend_Db_Table::setDefaultMetadataCache($metadataCache);
+ }
+
+ return $this;
+ }
+}
diff --git a/library/Zend/Application/Resource/Navigation.php b/library/Zend/Application/Resource/Navigation.php
new file mode 100644
index 0000000..580e432
--- /dev/null
+++ b/library/Zend/Application/Resource/Navigation.php
@@ -0,0 +1,128 @@
+_container) {
+ $options = $this->getOptions();
+
+ if(isset($options['defaultPageType'])) {
+ Zend_Navigation_Page::setDefaultPageType($options['defaultPageType']);
+ }
+
+ $pages = isset($options['pages']) ? $options['pages'] : array();
+ $this->_container = new Zend_Navigation($pages);
+ }
+
+ $this->store();
+ return $this->_container;
+ }
+
+ /**
+ * Stores navigation container in registry or Navigation view helper
+ *
+ * @return void
+ */
+ public function store()
+ {
+ $options = $this->getOptions();
+ if (isset($options['storage']['registry']) &&
+ $options['storage']['registry'] == true) {
+ $this->_storeRegistry();
+ } else {
+ $this->_storeHelper();
+ }
+ }
+
+ /**
+ * Stores navigation container in the registry
+ *
+ * @return void
+ */
+ protected function _storeRegistry()
+ {
+ $options = $this->getOptions();
+ if(isset($options['storage']['registry']['key']) &&
+ !is_numeric($options['storage']['registry']['key'])) // see ZF-7461
+ {
+ $key = $options['storage']['registry']['key'];
+ } else {
+ $key = self::DEFAULT_REGISTRY_KEY;
+ }
+
+ Zend_Registry::set($key,$this->getContainer());
+ }
+
+ /**
+ * Stores navigation container in the Navigation helper
+ *
+ * @return void
+ */
+ protected function _storeHelper()
+ {
+ $this->getBootstrap()->bootstrap('view');
+ $view = $this->getBootstrap()->view;
+ $view->getHelper('navigation')->navigation($this->getContainer());
+ }
+
+ /**
+ * Returns navigation container
+ *
+ * @return Zend_Navigation
+ */
+ public function getContainer()
+ {
+ return $this->_container;
+ }
+}
diff --git a/library/Zend/Application/Resource/Resource.php b/library/Zend/Application/Resource/Resource.php
new file mode 100644
index 0000000..670b27d
--- /dev/null
+++ b/library/Zend/Application/Resource/Resource.php
@@ -0,0 +1,80 @@
+setOptions($options);
+ } else if ($options instanceof Zend_Config) {
+ $this->setOptions($options->toArray());
+ }
+ }
+
+ /**
+ * Set options from array
+ *
+ * @param array $options Configuration for resource
+ * @return Zend_Application_Resource_ResourceAbstract
+ */
+ public function setOptions(array $options)
+ {
+ if (array_key_exists('bootstrap', $options)) {
+ $this->setBootstrap($options['bootstrap']);
+ unset($options['bootstrap']);
+ }
+
+ foreach ($options as $key => $value) {
+ if (in_array(strtolower($key), $this->_skipOptions)) {
+ continue;
+ }
+
+ $method = 'set' . strtolower($key);
+ if (method_exists($this, $method)) {
+ $this->$method($value);
+ }
+ }
+
+ $this->_options = $this->mergeOptions($this->_options, $options);
+
+ return $this;
+ }
+
+ /**
+ * Retrieve resource options
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
+ /**
+ * Merge options recursively
+ *
+ * @param array $array1
+ * @param mixed $array2
+ * @return array
+ */
+ public function mergeOptions(array $array1, $array2 = null)
+ {
+ if (is_array($array2)) {
+ foreach ($array2 as $key => $val) {
+ if (is_array($array2[$key])) {
+ $array1[$key] = (array_key_exists($key, $array1) && is_array($array1[$key]))
+ ? $this->mergeOptions($array1[$key], $array2[$key])
+ : $array2[$key];
+ } else {
+ $array1[$key] = $val;
+ }
+ }
+ }
+ return $array1;
+ }
+
+ /**
+ * Set the bootstrap to which the resource is attached
+ *
+ * @param Zend_Application_Bootstrap_Bootstrapper $bootstrap
+ * @return Zend_Application_Resource_Resource
+ */
+ public function setBootstrap(Zend_Application_Bootstrap_Bootstrapper $bootstrap)
+ {
+ $this->_bootstrap = $bootstrap;
+ return $this;
+ }
+
+ /**
+ * Retrieve the bootstrap to which the resource is attached
+ *
+ * @return null|Zend_Application_Bootstrap_Bootstrapper
+ */
+ public function getBootstrap()
+ {
+ return $this->_bootstrap;
+ }
+}
diff --git a/library/Zend/Application/Resource/Router.php b/library/Zend/Application/Resource/Router.php
new file mode 100644
index 0000000..9a570d2
--- /dev/null
+++ b/library/Zend/Application/Resource/Router.php
@@ -0,0 +1,87 @@
+getRouter();
+ }
+
+ /**
+ * Retrieve router object
+ *
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function getRouter()
+ {
+ if (null === $this->_router) {
+ $bootstrap = $this->getBootstrap();
+ $bootstrap->bootstrap('FrontController');
+ $this->_router = $bootstrap->getContainer()->frontcontroller->getRouter();
+
+ $options = $this->getOptions();
+ if (!isset($options['routes'])) {
+ $options['routes'] = array();
+ }
+
+ if (isset($options['chainNameSeparator'])) {
+ $this->_router->setChainNameSeparator($options['chainNameSeparator']);
+ }
+
+ if (isset($options['useRequestParametersAsGlobal'])) {
+ $this->_router->useRequestParametersAsGlobal($options['useRequestParametersAsGlobal']);
+ }
+
+ $this->_router->addConfig(new Zend_Config($options['routes']));
+ }
+
+ return $this->_router;
+ }
+}
diff --git a/library/Zend/Application/Resource/Session.php b/library/Zend/Application/Resource/Session.php
new file mode 100644
index 0000000..212e61d
--- /dev/null
+++ b/library/Zend/Application/Resource/Session.php
@@ -0,0 +1,118 @@
+_saveHandler = $saveHandler;
+ return $this;
+ }
+
+ /**
+ * Get session save handler
+ *
+ * @return Zend_Session_SaveHandler_Interface
+ */
+ public function getSaveHandler()
+ {
+ if (!$this->_saveHandler instanceof Zend_Session_SaveHandler_Interface) {
+ if (is_array($this->_saveHandler)) {
+ if (!array_key_exists('class', $this->_saveHandler)) {
+ throw new Zend_Application_Resource_Exception('Session save handler class not provided in options');
+ }
+ $options = array();
+ if (array_key_exists('options', $this->_saveHandler)) {
+ $options = $this->_saveHandler['options'];
+ }
+ $this->_saveHandler = $this->_saveHandler['class'];
+ $this->_saveHandler = new $this->_saveHandler($options);
+ } elseif (is_string($this->_saveHandler)) {
+ $this->_saveHandler = new $this->_saveHandler();
+ }
+
+ if (!$this->_saveHandler instanceof Zend_Session_SaveHandler_Interface) {
+ throw new Zend_Application_Resource_Exception('Invalid session save handler');
+ }
+ }
+ return $this->_saveHandler;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function _hasSaveHandler()
+ {
+ return ($this->_saveHandler !== null);
+ }
+
+ /**
+ * Defined by Zend_Application_Resource_Resource
+ *
+ * @return void
+ */
+ public function init()
+ {
+ $options = array_change_key_case($this->getOptions(), CASE_LOWER);
+ if (isset($options['savehandler'])) {
+ unset($options['savehandler']);
+ }
+
+ if (count($options) > 0) {
+ Zend_Session::setOptions($options);
+ }
+
+ if ($this->_hasSaveHandler()) {
+ Zend_Session::setSaveHandler($this->getSaveHandler());
+ }
+ }
+}
diff --git a/library/Zend/Application/Resource/Translate.php b/library/Zend/Application/Resource/Translate.php
new file mode 100644
index 0000000..42f8386
--- /dev/null
+++ b/library/Zend/Application/Resource/Translate.php
@@ -0,0 +1,134 @@
+getTranslate();
+ }
+
+ /**
+ * Retrieve translate object
+ *
+ * @return Zend_Translate
+ * @throws Zend_Application_Resource_Exception if registry key was used
+ * already but is no instance of Zend_Translate
+ */
+ public function getTranslate()
+ {
+ if (null === $this->_translate) {
+ $options = $this->getOptions();
+
+ if (!isset($options['content']) && !isset($options['data'])) {
+ require_once 'Zend/Application/Resource/Exception.php';
+ throw new Zend_Application_Resource_Exception('No translation source data provided.');
+ } else if (array_key_exists('content', $options) && array_key_exists('data', $options)) {
+ require_once 'Zend/Application/Resource/Exception.php';
+ throw new Zend_Application_Resource_Exception(
+ 'Conflict on translation source data: choose only one key between content and data.'
+ );
+ }
+
+ if (empty($options['adapter'])) {
+ $options['adapter'] = Zend_Translate::AN_ARRAY;
+ }
+
+ if (!empty($options['data'])) {
+ $options['content'] = $options['data'];
+ unset($options['data']);
+ }
+
+ if (isset($options['options'])) {
+ foreach($options['options'] as $key => $value) {
+ $options[$key] = $value;
+ }
+ }
+
+ if (!empty($options['cache']) && is_string($options['cache'])) {
+ $bootstrap = $this->getBootstrap();
+ if ($bootstrap instanceof Zend_Application_Bootstrap_ResourceBootstrapper &&
+ $bootstrap->hasPluginResource('CacheManager')
+ ) {
+ $cacheManager = $bootstrap->bootstrap('CacheManager')
+ ->getResource('CacheManager');
+ if (null !== $cacheManager &&
+ $cacheManager->hasCache($options['cache'])
+ ) {
+ $options['cache'] = $cacheManager->getCache($options['cache']);
+ }
+ }
+ }
+
+ $key = (isset($options['registry_key']) && !is_numeric($options['registry_key']))
+ ? $options['registry_key']
+ : self::DEFAULT_REGISTRY_KEY;
+ unset($options['registry_key']);
+
+ if(Zend_Registry::isRegistered($key)) {
+ $translate = Zend_Registry::get($key);
+ if(!$translate instanceof Zend_Translate) {
+ require_once 'Zend/Application/Resource/Exception.php';
+ throw new Zend_Application_Resource_Exception($key
+ . ' already registered in registry but is '
+ . 'no instance of Zend_Translate');
+ }
+
+ $translate->addTranslation($options);
+ $this->_translate = $translate;
+ } else {
+ $this->_translate = new Zend_Translate($options);
+ Zend_Registry::set($key, $this->_translate);
+ }
+ }
+
+ return $this->_translate;
+ }
+}
diff --git a/library/Zend/Application/Resource/Useragent.php b/library/Zend/Application/Resource/Useragent.php
new file mode 100644
index 0000000..d7411a4
--- /dev/null
+++ b/library/Zend/Application/Resource/Useragent.php
@@ -0,0 +1,72 @@
+getUserAgent();
+
+ // Optionally seed the UserAgent view helper
+ $bootstrap = $this->getBootstrap();
+ if ($bootstrap->hasResource('view') || $bootstrap->hasPluginResource('view')) {
+ $bootstrap->bootstrap('view');
+ $view = $bootstrap->getResource('view');
+ if (null !== $view) {
+ $view->userAgent($userAgent);
+ }
+ }
+
+ return $userAgent;
+ }
+
+ /**
+ * Get UserAgent instance
+ *
+ * @return Zend_Http_UserAgent
+ */
+ public function getUserAgent()
+ {
+ if (null === $this->_userAgent) {
+ $options = $this->getOptions();
+ $this->_userAgent = new Zend_Http_UserAgent($options);
+ }
+
+ return $this->_userAgent;
+ }
+}
diff --git a/library/Zend/Application/Resource/View.php b/library/Zend/Application/Resource/View.php
new file mode 100644
index 0000000..a5dac47
--- /dev/null
+++ b/library/Zend/Application/Resource/View.php
@@ -0,0 +1,86 @@
+getView();
+
+ $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
+ $viewRenderer->setView($view);
+ return $view;
+ }
+
+ /**
+ * Retrieve view object
+ *
+ * @return Zend_View
+ */
+ public function getView()
+ {
+ if (null === $this->_view) {
+ $options = $this->getOptions();
+ $this->_view = new Zend_View($options);
+
+ if (isset($options['doctype'])) {
+ $this->_view->doctype()->setDoctype(strtoupper($options['doctype']));
+ if (isset($options['charset']) && $this->_view->doctype()->isHtml5()) {
+ $this->_view->headMeta()->setCharset($options['charset']);
+ }
+ }
+ if (isset($options['contentType'])) {
+ $this->_view->headMeta()->appendHttpEquiv('Content-Type', $options['contentType']);
+ }
+ if (isset($options['assign']) && is_array($options['assign'])) {
+ $this->_view->assign($options['assign']);
+ }
+ }
+ return $this->_view;
+ }
+}
diff --git a/library/Zend/Auth.php b/library/Zend/Auth.php
new file mode 100644
index 0000000..a09e53a
--- /dev/null
+++ b/library/Zend/Auth.php
@@ -0,0 +1,169 @@
+_storage) {
+ /**
+ * @see Zend_Auth_Storage_Session
+ */
+ require_once 'Zend/Auth/Storage/Session.php';
+ $this->setStorage(new Zend_Auth_Storage_Session());
+ }
+
+ return $this->_storage;
+ }
+
+ /**
+ * Sets the persistent storage handler
+ *
+ * @param Zend_Auth_Storage_Interface $storage
+ * @return Zend_Auth Provides a fluent interface
+ */
+ public function setStorage(Zend_Auth_Storage_Interface $storage)
+ {
+ $this->_storage = $storage;
+ return $this;
+ }
+
+ /**
+ * Authenticates against the supplied adapter
+ *
+ * @param Zend_Auth_Adapter_Interface $adapter
+ * @return Zend_Auth_Result
+ */
+ public function authenticate(Zend_Auth_Adapter_Interface $adapter)
+ {
+ $result = $adapter->authenticate();
+
+ /**
+ * ZF-7546 - prevent multiple succesive calls from storing inconsistent results
+ * Ensure storage has clean state
+ */
+ if ($this->hasIdentity()) {
+ $this->clearIdentity();
+ }
+
+ if ($result->isValid()) {
+ $this->getStorage()->write($result->getIdentity());
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns true if and only if an identity is available from storage
+ *
+ * @return boolean
+ */
+ public function hasIdentity()
+ {
+ return !$this->getStorage()->isEmpty();
+ }
+
+ /**
+ * Returns the identity from storage or null if no identity is available
+ *
+ * @return mixed|null
+ */
+ public function getIdentity()
+ {
+ $storage = $this->getStorage();
+
+ if ($storage->isEmpty()) {
+ return null;
+ }
+
+ return $storage->read();
+ }
+
+ /**
+ * Clears the identity from persistent storage
+ *
+ * @return void
+ */
+ public function clearIdentity()
+ {
+ $this->getStorage()->clear();
+ }
+}
diff --git a/library/Zend/Auth/Adapter/DbTable.php b/library/Zend/Auth/Adapter/DbTable.php
new file mode 100644
index 0000000..0983860
--- /dev/null
+++ b/library/Zend/Auth/Adapter/DbTable.php
@@ -0,0 +1,561 @@
+_setDbAdapter($zendDb);
+
+ if (null !== $tableName) {
+ $this->setTableName($tableName);
+ }
+
+ if (null !== $identityColumn) {
+ $this->setIdentityColumn($identityColumn);
+ }
+
+ if (null !== $credentialColumn) {
+ $this->setCredentialColumn($credentialColumn);
+ }
+
+ if (null !== $credentialTreatment) {
+ $this->setCredentialTreatment($credentialTreatment);
+ }
+ }
+
+ /**
+ * _setDbAdapter() - set the database adapter to be used for quering
+ *
+ * @param Zend_Db_Adapter_Abstract
+ * @throws Zend_Auth_Adapter_Exception
+ * @return Zend_Auth_Adapter_DbTable
+ */
+ protected function _setDbAdapter(Zend_Db_Adapter_Abstract $zendDb = null)
+ {
+ $this->_zendDb = $zendDb;
+
+ /**
+ * If no adapter is specified, fetch default database adapter.
+ */
+ if(null === $this->_zendDb) {
+ require_once 'Zend/Db/Table/Abstract.php';
+ $this->_zendDb = Zend_Db_Table_Abstract::getDefaultAdapter();
+ if (null === $this->_zendDb) {
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('No database adapter present');
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * setTableName() - set the table name to be used in the select query
+ *
+ * @param string $tableName
+ * @return Zend_Auth_Adapter_DbTable Provides a fluent interface
+ */
+ public function setTableName($tableName)
+ {
+ $this->_tableName = $tableName;
+ return $this;
+ }
+
+ /**
+ * setIdentityColumn() - set the column name to be used as the identity column
+ *
+ * @param string $identityColumn
+ * @return Zend_Auth_Adapter_DbTable Provides a fluent interface
+ */
+ public function setIdentityColumn($identityColumn)
+ {
+ $this->_identityColumn = $identityColumn;
+ return $this;
+ }
+
+ /**
+ * setCredentialColumn() - set the column name to be used as the credential column
+ *
+ * @param string $credentialColumn
+ * @return Zend_Auth_Adapter_DbTable Provides a fluent interface
+ */
+ public function setCredentialColumn($credentialColumn)
+ {
+ $this->_credentialColumn = $credentialColumn;
+ return $this;
+ }
+
+ /**
+ * setCredentialTreatment() - allows the developer to pass a parameterized string that is
+ * used to transform or treat the input credential data.
+ *
+ * In many cases, passwords and other sensitive data are encrypted, hashed, encoded,
+ * obscured, or otherwise treated through some function or algorithm. By specifying a
+ * parameterized treatment string with this method, a developer may apply arbitrary SQL
+ * upon input credential data.
+ *
+ * Examples:
+ *
+ * 'PASSWORD(?)'
+ * 'MD5(?)'
+ *
+ * @param string $treatment
+ * @return Zend_Auth_Adapter_DbTable Provides a fluent interface
+ */
+ public function setCredentialTreatment($treatment)
+ {
+ $this->_credentialTreatment = $treatment;
+ return $this;
+ }
+
+ /**
+ * setIdentity() - set the value to be used as the identity
+ *
+ * @param string $value
+ * @return Zend_Auth_Adapter_DbTable Provides a fluent interface
+ */
+ public function setIdentity($value)
+ {
+ $this->_identity = $value;
+ return $this;
+ }
+
+ /**
+ * setCredential() - set the credential value to be used, optionally can specify a treatment
+ * to be used, should be supplied in parameterized form, such as 'MD5(?)' or 'PASSWORD(?)'
+ *
+ * @param string $credential
+ * @return Zend_Auth_Adapter_DbTable Provides a fluent interface
+ */
+ public function setCredential($credential)
+ {
+ $this->_credential = $credential;
+ return $this;
+ }
+
+ /**
+ * setAmbiguityIdentity() - sets a flag for usage of identical identities
+ * with unique credentials. It accepts integers (0, 1) or boolean (true,
+ * false) parameters. Default is false.
+ *
+ * @param int|bool $flag
+ * @return Zend_Auth_Adapter_DbTable
+ */
+ public function setAmbiguityIdentity($flag)
+ {
+ if (is_integer($flag)) {
+ $this->_ambiguityIdentity = (1 === $flag ? true : false);
+ } elseif (is_bool($flag)) {
+ $this->_ambiguityIdentity = $flag;
+ }
+ return $this;
+ }
+ /**
+ * getAmbiguityIdentity() - returns TRUE for usage of multiple identical
+ * identies with different credentials, FALSE if not used.
+ *
+ * @return bool
+ */
+ public function getAmbiguityIdentity()
+ {
+ return $this->_ambiguityIdentity;
+ }
+
+ /**
+ * getDbSelect() - Return the preauthentication Db Select object for userland select query modification
+ *
+ * @return Zend_Db_Select
+ */
+ public function getDbSelect()
+ {
+ if ($this->_dbSelect == null) {
+ $this->_dbSelect = $this->_zendDb->select();
+ }
+
+ return $this->_dbSelect;
+ }
+
+ /**
+ * getResultRowObject() - Returns the result row as a stdClass object
+ *
+ * @param string|array $returnColumns
+ * @param string|array $omitColumns
+ * @return stdClass|boolean
+ */
+ public function getResultRowObject($returnColumns = null, $omitColumns = null)
+ {
+ if (!$this->_resultRow) {
+ return false;
+ }
+
+ $returnObject = new stdClass();
+
+ if (null !== $returnColumns) {
+
+ $availableColumns = array_keys($this->_resultRow);
+ foreach ( (array) $returnColumns as $returnColumn) {
+ if (in_array($returnColumn, $availableColumns)) {
+ $returnObject->{$returnColumn} = $this->_resultRow[$returnColumn];
+ }
+ }
+ return $returnObject;
+
+ } elseif (null !== $omitColumns) {
+
+ $omitColumns = (array) $omitColumns;
+ foreach ($this->_resultRow as $resultColumn => $resultValue) {
+ if (!in_array($resultColumn, $omitColumns)) {
+ $returnObject->{$resultColumn} = $resultValue;
+ }
+ }
+ return $returnObject;
+
+ } else {
+
+ foreach ($this->_resultRow as $resultColumn => $resultValue) {
+ $returnObject->{$resultColumn} = $resultValue;
+ }
+ return $returnObject;
+
+ }
+ }
+
+ /**
+ * authenticate() - defined by Zend_Auth_Adapter_Interface. This method is called to
+ * attempt an authentication. Previous to this call, this adapter would have already
+ * been configured with all necessary information to successfully connect to a database
+ * table and attempt to find a record matching the provided identity.
+ *
+ * @throws Zend_Auth_Adapter_Exception if answering the authentication query is impossible
+ * @return Zend_Auth_Result
+ */
+ public function authenticate()
+ {
+ $this->_authenticateSetup();
+ $dbSelect = $this->_authenticateCreateSelect();
+ $resultIdentities = $this->_authenticateQuerySelect($dbSelect);
+
+ if ( ($authResult = $this->_authenticateValidateResultSet($resultIdentities)) instanceof Zend_Auth_Result) {
+ return $authResult;
+ }
+
+ if (true === $this->getAmbiguityIdentity()) {
+ $validIdentities = array ();
+ $zendAuthCredentialMatchColumn = $this->_zendDb->foldCase('zend_auth_credential_match');
+ foreach ($resultIdentities as $identity) {
+ if (1 === (int) $identity[$zendAuthCredentialMatchColumn]) {
+ $validIdentities[] = $identity;
+ }
+ }
+ $resultIdentities = $validIdentities;
+ }
+
+ $authResult = $this->_authenticateValidateResult(array_shift($resultIdentities));
+ return $authResult;
+ }
+
+ /**
+ * _authenticateSetup() - This method abstracts the steps involved with
+ * making sure that this adapter was indeed setup properly with all
+ * required pieces of information.
+ *
+ * @throws Zend_Auth_Adapter_Exception - in the event that setup was not done properly
+ * @return true
+ */
+ protected function _authenticateSetup()
+ {
+ $exception = null;
+
+ if ($this->_tableName == '') {
+ $exception = 'A table must be supplied for the Zend_Auth_Adapter_DbTable authentication adapter.';
+ } elseif ($this->_identityColumn == '') {
+ $exception = 'An identity column must be supplied for the Zend_Auth_Adapter_DbTable authentication adapter.';
+ } elseif ($this->_credentialColumn == '') {
+ $exception = 'A credential column must be supplied for the Zend_Auth_Adapter_DbTable authentication adapter.';
+ } elseif ($this->_identity == '') {
+ $exception = 'A value for the identity was not provided prior to authentication with Zend_Auth_Adapter_DbTable.';
+ } elseif ($this->_credential === null) {
+ $exception = 'A credential value was not provided prior to authentication with Zend_Auth_Adapter_DbTable.';
+ }
+
+ if (null !== $exception) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception($exception);
+ }
+
+ $this->_authenticateResultInfo = array(
+ 'code' => Zend_Auth_Result::FAILURE,
+ 'identity' => $this->_identity,
+ 'messages' => array()
+ );
+
+ return true;
+ }
+
+ /**
+ * _authenticateCreateSelect() - This method creates a Zend_Db_Select object that
+ * is completely configured to be queried against the database.
+ *
+ * @return Zend_Db_Select
+ */
+ protected function _authenticateCreateSelect()
+ {
+ // build credential expression
+ if (empty($this->_credentialTreatment) || (strpos($this->_credentialTreatment, '?') === false)) {
+ $this->_credentialTreatment = '?';
+ }
+
+ $credentialExpression = new Zend_Db_Expr(
+ '(CASE WHEN ' .
+ $this->_zendDb->quoteInto(
+ $this->_zendDb->quoteIdentifier($this->_credentialColumn, true)
+ . ' = ' . $this->_credentialTreatment, $this->_credential
+ )
+ . ' THEN 1 ELSE 0 END) AS '
+ . $this->_zendDb->quoteIdentifier(
+ $this->_zendDb->foldCase('zend_auth_credential_match')
+ )
+ );
+
+ // get select
+ $dbSelect = clone $this->getDbSelect();
+ $dbSelect->from($this->_tableName, array('*', $credentialExpression))
+ ->where($this->_zendDb->quoteIdentifier($this->_identityColumn, true) . ' = ?', $this->_identity);
+
+ return $dbSelect;
+ }
+
+ /**
+ * _authenticateQuerySelect() - This method accepts a Zend_Db_Select object and
+ * performs a query against the database with that object.
+ *
+ * @param Zend_Db_Select $dbSelect
+ * @throws Zend_Auth_Adapter_Exception - when an invalid select
+ * object is encountered
+ * @return array
+ */
+ protected function _authenticateQuerySelect(Zend_Db_Select $dbSelect)
+ {
+ try {
+ if ($this->_zendDb->getFetchMode() != Zend_DB::FETCH_ASSOC) {
+ $origDbFetchMode = $this->_zendDb->getFetchMode();
+ $this->_zendDb->setFetchMode(Zend_DB::FETCH_ASSOC);
+ }
+ $resultIdentities = $this->_zendDb->fetchAll($dbSelect);
+ if (isset($origDbFetchMode)) {
+ $this->_zendDb->setFetchMode($origDbFetchMode);
+ unset($origDbFetchMode);
+ }
+ } catch (Exception $e) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('The supplied parameters to Zend_Auth_Adapter_DbTable failed to '
+ . 'produce a valid sql statement, please check table and column names '
+ . 'for validity.', 0, $e);
+ }
+ return $resultIdentities;
+ }
+
+ /**
+ * _authenticateValidateResultSet() - This method attempts to make
+ * certain that only one record was returned in the resultset
+ *
+ * @param array $resultIdentities
+ * @return true|Zend_Auth_Result
+ */
+ protected function _authenticateValidateResultSet(array $resultIdentities)
+ {
+
+ if (count($resultIdentities) < 1) {
+ $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
+ $this->_authenticateResultInfo['messages'][] = 'A record with the supplied identity could not be found.';
+ return $this->_authenticateCreateAuthResult();
+ } elseif (count($resultIdentities) > 1 && false === $this->getAmbiguityIdentity()) {
+ $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS;
+ $this->_authenticateResultInfo['messages'][] = 'More than one record matches the supplied identity.';
+ return $this->_authenticateCreateAuthResult();
+ }
+
+ return true;
+ }
+
+ /**
+ * _authenticateValidateResult() - This method attempts to validate that
+ * the record in the resultset is indeed a record that matched the
+ * identity provided to this adapter.
+ *
+ * @param array $resultIdentity
+ * @return Zend_Auth_Result
+ */
+ protected function _authenticateValidateResult($resultIdentity)
+ {
+ $zendAuthCredentialMatchColumn = $this->_zendDb->foldCase('zend_auth_credential_match');
+
+ if ($resultIdentity[$zendAuthCredentialMatchColumn] != '1') {
+ $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
+ $this->_authenticateResultInfo['messages'][] = 'Supplied credential is invalid.';
+ return $this->_authenticateCreateAuthResult();
+ }
+
+ unset($resultIdentity[$zendAuthCredentialMatchColumn]);
+ $this->_resultRow = $resultIdentity;
+
+ $this->_authenticateResultInfo['code'] = Zend_Auth_Result::SUCCESS;
+ $this->_authenticateResultInfo['messages'][] = 'Authentication successful.';
+ return $this->_authenticateCreateAuthResult();
+ }
+
+ /**
+ * _authenticateCreateAuthResult() - Creates a Zend_Auth_Result object from
+ * the information that has been collected during the authenticate() attempt.
+ *
+ * @return Zend_Auth_Result
+ */
+ protected function _authenticateCreateAuthResult()
+ {
+ return new Zend_Auth_Result(
+ $this->_authenticateResultInfo['code'],
+ $this->_authenticateResultInfo['identity'],
+ $this->_authenticateResultInfo['messages']
+ );
+ }
+
+}
diff --git a/library/Zend/Auth/Adapter/Digest.php b/library/Zend/Auth/Adapter/Digest.php
new file mode 100644
index 0000000..065ea9e
--- /dev/null
+++ b/library/Zend/Auth/Adapter/Digest.php
@@ -0,0 +1,252 @@
+$methodName($$option);
+ }
+ }
+ }
+
+ /**
+ * Returns the filename option value or null if it has not yet been set
+ *
+ * @return string|null
+ */
+ public function getFilename()
+ {
+ return $this->_filename;
+ }
+
+ /**
+ * Sets the filename option value
+ *
+ * @param mixed $filename
+ * @return Zend_Auth_Adapter_Digest Provides a fluent interface
+ */
+ public function setFilename($filename)
+ {
+ $this->_filename = (string) $filename;
+ return $this;
+ }
+
+ /**
+ * Returns the realm option value or null if it has not yet been set
+ *
+ * @return string|null
+ */
+ public function getRealm()
+ {
+ return $this->_realm;
+ }
+
+ /**
+ * Sets the realm option value
+ *
+ * @param mixed $realm
+ * @return Zend_Auth_Adapter_Digest Provides a fluent interface
+ */
+ public function setRealm($realm)
+ {
+ $this->_realm = (string) $realm;
+ return $this;
+ }
+
+ /**
+ * Returns the username option value or null if it has not yet been set
+ *
+ * @return string|null
+ */
+ public function getUsername()
+ {
+ return $this->_username;
+ }
+
+ /**
+ * Sets the username option value
+ *
+ * @param mixed $username
+ * @return Zend_Auth_Adapter_Digest Provides a fluent interface
+ */
+ public function setUsername($username)
+ {
+ $this->_username = (string) $username;
+ return $this;
+ }
+
+ /**
+ * Returns the password option value or null if it has not yet been set
+ *
+ * @return string|null
+ */
+ public function getPassword()
+ {
+ return $this->_password;
+ }
+
+ /**
+ * Sets the password option value
+ *
+ * @param mixed $password
+ * @return Zend_Auth_Adapter_Digest Provides a fluent interface
+ */
+ public function setPassword($password)
+ {
+ $this->_password = (string) $password;
+ return $this;
+ }
+
+ /**
+ * Defined by Zend_Auth_Adapter_Interface
+ *
+ * @throws Zend_Auth_Adapter_Exception
+ * @return Zend_Auth_Result
+ */
+ public function authenticate()
+ {
+ $optionsRequired = array('filename', 'realm', 'username', 'password');
+ foreach ($optionsRequired as $optionRequired) {
+ if (null === $this->{"_$optionRequired"}) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception("Option '$optionRequired' must be set before authentication");
+ }
+ }
+
+ if (false === ($fileHandle = @fopen($this->_filename, 'r'))) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception("Cannot open '$this->_filename' for reading");
+ }
+
+ $id = "$this->_username:$this->_realm";
+ $idLength = strlen($id);
+
+ $result = array(
+ 'code' => Zend_Auth_Result::FAILURE,
+ 'identity' => array(
+ 'realm' => $this->_realm,
+ 'username' => $this->_username,
+ ),
+ 'messages' => array()
+ );
+
+ while ($line = trim(fgets($fileHandle))) {
+ if (substr($line, 0, $idLength) === $id) {
+ if ($this->_secureStringCompare(substr($line, -32), md5("$this->_username:$this->_realm:$this->_password"))) {
+ $result['code'] = Zend_Auth_Result::SUCCESS;
+ } else {
+ $result['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
+ $result['messages'][] = 'Password incorrect';
+ }
+ return new Zend_Auth_Result($result['code'], $result['identity'], $result['messages']);
+ }
+ }
+
+ $result['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
+ $result['messages'][] = "Username '$this->_username' and realm '$this->_realm' combination not found";
+ return new Zend_Auth_Result($result['code'], $result['identity'], $result['messages']);
+ }
+
+ /**
+ * Securely compare two strings for equality while avoided C level memcmp()
+ * optimisations capable of leaking timing information useful to an attacker
+ * attempting to iteratively guess the unknown string (e.g. password) being
+ * compared against.
+ *
+ * @param string $a
+ * @param string $b
+ * @return bool
+ */
+ protected function _secureStringCompare($a, $b)
+ {
+ if (strlen($a) !== strlen($b)) {
+ return false;
+ }
+ $result = 0;
+ for ($i = 0; $i < strlen($a); $i++) {
+ $result |= ord($a[$i]) ^ ord($b[$i]);
+ }
+ return $result == 0;
+ }
+}
diff --git a/library/Zend/Auth/Adapter/Exception.php b/library/Zend/Auth/Adapter/Exception.php
new file mode 100644
index 0000000..92eed9e
--- /dev/null
+++ b/library/Zend/Auth/Adapter/Exception.php
@@ -0,0 +1,38 @@
+ 'basic'|'digest'|'basic digest'
+ * 'realm' =>
+ * 'digest_domains' => Space-delimited list of URIs
+ * 'nonce_timeout' =>
+ * 'use_opaque' => Whether to send the opaque value in the header
+ * 'alogrithm' => See $_supportedAlgos. Default: MD5
+ * 'proxy_auth' => Whether to do authentication as a Proxy
+ * @throws Zend_Auth_Adapter_Exception
+ * @return void
+ */
+ public function __construct(array $config)
+ {
+ if (!extension_loaded('hash')) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception(__CLASS__ . ' requires the \'hash\' extension');
+ }
+
+ $this->_request = null;
+ $this->_response = null;
+ $this->_ieNoOpaque = false;
+
+
+ if (empty($config['accept_schemes'])) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Config key \'accept_schemes\' is required');
+ }
+
+ $schemes = explode(' ', $config['accept_schemes']);
+ $this->_acceptSchemes = array_intersect($schemes, $this->_supportedSchemes);
+ if (empty($this->_acceptSchemes)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('No supported schemes given in \'accept_schemes\'. Valid values: '
+ . implode(', ', $this->_supportedSchemes));
+ }
+
+ // Double-quotes are used to delimit the realm string in the HTTP header,
+ // and colons are field delimiters in the password file.
+ if (empty($config['realm']) ||
+ !ctype_print($config['realm']) ||
+ strpos($config['realm'], ':') !== false ||
+ strpos($config['realm'], '"') !== false) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Config key \'realm\' is required, and must contain only printable '
+ . 'characters, excluding quotation marks and colons');
+ } else {
+ $this->_realm = $config['realm'];
+ }
+
+ if (in_array('digest', $this->_acceptSchemes)) {
+ if (empty($config['digest_domains']) ||
+ !ctype_print($config['digest_domains']) ||
+ strpos($config['digest_domains'], '"') !== false) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Config key \'digest_domains\' is required, and must contain '
+ . 'only printable characters, excluding quotation marks');
+ } else {
+ $this->_domains = $config['digest_domains'];
+ }
+
+ if (empty($config['nonce_timeout']) ||
+ !is_numeric($config['nonce_timeout'])) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Config key \'nonce_timeout\' is required, and must be an '
+ . 'integer');
+ } else {
+ $this->_nonceTimeout = (int) $config['nonce_timeout'];
+ }
+
+ // We use the opaque value unless explicitly told not to
+ if (isset($config['use_opaque']) && false == (bool) $config['use_opaque']) {
+ $this->_useOpaque = false;
+ } else {
+ $this->_useOpaque = true;
+ }
+
+ if (isset($config['algorithm']) && in_array($config['algorithm'], $this->_supportedAlgos)) {
+ $this->_algo = $config['algorithm'];
+ } else {
+ $this->_algo = 'MD5';
+ }
+ }
+
+ // Don't be a proxy unless explicitly told to do so
+ if (isset($config['proxy_auth']) && true == (bool) $config['proxy_auth']) {
+ $this->_imaProxy = true; // I'm a Proxy
+ } else {
+ $this->_imaProxy = false;
+ }
+ }
+
+ /**
+ * Setter for the _basicResolver property
+ *
+ * @param Zend_Auth_Adapter_Http_Resolver_Interface $resolver
+ * @return Zend_Auth_Adapter_Http Provides a fluent interface
+ */
+ public function setBasicResolver(Zend_Auth_Adapter_Http_Resolver_Interface $resolver)
+ {
+ $this->_basicResolver = $resolver;
+
+ return $this;
+ }
+
+ /**
+ * Getter for the _basicResolver property
+ *
+ * @return Zend_Auth_Adapter_Http_Resolver_Interface
+ */
+ public function getBasicResolver()
+ {
+ return $this->_basicResolver;
+ }
+
+ /**
+ * Setter for the _digestResolver property
+ *
+ * @param Zend_Auth_Adapter_Http_Resolver_Interface $resolver
+ * @return Zend_Auth_Adapter_Http Provides a fluent interface
+ */
+ public function setDigestResolver(Zend_Auth_Adapter_Http_Resolver_Interface $resolver)
+ {
+ $this->_digestResolver = $resolver;
+
+ return $this;
+ }
+
+ /**
+ * Getter for the _digestResolver property
+ *
+ * @return Zend_Auth_Adapter_Http_Resolver_Interface
+ */
+ public function getDigestResolver()
+ {
+ return $this->_digestResolver;
+ }
+
+ /**
+ * Setter for the Request object
+ *
+ * @param Zend_Controller_Request_Http $request
+ * @return Zend_Auth_Adapter_Http Provides a fluent interface
+ */
+ public function setRequest(Zend_Controller_Request_Http $request)
+ {
+ $this->_request = $request;
+
+ return $this;
+ }
+
+ /**
+ * Getter for the Request object
+ *
+ * @return Zend_Controller_Request_Http
+ */
+ public function getRequest()
+ {
+ return $this->_request;
+ }
+
+ /**
+ * Setter for the Response object
+ *
+ * @param Zend_Controller_Response_Http $response
+ * @return Zend_Auth_Adapter_Http Provides a fluent interface
+ */
+ public function setResponse(Zend_Controller_Response_Http $response)
+ {
+ $this->_response = $response;
+
+ return $this;
+ }
+
+ /**
+ * Getter for the Response object
+ *
+ * @return Zend_Controller_Response_Http
+ */
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+
+ /**
+ * Authenticate
+ *
+ * @throws Zend_Auth_Adapter_Exception
+ * @return Zend_Auth_Result
+ */
+ public function authenticate()
+ {
+ if (empty($this->_request) ||
+ empty($this->_response)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Request and Response objects must be set before calling '
+ . 'authenticate()');
+ }
+
+ if ($this->_imaProxy) {
+ $getHeader = 'Proxy-Authorization';
+ } else {
+ $getHeader = 'Authorization';
+ }
+
+ $authHeader = $this->_request->getHeader($getHeader);
+ if (!$authHeader) {
+ return $this->_challengeClient();
+ }
+
+ list($clientScheme) = explode(' ', $authHeader);
+ $clientScheme = strtolower($clientScheme);
+
+ // The server can issue multiple challenges, but the client should
+ // answer with only the selected auth scheme.
+ if (!in_array($clientScheme, $this->_supportedSchemes)) {
+ $this->_response->setHttpResponseCode(400);
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE_UNCATEGORIZED,
+ array(),
+ array('Client requested an incorrect or unsupported authentication scheme')
+ );
+ }
+
+ // client sent a scheme that is not the one required
+ if (!in_array($clientScheme, $this->_acceptSchemes)) {
+ // challenge again the client
+ return $this->_challengeClient();
+ }
+
+ switch ($clientScheme) {
+ case 'basic':
+ $result = $this->_basicAuth($authHeader);
+ break;
+ case 'digest':
+ $result = $this->_digestAuth($authHeader);
+ break;
+ default:
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Unsupported authentication scheme');
+ }
+
+ return $result;
+ }
+
+ /**
+ * Challenge Client
+ *
+ * Sets a 401 or 407 Unauthorized response code, and creates the
+ * appropriate Authenticate header(s) to prompt for credentials.
+ *
+ * @return Zend_Auth_Result Always returns a non-identity Auth result
+ */
+ protected function _challengeClient()
+ {
+ if ($this->_imaProxy) {
+ $statusCode = 407;
+ $headerName = 'Proxy-Authenticate';
+ } else {
+ $statusCode = 401;
+ $headerName = 'WWW-Authenticate';
+ }
+
+ $this->_response->setHttpResponseCode($statusCode);
+
+ // Send a challenge in each acceptable authentication scheme
+ if (in_array('basic', $this->_acceptSchemes)) {
+ $this->_response->setHeader($headerName, $this->_basicHeader());
+ }
+ if (in_array('digest', $this->_acceptSchemes)) {
+ $this->_response->setHeader($headerName, $this->_digestHeader());
+ }
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID,
+ array(),
+ array('Invalid or absent credentials; challenging client')
+ );
+ }
+
+ /**
+ * Basic Header
+ *
+ * Generates a Proxy- or WWW-Authenticate header value in the Basic
+ * authentication scheme.
+ *
+ * @return string Authenticate header value
+ */
+ protected function _basicHeader()
+ {
+ return 'Basic realm="' . $this->_realm . '"';
+ }
+
+ /**
+ * Digest Header
+ *
+ * Generates a Proxy- or WWW-Authenticate header value in the Digest
+ * authentication scheme.
+ *
+ * @return string Authenticate header value
+ */
+ protected function _digestHeader()
+ {
+ $wwwauth = 'Digest realm="' . $this->_realm . '", '
+ . 'domain="' . $this->_domains . '", '
+ . 'nonce="' . $this->_calcNonce() . '", '
+ . ($this->_useOpaque ? 'opaque="' . $this->_calcOpaque() . '", ' : '')
+ . 'algorithm="' . $this->_algo . '", '
+ . 'qop="' . implode(',', $this->_supportedQops) . '"';
+
+ return $wwwauth;
+ }
+
+ /**
+ * Basic Authentication
+ *
+ * @param string $header Client's Authorization header
+ * @throws Zend_Auth_Adapter_Exception
+ * @return Zend_Auth_Result
+ */
+ protected function _basicAuth($header)
+ {
+ if (empty($header)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('The value of the client Authorization header is required');
+ }
+ if (empty($this->_basicResolver)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('A basicResolver object must be set before doing Basic '
+ . 'authentication');
+ }
+
+ // Decode the Authorization header
+ $auth = substr($header, strlen('Basic '));
+ $auth = base64_decode($auth);
+ if (!$auth) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Unable to base64_decode Authorization header value');
+ }
+
+ // See ZF-1253. Validate the credentials the same way the digest
+ // implementation does. If invalid credentials are detected,
+ // re-challenge the client.
+ if (!ctype_print($auth)) {
+ return $this->_challengeClient();
+ }
+ // Fix for ZF-1515: Now re-challenges on empty username or password
+ $creds = array_filter(explode(':', $auth));
+ if (count($creds) != 2) {
+ return $this->_challengeClient();
+ }
+
+ $password = $this->_basicResolver->resolve($creds[0], $this->_realm);
+ if ($password && $this->_secureStringCompare($password, $creds[1])) {
+ $identity = array('username'=>$creds[0], 'realm'=>$this->_realm);
+ return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $identity);
+ } else {
+ return $this->_challengeClient();
+ }
+ }
+
+ /**
+ * Digest Authentication
+ *
+ * @param string $header Client's Authorization header
+ * @throws Zend_Auth_Adapter_Exception
+ * @return Zend_Auth_Result Valid auth result only on successful auth
+ */
+ protected function _digestAuth($header)
+ {
+ if (empty($header)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('The value of the client Authorization header is required');
+ }
+ if (empty($this->_digestResolver)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('A digestResolver object must be set before doing Digest authentication');
+ }
+
+ $data = $this->_parseDigestAuth($header);
+ if ($data === false) {
+ $this->_response->setHttpResponseCode(400);
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE_UNCATEGORIZED,
+ array(),
+ array('Invalid Authorization header format')
+ );
+ }
+
+ // See ZF-1052. This code was a bit too unforgiving of invalid
+ // usernames. Now, if the username is bad, we re-challenge the client.
+ if ('::invalid::' == $data['username']) {
+ return $this->_challengeClient();
+ }
+
+ // Verify that the client sent back the same nonce
+ if ($this->_calcNonce() != $data['nonce']) {
+ return $this->_challengeClient();
+ }
+ // The opaque value is also required to match, but of course IE doesn't
+ // play ball.
+ if (!$this->_ieNoOpaque && $this->_calcOpaque() != $data['opaque']) {
+ return $this->_challengeClient();
+ }
+
+ // Look up the user's password hash. If not found, deny access.
+ // This makes no assumptions about how the password hash was
+ // constructed beyond that it must have been built in such a way as
+ // to be recreatable with the current settings of this object.
+ $ha1 = $this->_digestResolver->resolve($data['username'], $data['realm']);
+ if ($ha1 === false) {
+ return $this->_challengeClient();
+ }
+
+ // If MD5-sess is used, a1 value is made of the user's password
+ // hash with the server and client nonce appended, separated by
+ // colons.
+ if ($this->_algo == 'MD5-sess') {
+ $ha1 = hash('md5', $ha1 . ':' . $data['nonce'] . ':' . $data['cnonce']);
+ }
+
+ // Calculate h(a2). The value of this hash depends on the qop
+ // option selected by the client and the supported hash functions
+ switch ($data['qop']) {
+ case 'auth':
+ $a2 = $this->_request->getMethod() . ':' . $data['uri'];
+ break;
+ case 'auth-int':
+ // Should be REQUEST_METHOD . ':' . uri . ':' . hash(entity-body),
+ // but this isn't supported yet, so fall through to default case
+ default:
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Client requested an unsupported qop option');
+ }
+ // Using hash() should make parameterizing the hash algorithm
+ // easier
+ $ha2 = hash('md5', $a2);
+
+
+ // Calculate the server's version of the request-digest. This must
+ // match $data['response']. See RFC 2617, section 3.2.2.1
+ $message = $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $ha2;
+ $digest = hash('md5', $ha1 . ':' . $message);
+
+ // If our digest matches the client's let them in, otherwise return
+ // a 401 code and exit to prevent access to the protected resource.
+ if ($this->_secureStringCompare($digest, $data['response'])) {
+ $identity = array('username'=>$data['username'], 'realm'=>$data['realm']);
+ return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $identity);
+ } else {
+ return $this->_challengeClient();
+ }
+ }
+
+ /**
+ * Calculate Nonce
+ *
+ * @return string The nonce value
+ */
+ protected function _calcNonce()
+ {
+ // Once subtle consequence of this timeout calculation is that it
+ // actually divides all of time into _nonceTimeout-sized sections, such
+ // that the value of timeout is the point in time of the next
+ // approaching "boundary" of a section. This allows the server to
+ // consistently generate the same timeout (and hence the same nonce
+ // value) across requests, but only as long as one of those
+ // "boundaries" is not crossed between requests. If that happens, the
+ // nonce will change on its own, and effectively log the user out. This
+ // would be surprising if the user just logged in.
+ $timeout = ceil(time() / $this->_nonceTimeout) * $this->_nonceTimeout;
+
+ $nonce = hash('md5', $timeout . ':' . $this->_request->getServer('HTTP_USER_AGENT') . ':' . __CLASS__);
+ return $nonce;
+ }
+
+ /**
+ * Calculate Opaque
+ *
+ * The opaque string can be anything; the client must return it exactly as
+ * it was sent. It may be useful to store data in this string in some
+ * applications. Ideally, a new value for this would be generated each time
+ * a WWW-Authenticate header is sent (in order to reduce predictability),
+ * but we would have to be able to create the same exact value across at
+ * least two separate requests from the same client.
+ *
+ * @return string The opaque value
+ */
+ protected function _calcOpaque()
+ {
+ return hash('md5', 'Opaque Data:' . __CLASS__);
+ }
+
+ /**
+ * Parse Digest Authorization header
+ *
+ * @param string $header Client's Authorization: HTTP header
+ * @return array|false Data elements from header, or false if any part of
+ * the header is invalid
+ */
+ protected function _parseDigestAuth($header)
+ {
+ $temp = null;
+ $data = array();
+
+ // See ZF-1052. Detect invalid usernames instead of just returning a
+ // 400 code.
+ $ret = preg_match('/username="([^"]+)"/', $header, $temp);
+ if (!$ret || empty($temp[1])
+ || !ctype_print($temp[1])
+ || strpos($temp[1], ':') !== false) {
+ $data['username'] = '::invalid::';
+ } else {
+ $data['username'] = $temp[1];
+ }
+ $temp = null;
+
+ $ret = preg_match('/realm="([^"]+)"/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+ return false;
+ }
+ if (!ctype_print($temp[1]) || strpos($temp[1], ':') !== false) {
+ return false;
+ } else {
+ $data['realm'] = $temp[1];
+ }
+ $temp = null;
+
+ $ret = preg_match('/nonce="([^"]+)"/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+ return false;
+ }
+ if (!ctype_xdigit($temp[1])) {
+ return false;
+ } else {
+ $data['nonce'] = $temp[1];
+ }
+ $temp = null;
+
+ $ret = preg_match('/uri="([^"]+)"/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+ return false;
+ }
+ // Section 3.2.2.5 in RFC 2617 says the authenticating server must
+ // verify that the URI field in the Authorization header is for the
+ // same resource requested in the Request Line.
+ $rUri = @parse_url($this->_request->getRequestUri());
+ $cUri = @parse_url($temp[1]);
+ if (false === $rUri || false === $cUri) {
+ return false;
+ } else {
+ // Make sure the path portion of both URIs is the same
+ if ($rUri['path'] != $cUri['path']) {
+ return false;
+ }
+ // Section 3.2.2.5 seems to suggest that the value of the URI
+ // Authorization field should be made into an absolute URI if the
+ // Request URI is absolute, but it's vague, and that's a bunch of
+ // code I don't want to write right now.
+ $data['uri'] = $temp[1];
+ }
+ $temp = null;
+
+ $ret = preg_match('/response="([^"]+)"/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+ return false;
+ }
+ if (32 != strlen($temp[1]) || !ctype_xdigit($temp[1])) {
+ return false;
+ } else {
+ $data['response'] = $temp[1];
+ }
+ $temp = null;
+
+ // The spec says this should default to MD5 if omitted. OK, so how does
+ // that square with the algo we send out in the WWW-Authenticate header,
+ // if it can easily be overridden by the client?
+ $ret = preg_match('/algorithm="?(' . $this->_algo . ')"?/', $header, $temp);
+ if ($ret && !empty($temp[1])
+ && in_array($temp[1], $this->_supportedAlgos)) {
+ $data['algorithm'] = $temp[1];
+ } else {
+ $data['algorithm'] = 'MD5'; // = $this->_algo; ?
+ }
+ $temp = null;
+
+ // Not optional in this implementation
+ $ret = preg_match('/cnonce="([^"]+)"/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+ return false;
+ }
+ if (!ctype_print($temp[1])) {
+ return false;
+ } else {
+ $data['cnonce'] = $temp[1];
+ }
+ $temp = null;
+
+ // If the server sent an opaque value, the client must send it back
+ if ($this->_useOpaque) {
+ $ret = preg_match('/opaque="([^"]+)"/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+
+ // Big surprise: IE isn't RFC 2617-compliant.
+ if (false !== strpos($this->_request->getHeader('User-Agent'), 'MSIE')) {
+ $temp[1] = '';
+ $this->_ieNoOpaque = true;
+ } else {
+ return false;
+ }
+ }
+ // This implementation only sends MD5 hex strings in the opaque value
+ if (!$this->_ieNoOpaque &&
+ (32 != strlen($temp[1]) || !ctype_xdigit($temp[1]))) {
+ return false;
+ } else {
+ $data['opaque'] = $temp[1];
+ }
+ $temp = null;
+ }
+
+ // Not optional in this implementation, but must be one of the supported
+ // qop types
+ $ret = preg_match('/qop="?(' . implode('|', $this->_supportedQops) . ')"?/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+ return false;
+ }
+ if (!in_array($temp[1], $this->_supportedQops)) {
+ return false;
+ } else {
+ $data['qop'] = $temp[1];
+ }
+ $temp = null;
+
+ // Not optional in this implementation. The spec says this value
+ // shouldn't be a quoted string, but apparently some implementations
+ // quote it anyway. See ZF-1544.
+ $ret = preg_match('/nc="?([0-9A-Fa-f]{8})"?/', $header, $temp);
+ if (!$ret || empty($temp[1])) {
+ return false;
+ }
+ if (8 != strlen($temp[1]) || !ctype_xdigit($temp[1])) {
+ return false;
+ } else {
+ $data['nc'] = $temp[1];
+ }
+ $temp = null;
+
+ return $data;
+ }
+
+ /**
+ * Securely compare two strings for equality while avoided C level memcmp()
+ * optimisations capable of leaking timing information useful to an attacker
+ * attempting to iteratively guess the unknown string (e.g. password) being
+ * compared against.
+ *
+ * @param string $a
+ * @param string $b
+ * @return bool
+ */
+ protected function _secureStringCompare($a, $b)
+ {
+ if (strlen($a) !== strlen($b)) {
+ return false;
+ }
+ $result = 0;
+ for ($i = 0; $i < strlen($a); $i++) {
+ $result |= ord($a[$i]) ^ ord($b[$i]);
+ }
+ return $result == 0;
+ }
+}
diff --git a/library/Zend/Auth/Adapter/Http/Resolver/Exception.php b/library/Zend/Auth/Adapter/Http/Resolver/Exception.php
new file mode 100644
index 0000000..46f3fc9
--- /dev/null
+++ b/library/Zend/Auth/Adapter/Http/Resolver/Exception.php
@@ -0,0 +1,40 @@
+setFile($path);
+ }
+ }
+
+ /**
+ * Set the path to the credentials file
+ *
+ * @param string $path
+ * @throws Zend_Auth_Adapter_Http_Resolver_Exception
+ * @return Zend_Auth_Adapter_Http_Resolver_File Provides a fluent interface
+ */
+ public function setFile($path)
+ {
+ if (empty($path) || !is_readable($path)) {
+ /**
+ * @see Zend_Auth_Adapter_Http_Resolver_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Http/Resolver/Exception.php';
+ throw new Zend_Auth_Adapter_Http_Resolver_Exception('Path not readable: ' . $path);
+ }
+ $this->_file = $path;
+
+ return $this;
+ }
+
+ /**
+ * Returns the path to the credentials file
+ *
+ * @return string
+ */
+ public function getFile()
+ {
+ return $this->_file;
+ }
+
+ /**
+ * Resolve credentials
+ *
+ * Only the first matching username/realm combination in the file is
+ * returned. If the file contains credentials for Digest authentication,
+ * the returned string is the password hash, or h(a1) from RFC 2617. The
+ * returned string is the plain-text password for Basic authentication.
+ *
+ * The expected format of the file is:
+ * username:realm:sharedSecret
+ *
+ * That is, each line consists of the user's username, the applicable
+ * authentication realm, and the password or hash, each delimited by
+ * colons.
+ *
+ * @param string $username Username
+ * @param string $realm Authentication Realm
+ * @throws Zend_Auth_Adapter_Http_Resolver_Exception
+ * @return string|false User's shared secret, if the user is found in the
+ * realm, false otherwise.
+ */
+ public function resolve($username, $realm)
+ {
+ if (empty($username)) {
+ /**
+ * @see Zend_Auth_Adapter_Http_Resolver_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Http/Resolver/Exception.php';
+ throw new Zend_Auth_Adapter_Http_Resolver_Exception('Username is required');
+ } else if (!ctype_print($username) || strpos($username, ':') !== false) {
+ /**
+ * @see Zend_Auth_Adapter_Http_Resolver_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Http/Resolver/Exception.php';
+ throw new Zend_Auth_Adapter_Http_Resolver_Exception('Username must consist only of printable characters, '
+ . 'excluding the colon');
+ }
+ if (empty($realm)) {
+ /**
+ * @see Zend_Auth_Adapter_Http_Resolver_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Http/Resolver/Exception.php';
+ throw new Zend_Auth_Adapter_Http_Resolver_Exception('Realm is required');
+ } else if (!ctype_print($realm) || strpos($realm, ':') !== false) {
+ /**
+ * @see Zend_Auth_Adapter_Http_Resolver_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Http/Resolver/Exception.php';
+ throw new Zend_Auth_Adapter_Http_Resolver_Exception('Realm must consist only of printable characters, '
+ . 'excluding the colon.');
+ }
+
+ // Open file, read through looking for matching credentials
+ $fp = @fopen($this->_file, 'r');
+ if (!$fp) {
+ /**
+ * @see Zend_Auth_Adapter_Http_Resolver_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Http/Resolver/Exception.php';
+ throw new Zend_Auth_Adapter_Http_Resolver_Exception('Unable to open password file: ' . $this->_file);
+ }
+
+ // No real validation is done on the contents of the password file. The
+ // assumption is that we trust the administrators to keep it secure.
+ while (($line = fgetcsv($fp, 512, ':')) !== false) {
+ if ($line[0] == $username && $line[1] == $realm) {
+ $password = $line[2];
+ fclose($fp);
+ return $password;
+ }
+ }
+
+ fclose($fp);
+ return false;
+ }
+}
diff --git a/library/Zend/Auth/Adapter/Http/Resolver/Interface.php b/library/Zend/Auth/Adapter/Http/Resolver/Interface.php
new file mode 100644
index 0000000..d4420f9
--- /dev/null
+++ b/library/Zend/Auth/Adapter/Http/Resolver/Interface.php
@@ -0,0 +1,47 @@
+_xmlToken = $strXmlDocument;
+ $this->_infoCard = new Zend_InfoCard();
+ }
+
+ /**
+ * Sets the InfoCard component Adapter to use
+ *
+ * @param Zend_InfoCard_Adapter_Interface $a
+ * @return Zend_Auth_Adapter_InfoCard Provides a fluent interface
+ */
+ public function setAdapter(Zend_InfoCard_Adapter_Interface $a)
+ {
+ $this->_infoCard->setAdapter($a);
+ return $this;
+ }
+
+ /**
+ * Retrieves the InfoCard component adapter being used
+ *
+ * @return Zend_InfoCard_Adapter_Interface
+ */
+ public function getAdapter()
+ {
+ return $this->_infoCard->getAdapter();
+ }
+
+ /**
+ * Retrieves the InfoCard public key cipher object being used
+ *
+ * @return Zend_InfoCard_Cipher_PKI_Interface
+ */
+ public function getPKCipherObject()
+ {
+ return $this->_infoCard->getPKCipherObject();
+ }
+
+ /**
+ * Sets the InfoCard public key cipher object to use
+ *
+ * @param Zend_InfoCard_Cipher_PKI_Interface $cipherObj
+ * @return Zend_Auth_Adapter_InfoCard Provides a fluent interface
+ */
+ public function setPKICipherObject(Zend_InfoCard_Cipher_PKI_Interface $cipherObj)
+ {
+ $this->_infoCard->setPKICipherObject($cipherObj);
+ return $this;
+ }
+
+ /**
+ * Retrieves the Symmetric cipher object being used
+ *
+ * @return Zend_InfoCard_Cipher_Symmetric_Interface
+ */
+ public function getSymCipherObject()
+ {
+ return $this->_infoCard->getSymCipherObject();
+ }
+
+ /**
+ * Sets the InfoCard symmetric cipher object to use
+ *
+ * @param Zend_InfoCard_Cipher_Symmetric_Interface $cipherObj
+ * @return Zend_Auth_Adapter_InfoCard Provides a fluent interface
+ */
+ public function setSymCipherObject(Zend_InfoCard_Cipher_Symmetric_Interface $cipherObj)
+ {
+ $this->_infoCard->setSymCipherObject($cipherObj);
+ return $this;
+ }
+
+ /**
+ * Remove a Certificate Pair by Key ID from the search list
+ *
+ * @param string $key_id The Certificate Key ID returned from adding the certificate pair
+ * @throws Zend_InfoCard_Exception
+ * @return Zend_Auth_Adapter_InfoCard Provides a fluent interface
+ */
+ public function removeCertificatePair($key_id)
+ {
+ $this->_infoCard->removeCertificatePair($key_id);
+ return $this;
+ }
+
+ /**
+ * Add a Certificate Pair to the list of certificates searched by the component
+ *
+ * @param string $private_key_file The path to the private key file for the pair
+ * @param string $public_key_file The path to the certificate / public key for the pair
+ * @param string $type (optional) The URI for the type of key pair this is (default RSA with OAEP padding)
+ * @param string $password (optional) The password for the private key file if necessary
+ * @throws Zend_InfoCard_Exception
+ * @return string A key ID representing this key pair in the component
+ */
+ public function addCertificatePair($private_key_file, $public_key_file, $type = Zend_InfoCard_Cipher::ENC_RSA_OAEP_MGF1P, $password = null)
+ {
+ return $this->_infoCard->addCertificatePair($private_key_file, $public_key_file, $type, $password);
+ }
+
+ /**
+ * Return a Certificate Pair from a key ID
+ *
+ * @param string $key_id The Key ID of the certificate pair in the component
+ * @throws Zend_InfoCard_Exception
+ * @return array An array containing the path to the private/public key files,
+ * the type URI and the password if provided
+ */
+ public function getCertificatePair($key_id)
+ {
+ return $this->_infoCard->getCertificatePair($key_id);
+ }
+
+ /**
+ * Set the XML Token to be processed
+ *
+ * @param string $strXmlToken The XML token to process
+ * @return Zend_Auth_Adapter_InfoCard Provides a fluent interface
+ */
+ public function setXmlToken($strXmlToken)
+ {
+ $this->_xmlToken = $strXmlToken;
+ return $this;
+ }
+
+ /**
+ * Get the XML Token being processed
+ *
+ * @return string The XML token to be processed
+ */
+ public function getXmlToken()
+ {
+ return $this->_xmlToken;
+ }
+
+ /**
+ * Authenticates the XML token
+ *
+ * @return Zend_Auth_Result The result of the authentication
+ */
+ public function authenticate()
+ {
+ try {
+ $claims = $this->_infoCard->process($this->getXmlToken());
+ } catch(Exception $e) {
+ return new Zend_Auth_Result(Zend_Auth_Result::FAILURE , null, array('Exception Thrown',
+ $e->getMessage(),
+ $e->getTraceAsString(),
+ serialize($e)));
+ }
+
+ if(!$claims->isValid()) {
+ switch($claims->getCode()) {
+ case Zend_infoCard_Claims::RESULT_PROCESSING_FAILURE:
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE,
+ $claims,
+ array(
+ 'Processing Failure',
+ $claims->getErrorMsg()
+ )
+ );
+ break;
+ case Zend_InfoCard_Claims::RESULT_VALIDATION_FAILURE:
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID,
+ $claims,
+ array(
+ 'Validation Failure',
+ $claims->getErrorMsg()
+ )
+ );
+ break;
+ default:
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE,
+ $claims,
+ array(
+ 'Unknown Failure',
+ $claims->getErrorMsg()
+ )
+ );
+ break;
+ }
+ }
+
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::SUCCESS,
+ $claims
+ );
+ }
+}
diff --git a/library/Zend/Auth/Adapter/Interface.php b/library/Zend/Auth/Adapter/Interface.php
new file mode 100644
index 0000000..ed01a33
--- /dev/null
+++ b/library/Zend/Auth/Adapter/Interface.php
@@ -0,0 +1,46 @@
+setOptions($options);
+ if ($username !== null) {
+ $this->setUsername($username);
+ }
+ if ($password !== null) {
+ $this->setPassword($password);
+ }
+ }
+
+ /**
+ * Returns the array of arrays of Zend_Ldap options of this adapter.
+ *
+ * @return array|null
+ */
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
+ /**
+ * Sets the array of arrays of Zend_Ldap options to be used by
+ * this adapter.
+ *
+ * @param array $options The array of arrays of Zend_Ldap options
+ * @return Zend_Auth_Adapter_Ldap Provides a fluent interface
+ */
+ public function setOptions($options)
+ {
+ $this->_options = is_array($options) ? $options : array();
+ return $this;
+ }
+
+ /**
+ * Returns the username of the account being authenticated, or
+ * NULL if none is set.
+ *
+ * @return string|null
+ */
+ public function getUsername()
+ {
+ return $this->_username;
+ }
+
+ /**
+ * Sets the username for binding
+ *
+ * @param string $username The username for binding
+ * @return Zend_Auth_Adapter_Ldap Provides a fluent interface
+ */
+ public function setUsername($username)
+ {
+ $this->_username = (string) $username;
+ return $this;
+ }
+
+ /**
+ * Returns the password of the account being authenticated, or
+ * NULL if none is set.
+ *
+ * @return string|null
+ */
+ public function getPassword()
+ {
+ return $this->_password;
+ }
+
+ /**
+ * Sets the passwort for the account
+ *
+ * @param string $password The password of the account being authenticated
+ * @return Zend_Auth_Adapter_Ldap Provides a fluent interface
+ */
+ public function setPassword($password)
+ {
+ $this->_password = (string) $password;
+ return $this;
+ }
+
+ /**
+ * setIdentity() - set the identity (username) to be used
+ *
+ * Proxies to {@see setUsername()}
+ *
+ * Closes ZF-6813
+ *
+ * @param string $identity
+ * @return Zend_Auth_Adapter_Ldap Provides a fluent interface
+ */
+ public function setIdentity($identity)
+ {
+ return $this->setUsername($identity);
+ }
+
+ /**
+ * setCredential() - set the credential (password) value to be used
+ *
+ * Proxies to {@see setPassword()}
+ *
+ * Closes ZF-6813
+ *
+ * @param string $credential
+ * @return Zend_Auth_Adapter_Ldap Provides a fluent interface
+ */
+ public function setCredential($credential)
+ {
+ return $this->setPassword($credential);
+ }
+
+ /**
+ * Returns the LDAP Object
+ *
+ * @return Zend_Ldap The Zend_Ldap object used to authenticate the credentials
+ */
+ public function getLdap()
+ {
+ if ($this->_ldap === null) {
+ /**
+ * @see Zend_Ldap
+ */
+ require_once 'Zend/Ldap.php';
+ $this->_ldap = new Zend_Ldap();
+ }
+
+ return $this->_ldap;
+ }
+
+ /**
+ * Set an Ldap connection
+ *
+ * @param Zend_Ldap $ldap An existing Ldap object
+ * @return Zend_Auth_Adapter_Ldap Provides a fluent interface
+ */
+ public function setLdap(Zend_Ldap $ldap)
+ {
+ $this->_ldap = $ldap;
+
+ $this->setOptions(array($ldap->getOptions()));
+
+ return $this;
+ }
+
+ /**
+ * Returns a domain name for the current LDAP options. This is used
+ * for skipping redundant operations (e.g. authentications).
+ *
+ * @return string
+ */
+ protected function _getAuthorityName()
+ {
+ $options = $this->getLdap()->getOptions();
+ $name = $options['accountDomainName'];
+ if (!$name)
+ $name = $options['accountDomainNameShort'];
+ return $name ? $name : '';
+ }
+
+ /**
+ * Authenticate the user
+ *
+ * @throws Zend_Auth_Adapter_Exception
+ * @return Zend_Auth_Result
+ */
+ public function authenticate()
+ {
+ /**
+ * @see Zend_Ldap_Exception
+ */
+ require_once 'Zend/Ldap/Exception.php';
+
+ $messages = array();
+ $messages[0] = ''; // reserved
+ $messages[1] = ''; // reserved
+
+ $username = $this->_username;
+ $password = $this->_password;
+
+ if (!$username) {
+ $code = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
+ $messages[0] = 'A username is required';
+ return new Zend_Auth_Result($code, '', $messages);
+ }
+ if (!$password) {
+ /* A password is required because some servers will
+ * treat an empty password as an anonymous bind.
+ */
+ $code = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
+ $messages[0] = 'A password is required';
+ return new Zend_Auth_Result($code, '', $messages);
+ }
+
+ $ldap = $this->getLdap();
+
+ $code = Zend_Auth_Result::FAILURE;
+ $messages[0] = "Authority not found: $username";
+ $failedAuthorities = array();
+
+ /* Iterate through each server and try to authenticate the supplied
+ * credentials against it.
+ */
+ foreach ($this->_options as $name => $options) {
+
+ if (!is_array($options)) {
+ /**
+ * @see Zend_Auth_Adapter_Exception
+ */
+ require_once 'Zend/Auth/Adapter/Exception.php';
+ throw new Zend_Auth_Adapter_Exception('Adapter options array not an array');
+ }
+ $adapterOptions = $this->_prepareOptions($ldap, $options);
+ $dname = '';
+
+ try {
+ if ($messages[1])
+ $messages[] = $messages[1];
+ $messages[1] = '';
+ $messages[] = $this->_optionsToString($options);
+
+ $dname = $this->_getAuthorityName();
+ if (isset($failedAuthorities[$dname])) {
+ /* If multiple sets of server options for the same domain
+ * are supplied, we want to skip redundant authentications
+ * where the identity or credentials where found to be
+ * invalid with another server for the same domain. The
+ * $failedAuthorities array tracks this condition (and also
+ * serves to supply the original error message).
+ * This fixes issue ZF-4093.
+ */
+ $messages[1] = $failedAuthorities[$dname];
+ $messages[] = "Skipping previously failed authority: $dname";
+ continue;
+ }
+
+ $canonicalName = $ldap->getCanonicalAccountName($username);
+ $ldap->bind($canonicalName, $password);
+ /*
+ * Fixes problem when authenticated user is not allowed to retrieve
+ * group-membership information or own account.
+ * This requires that the user specified with "username" and optionally
+ * "password" in the Zend_Ldap options is able to retrieve the required
+ * information.
+ */
+ $requireRebind = false;
+ if (isset($options['username'])) {
+ $ldap->bind();
+ $requireRebind = true;
+ }
+ $dn = $ldap->getCanonicalAccountName($canonicalName, Zend_Ldap::ACCTNAME_FORM_DN);
+
+ $groupResult = $this->_checkGroupMembership($ldap, $canonicalName, $dn, $adapterOptions);
+ if ($groupResult === true) {
+ $this->_authenticatedDn = $dn;
+ $messages[0] = '';
+ $messages[1] = '';
+ $messages[] = "$canonicalName authentication successful";
+ if ($requireRebind === true) {
+ // rebinding with authenticated user
+ $ldap->bind($dn, $password);
+ }
+ return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $canonicalName, $messages);
+ } else {
+ $messages[0] = 'Account is not a member of the specified group';
+ $messages[1] = $groupResult;
+ $failedAuthorities[$dname] = $groupResult;
+ }
+ } catch (Zend_Ldap_Exception $zle) {
+
+ /* LDAP based authentication is notoriously difficult to diagnose. Therefore
+ * we bend over backwards to capture and record every possible bit of
+ * information when something goes wrong.
+ */
+
+ $err = $zle->getCode();
+
+ if ($err == Zend_Ldap_Exception::LDAP_X_DOMAIN_MISMATCH) {
+ /* This error indicates that the domain supplied in the
+ * username did not match the domains in the server options
+ * and therefore we should just skip to the next set of
+ * server options.
+ */
+ continue;
+ } else if ($err == Zend_Ldap_Exception::LDAP_NO_SUCH_OBJECT) {
+ $code = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
+ $messages[0] = "Account not found: $username";
+ $failedAuthorities[$dname] = $zle->getMessage();
+ } else if ($err == Zend_Ldap_Exception::LDAP_INVALID_CREDENTIALS) {
+ $code = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
+ $messages[0] = 'Invalid credentials';
+ $failedAuthorities[$dname] = $zle->getMessage();
+ } else {
+ $line = $zle->getLine();
+ $messages[] = $zle->getFile() . "($line): " . $zle->getMessage();
+ $messages[] = str_replace($password, '*****', $zle->getTraceAsString());
+ $messages[0] = 'An unexpected failure occurred';
+ }
+ $messages[1] = $zle->getMessage();
+ }
+ }
+
+ $msg = isset($messages[1]) ? $messages[1] : $messages[0];
+ $messages[] = "$username authentication failed: $msg";
+
+ return new Zend_Auth_Result($code, $username, $messages);
+ }
+
+ /**
+ * Sets the LDAP specific options on the Zend_Ldap instance
+ *
+ * @param Zend_Ldap $ldap
+ * @param array $options
+ * @return array of auth-adapter specific options
+ */
+ protected function _prepareOptions(Zend_Ldap $ldap, array $options)
+ {
+ $adapterOptions = array(
+ 'group' => null,
+ 'groupDn' => $ldap->getBaseDn(),
+ 'groupScope' => Zend_Ldap::SEARCH_SCOPE_SUB,
+ 'groupAttr' => 'cn',
+ 'groupFilter' => 'objectClass=groupOfUniqueNames',
+ 'memberAttr' => 'uniqueMember',
+ 'memberIsDn' => true
+ );
+ foreach ($adapterOptions as $key => $value) {
+ if (array_key_exists($key, $options)) {
+ $value = $options[$key];
+ unset($options[$key]);
+ switch ($key) {
+ case 'groupScope':
+ $value = (int)$value;
+ if (in_array($value, array(Zend_Ldap::SEARCH_SCOPE_BASE,
+ Zend_Ldap::SEARCH_SCOPE_ONE, Zend_Ldap::SEARCH_SCOPE_SUB), true)) {
+ $adapterOptions[$key] = $value;
+ }
+ break;
+ case 'memberIsDn':
+ $adapterOptions[$key] = ($value === true ||
+ $value === '1' || strcasecmp($value, 'true') == 0);
+ break;
+ default:
+ $adapterOptions[$key] = trim($value);
+ break;
+ }
+ }
+ }
+ $ldap->setOptions($options);
+ return $adapterOptions;
+ }
+
+ /**
+ * Checks the group membership of the bound user
+ *
+ * @param Zend_Ldap $ldap
+ * @param string $canonicalName
+ * @param string $dn
+ * @param array $adapterOptions
+ * @return string|true
+ */
+ protected function _checkGroupMembership(Zend_Ldap $ldap, $canonicalName, $dn, array $adapterOptions)
+ {
+ if ($adapterOptions['group'] === null) {
+ return true;
+ }
+
+ if ($adapterOptions['memberIsDn'] === false) {
+ $user = $canonicalName;
+ } else {
+ $user = $dn;
+ }
+
+ /**
+ * @see Zend_Ldap_Filter
+ */
+ require_once 'Zend/Ldap/Filter.php';
+ $groupName = Zend_Ldap_Filter::equals($adapterOptions['groupAttr'], $adapterOptions['group']);
+ $membership = Zend_Ldap_Filter::equals($adapterOptions['memberAttr'], $user);
+ $group = Zend_Ldap_Filter::andFilter($groupName, $membership);
+ $groupFilter = $adapterOptions['groupFilter'];
+ if (!empty($groupFilter)) {
+ $group = $group->addAnd($groupFilter);
+ }
+
+ $result = $ldap->count($group, $adapterOptions['groupDn'], $adapterOptions['groupScope']);
+
+ if ($result === 1) {
+ return true;
+ } else {
+ return 'Failed to verify group membership with ' . $group->toString();
+ }
+ }
+
+ /**
+ * getAccountObject() - Returns the result entry as a stdClass object
+ *
+ * This resembles the feature {@see Zend_Auth_Adapter_DbTable::getResultRowObject()}.
+ * Closes ZF-6813
+ *
+ * @param array $returnAttribs
+ * @param array $omitAttribs
+ * @return stdClass|boolean
+ */
+ public function getAccountObject(array $returnAttribs = array(), array $omitAttribs = array())
+ {
+ if (!$this->_authenticatedDn) {
+ return false;
+ }
+
+ $returnObject = new stdClass();
+
+ $returnAttribs = array_map('strtolower', $returnAttribs);
+ $omitAttribs = array_map('strtolower', $omitAttribs);
+ $returnAttribs = array_diff($returnAttribs, $omitAttribs);
+
+ $entry = $this->getLdap()->getEntry($this->_authenticatedDn, $returnAttribs, true);
+ foreach ($entry as $attr => $value) {
+ if (in_array($attr, $omitAttribs)) {
+ // skip attributes marked to be omitted
+ continue;
+ }
+ if (is_array($value)) {
+ $returnObject->$attr = (count($value) > 1) ? $value : $value[0];
+ } else {
+ $returnObject->$attr = $value;
+ }
+ }
+ return $returnObject;
+ }
+
+ /**
+ * Converts options to string
+ *
+ * @param array $options
+ * @return string
+ */
+ private function _optionsToString(array $options)
+ {
+ $str = '';
+ foreach ($options as $key => $val) {
+ if ($key === 'password')
+ $val = '*****';
+ if ($str)
+ $str .= ',';
+ $str .= $key . '=' . $val;
+ }
+ return $str;
+ }
+}
diff --git a/library/Zend/Auth/Adapter/OpenId.php b/library/Zend/Auth/Adapter/OpenId.php
new file mode 100644
index 0000000..8115032
--- /dev/null
+++ b/library/Zend/Auth/Adapter/OpenId.php
@@ -0,0 +1,284 @@
+_id = $id;
+ $this->_storage = $storage;
+ $this->_returnTo = $returnTo;
+ $this->_root = $root;
+ $this->_extensions = $extensions;
+ $this->_response = $response;
+ }
+
+ /**
+ * Sets the value to be used as the identity
+ *
+ * @param string $id the identity value
+ * @return Zend_Auth_Adapter_OpenId Provides a fluent interface
+ */
+ public function setIdentity($id)
+ {
+ $this->_id = $id;
+ return $this;
+ }
+
+ /**
+ * Sets the storage implementation which will be use by OpenId
+ *
+ * @param Zend_OpenId_Consumer_Storage $storage
+ * @return Zend_Auth_Adapter_OpenId Provides a fluent interface
+ */
+ public function setStorage(Zend_OpenId_Consumer_Storage $storage)
+ {
+ $this->_storage = $storage;
+ return $this;
+ }
+
+ /**
+ * Sets the HTTP URL to redirect response from server to
+ *
+ * @param string $returnTo
+ * @return Zend_Auth_Adapter_OpenId Provides a fluent interface
+ */
+ public function setReturnTo($returnTo)
+ {
+ $this->_returnTo = $returnTo;
+ return $this;
+ }
+
+ /**
+ * Sets HTTP URL to identify consumer on server
+ *
+ * @param string $root
+ * @return Zend_Auth_Adapter_OpenId Provides a fluent interface
+ */
+ public function setRoot($root)
+ {
+ $this->_root = $root;
+ return $this;
+ }
+
+ /**
+ * Sets OpenID extension(s)
+ *
+ * @param mixed $extensions
+ * @return Zend_Auth_Adapter_OpenId Provides a fluent interface
+ */
+ public function setExtensions($extensions)
+ {
+ $this->_extensions = $extensions;
+ return $this;
+ }
+
+ /**
+ * Sets an optional response object to perform HTTP or HTML form redirection
+ *
+ * @param string $root
+ * @return Zend_Auth_Adapter_OpenId Provides a fluent interface
+ */
+ public function setResponse($response)
+ {
+ $this->_response = $response;
+ return $this;
+ }
+
+ /**
+ * Enables or disables interaction with user during authentication on
+ * OpenID provider.
+ *
+ * @param bool $check_immediate
+ * @return Zend_Auth_Adapter_OpenId Provides a fluent interface
+ */
+ public function setCheckImmediate($check_immediate)
+ {
+ $this->_check_immediate = $check_immediate;
+ return $this;
+ }
+
+ /**
+ * Sets HTTP client object to make HTTP requests
+ *
+ * @param Zend_Http_Client $client HTTP client object to be used
+ */
+ public function setHttpClient($client) {
+ $this->_httpClient = $client;
+ }
+
+ /**
+ * Authenticates the given OpenId identity.
+ * Defined by Zend_Auth_Adapter_Interface.
+ *
+ * @throws Zend_Auth_Adapter_Exception If answering the authentication query is impossible
+ * @return Zend_Auth_Result
+ */
+ public function authenticate() {
+ $id = $this->_id;
+ if (!empty($id)) {
+ $consumer = new Zend_OpenId_Consumer($this->_storage);
+ $consumer->setHttpClient($this->_httpClient);
+ /* login() is never returns on success */
+ if (!$this->_check_immediate) {
+ if (!$consumer->login($id,
+ $this->_returnTo,
+ $this->_root,
+ $this->_extensions,
+ $this->_response)) {
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE,
+ $id,
+ array("Authentication failed", $consumer->getError()));
+ }
+ } else {
+ if (!$consumer->check($id,
+ $this->_returnTo,
+ $this->_root,
+ $this->_extensions,
+ $this->_response)) {
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE,
+ $id,
+ array("Authentication failed", $consumer->getError()));
+ }
+ }
+ } else {
+ $params = (isset($_SERVER['REQUEST_METHOD']) &&
+ $_SERVER['REQUEST_METHOD']=='POST') ? $_POST: $_GET;
+ $consumer = new Zend_OpenId_Consumer($this->_storage);
+ $consumer->setHttpClient($this->_httpClient);
+ if ($consumer->verify(
+ $params,
+ $id,
+ $this->_extensions)) {
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::SUCCESS,
+ $id,
+ array("Authentication successful"));
+ } else {
+ return new Zend_Auth_Result(
+ Zend_Auth_Result::FAILURE,
+ $id,
+ array("Authentication failed", $consumer->getError()));
+ }
+ }
+ }
+
+}
diff --git a/library/Zend/Auth/Exception.php b/library/Zend/Auth/Exception.php
new file mode 100644
index 0000000..522ef77
--- /dev/null
+++ b/library/Zend/Auth/Exception.php
@@ -0,0 +1,36 @@
+ self::SUCCESS ) {
+ $code = 1;
+ }
+
+ $this->_code = $code;
+ $this->_identity = $identity;
+ $this->_messages = $messages;
+ }
+
+ /**
+ * Returns whether the result represents a successful authentication attempt
+ *
+ * @return boolean
+ */
+ public function isValid()
+ {
+ return ($this->_code > 0) ? true : false;
+ }
+
+ /**
+ * getCode() - Get the result code for this authentication attempt
+ *
+ * @return int
+ */
+ public function getCode()
+ {
+ return $this->_code;
+ }
+
+ /**
+ * Returns the identity used in the authentication attempt
+ *
+ * @return mixed
+ */
+ public function getIdentity()
+ {
+ return $this->_identity;
+ }
+
+ /**
+ * Returns an array of string reasons why the authentication attempt was unsuccessful
+ *
+ * If authentication was successful, this method returns an empty array.
+ *
+ * @return array
+ */
+ public function getMessages()
+ {
+ return $this->_messages;
+ }
+}
diff --git a/library/Zend/Auth/Storage/Exception.php b/library/Zend/Auth/Storage/Exception.php
new file mode 100644
index 0000000..7e7ed10
--- /dev/null
+++ b/library/Zend/Auth/Storage/Exception.php
@@ -0,0 +1,38 @@
+_data);
+ }
+
+ /**
+ * Returns the contents of storage
+ * Behavior is undefined when storage is empty.
+ *
+ * @throws Zend_Auth_Storage_Exception If reading contents from storage is impossible
+ * @return mixed
+ */
+ public function read()
+ {
+ return $this->_data;
+ }
+
+ /**
+ * Writes $contents to storage
+ *
+ * @param mixed $contents
+ * @throws Zend_Auth_Storage_Exception If writing $contents to storage is impossible
+ * @return void
+ */
+ public function write($contents)
+ {
+ $this->_data = $contents;
+ }
+
+ /**
+ * Clears contents from storage
+ *
+ * @throws Zend_Auth_Storage_Exception If clearing contents from storage is impossible
+ * @return void
+ */
+ public function clear()
+ {
+ $this->_data = null;
+ }
+}
diff --git a/library/Zend/Auth/Storage/Session.php b/library/Zend/Auth/Storage/Session.php
new file mode 100644
index 0000000..1799632
--- /dev/null
+++ b/library/Zend/Auth/Storage/Session.php
@@ -0,0 +1,150 @@
+_namespace = $namespace;
+ $this->_member = $member;
+ $this->_session = new Zend_Session_Namespace($this->_namespace);
+ }
+
+ /**
+ * Returns the session namespace
+ *
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return $this->_namespace;
+ }
+
+ /**
+ * Returns the name of the session object member
+ *
+ * @return string
+ */
+ public function getMember()
+ {
+ return $this->_member;
+ }
+
+ /**
+ * Defined by Zend_Auth_Storage_Interface
+ *
+ * @return boolean
+ */
+ public function isEmpty()
+ {
+ return !isset($this->_session->{$this->_member});
+ }
+
+ /**
+ * Defined by Zend_Auth_Storage_Interface
+ *
+ * @return mixed
+ */
+ public function read()
+ {
+ return $this->_session->{$this->_member};
+ }
+
+ /**
+ * Defined by Zend_Auth_Storage_Interface
+ *
+ * @param mixed $contents
+ * @return void
+ */
+ public function write($contents)
+ {
+ $this->_session->{$this->_member} = $contents;
+ }
+
+ /**
+ * Defined by Zend_Auth_Storage_Interface
+ *
+ * @return void
+ */
+ public function clear()
+ {
+ unset($this->_session->{$this->_member});
+ }
+}
diff --git a/library/Zend/Barcode.php b/library/Zend/Barcode.php
new file mode 100644
index 0000000..63e197e
--- /dev/null
+++ b/library/Zend/Barcode.php
@@ -0,0 +1,352 @@
+rendererParams)) {
+ $rendererConfig = $barcode->rendererParams->toArray();
+ }
+ if (isset($barcode->renderer)) {
+ $renderer = (string) $barcode->renderer;
+ }
+ if (isset($barcode->barcodeParams)) {
+ $barcodeConfig = $barcode->barcodeParams->toArray();
+ }
+ if (isset($barcode->barcode)) {
+ $barcode = (string) $barcode->barcode;
+ } else {
+ $barcode = null;
+ }
+ }
+
+ try {
+ $barcode = self::makeBarcode($barcode, $barcodeConfig);
+ $renderer = self::makeRenderer($renderer, $rendererConfig);
+ } catch (Zend_Exception $e) {
+ $renderable = ($e instanceof Zend_Barcode_Exception) ? $e->isRenderable() : false;
+ if ($automaticRenderError && $renderable) {
+ $barcode = self::makeBarcode('error', array(
+ 'text' => $e->getMessage()
+ ));
+ $renderer = self::makeRenderer($renderer, array());
+ } else {
+ throw $e;
+ }
+ }
+
+ $renderer->setAutomaticRenderError($automaticRenderError);
+ return $renderer->setBarcode($barcode);
+ }
+
+ /**
+ * Barcode Constructor
+ *
+ * @param mixed $barcode String name of barcode class, or Zend_Config object.
+ * @param mixed $barcodeConfig OPTIONAL; an array or Zend_Config object with barcode parameters.
+ * @return Zend_Barcode_Object
+ */
+ public static function makeBarcode($barcode, $barcodeConfig = array())
+ {
+ if ($barcode instanceof Zend_Barcode_Object_ObjectAbstract) {
+ return $barcode;
+ }
+
+ /*
+ * Convert Zend_Config argument to plain string
+ * barcode name and separate config object.
+ */
+ if ($barcode instanceof Zend_Config) {
+ if (isset($barcode->barcodeParams) && $barcode->barcodeParams instanceof Zend_Config) {
+ $barcodeConfig = $barcode->barcodeParams->toArray();
+ }
+ if (isset($barcode->barcode)) {
+ $barcode = (string) $barcode->barcode;
+ } else {
+ $barcode = null;
+ }
+ }
+ if ($barcodeConfig instanceof Zend_Config) {
+ $barcodeConfig = $barcodeConfig->toArray();
+ }
+
+ /*
+ * Verify that barcode parameters are in an array.
+ */
+ if (!is_array($barcodeConfig)) {
+ /**
+ * @see Zend_Barcode_Exception
+ */
+ require_once 'Zend/Barcode/Exception.php';
+ throw new Zend_Barcode_Exception(
+ 'Barcode parameters must be in an array or a Zend_Config object'
+ );
+ }
+
+ /*
+ * Verify that an barcode name has been specified.
+ */
+ if (!is_string($barcode) || empty($barcode)) {
+ /**
+ * @see Zend_Barcode_Exception
+ */
+ require_once 'Zend/Barcode/Exception.php';
+ throw new Zend_Barcode_Exception(
+ 'Barcode name must be specified in a string'
+ );
+ }
+ /*
+ * Form full barcode class name
+ */
+ $barcodeNamespace = 'Zend_Barcode_Object';
+ if (isset($barcodeConfig['barcodeNamespace'])) {
+ $barcodeNamespace = $barcodeConfig['barcodeNamespace'];
+ }
+
+ $barcodeName = strtolower($barcodeNamespace . '_' . $barcode);
+ $barcodeName = str_replace(' ', '_', ucwords(
+ str_replace( '_', ' ', $barcodeName)
+ ));
+
+ /*
+ * Load the barcode class. This throws an exception
+ * if the specified class cannot be loaded.
+ */
+ if (!class_exists($barcodeName)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($barcodeName);
+ }
+
+ /*
+ * Create an instance of the barcode class.
+ * Pass the config to the barcode class constructor.
+ */
+ $bcAdapter = new $barcodeName($barcodeConfig);
+
+ /*
+ * Verify that the object created is a descendent of the abstract barcode type.
+ */
+ if (!$bcAdapter instanceof Zend_Barcode_Object_ObjectAbstract) {
+ /**
+ * @see Zend_Barcode_Exception
+ */
+ require_once 'Zend/Barcode/Exception.php';
+ throw new Zend_Barcode_Exception(
+ "Barcode class '$barcodeName' does not extend Zend_Barcode_Object_ObjectAbstract"
+ );
+ }
+ return $bcAdapter;
+ }
+
+ /**
+ * Renderer Constructor
+ *
+ * @param mixed $renderer String name of renderer class, or Zend_Config object.
+ * @param mixed $rendererConfig OPTIONAL; an array or Zend_Config object with renderer parameters.
+ * @return Zend_Barcode_Renderer
+ */
+ public static function makeRenderer($renderer = 'image', $rendererConfig = array())
+ {
+ if ($renderer instanceof Zend_Barcode_Renderer_RendererAbstract) {
+ return $renderer;
+ }
+
+ /*
+ * Convert Zend_Config argument to plain string
+ * barcode name and separate config object.
+ */
+ if ($renderer instanceof Zend_Config) {
+ if (isset($renderer->rendererParams)) {
+ $rendererConfig = $renderer->rendererParams->toArray();
+ }
+ if (isset($renderer->renderer)) {
+ $renderer = (string) $renderer->renderer;
+ }
+ }
+ if ($rendererConfig instanceof Zend_Config) {
+ $rendererConfig = $rendererConfig->toArray();
+ }
+
+ /*
+ * Verify that barcode parameters are in an array.
+ */
+ if (!is_array($rendererConfig)) {
+ /**
+ * @see Zend_Barcode_Exception
+ */
+ require_once 'Zend/Barcode/Exception.php';
+ $e = new Zend_Barcode_Exception(
+ 'Barcode parameters must be in an array or a Zend_Config object'
+ );
+ $e->setIsRenderable(false);
+ throw $e;
+ }
+
+ /*
+ * Verify that an barcode name has been specified.
+ */
+ if (!is_string($renderer) || empty($renderer)) {
+ /**
+ * @see Zend_Barcode_Exception
+ */
+ require_once 'Zend/Barcode/Exception.php';
+ $e = new Zend_Barcode_Exception(
+ 'Renderer name must be specified in a string'
+ );
+ $e->setIsRenderable(false);
+ throw $e;
+ }
+
+ /*
+ * Form full barcode class name
+ */
+ $rendererNamespace = 'Zend_Barcode_Renderer';
+ if (isset($rendererConfig['rendererNamespace'])) {
+ $rendererNamespace = $rendererConfig['rendererNamespace'];
+ }
+
+ $rendererName = strtolower($rendererNamespace . '_' . $renderer);
+ $rendererName = str_replace(' ', '_', ucwords(
+ str_replace( '_', ' ', $rendererName)
+ ));
+
+ /*
+ * Load the barcode class. This throws an exception
+ * if the specified class cannot be loaded.
+ */
+ if (!class_exists($rendererName)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($rendererName);
+ }
+
+ /*
+ * Create an instance of the barcode class.
+ * Pass the config to the barcode class constructor.
+ */
+ $rdrAdapter = new $rendererName($rendererConfig);
+
+ /*
+ * Verify that the object created is a descendent of the abstract barcode type.
+ */
+ if (!$rdrAdapter instanceof Zend_Barcode_Renderer_RendererAbstract) {
+ /**
+ * @see Zend_Barcode_Exception
+ */
+ require_once 'Zend/Barcode/Exception.php';
+ $e = new Zend_Barcode_Exception(
+ "Renderer class '$rendererName' does not extend Zend_Barcode_Renderer_RendererAbstract"
+ );
+ $e->setIsRenderable(false);
+ throw $e;
+ }
+ return $rdrAdapter;
+ }
+
+ /**
+ * Proxy to renderer render() method
+ *
+ * @param string | Zend_Barcode_Object | array | Zend_Config $barcode
+ * @param string | Zend_Barcode_Renderer $renderer
+ * @param array | Zend_Config $barcodeConfig
+ * @param array | Zend_Config $rendererConfig
+ */
+ public static function render(
+ $barcode,
+ $renderer,
+ $barcodeConfig = array(),
+ $rendererConfig = array()
+ ) {
+ self::factory($barcode, $renderer, $barcodeConfig, $rendererConfig)->render();
+ }
+
+ /**
+ * Proxy to renderer draw() method
+ *
+ * @param string | Zend_Barcode_Object | array | Zend_Config $barcode
+ * @param string | Zend_Barcode_Renderer $renderer
+ * @param array | Zend_Config $barcodeConfig
+ * @param array | Zend_Config $rendererConfig
+ * @return mixed
+ */
+ public static function draw(
+ $barcode,
+ $renderer,
+ $barcodeConfig = array(),
+ $rendererConfig = array()
+ ) {
+ return self::factory($barcode, $renderer, $barcodeConfig, $rendererConfig)->draw();
+ }
+
+ /**
+ * Proxy for setBarcodeFont of Zend_Barcode_Object
+ * @param string $font
+ * @eturn void
+ */
+ public static function setBarcodeFont($font)
+ {
+ require_once 'Zend/Barcode/Object/ObjectAbstract.php';
+ Zend_Barcode_Object_ObjectAbstract::setBarcodeFont($font);
+ }
+}
diff --git a/library/Zend/Barcode/Exception.php b/library/Zend/Barcode/Exception.php
new file mode 100644
index 0000000..ecff266
--- /dev/null
+++ b/library/Zend/Barcode/Exception.php
@@ -0,0 +1,63 @@
+_isRenderable = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Retrieve renderable flag
+ *
+ * @return bool
+ */
+ public function isRenderable()
+ {
+ return $this->_isRenderable;
+ }
+}
diff --git a/library/Zend/Barcode/Object/Code128.php b/library/Zend/Barcode/Object/Code128.php
new file mode 100644
index 0000000..917e369
--- /dev/null
+++ b/library/Zend/Barcode/Object/Code128.php
@@ -0,0 +1,391 @@
+ "11011001100", 1 => "11001101100", 2 => "11001100110",
+ 3 => "10010011000", 4 => "10010001100", 5 => "10001001100",
+ 6 => "10011001000", 7 => "10011000100", 8 => "10001100100",
+ 9 => "11001001000", 10 => "11001000100", 11 => "11000100100",
+ 12 => "10110011100", 13 => "10011011100", 14 => "10011001110",
+ 15 => "10111001100", 16 => "10011101100", 17 => "10011100110",
+ 18 => "11001110010", 19 => "11001011100", 20 => "11001001110",
+ 21 => "11011100100", 22 => "11001110100", 23 => "11101101110",
+ 24 => "11101001100", 25 => "11100101100", 26 => "11100100110",
+ 27 => "11101100100", 28 => "11100110100", 29 => "11100110010",
+ 30 => "11011011000", 31 => "11011000110", 32 => "11000110110",
+ 33 => "10100011000", 34 => "10001011000", 35 => "10001000110",
+ 36 => "10110001000", 37 => "10001101000", 38 => "10001100010",
+ 39 => "11010001000", 40 => "11000101000", 41 => "11000100010",
+ 42 => "10110111000", 43 => "10110001110", 44 => "10001101110",
+ 45 => "10111011000", 46 => "10111000110", 47 => "10001110110",
+ 48 => "11101110110", 49 => "11010001110", 50 => "11000101110",
+ 51 => "11011101000", 52 => "11011100010", 53 => "11011101110",
+ 54 => "11101011000", 55 => "11101000110", 56 => "11100010110",
+ 57 => "11101101000", 58 => "11101100010", 59 => "11100011010",
+ 60 => "11101111010", 61 => "11001000010", 62 => "11110001010",
+ 63 => "10100110000", 64 => "10100001100", 65 => "10010110000",
+ 66 => "10010000110", 67 => "10000101100", 68 => "10000100110",
+ 69 => "10110010000", 70 => "10110000100", 71 => "10011010000",
+ 72 => "10011000010", 73 => "10000110100", 74 => "10000110010",
+ 75 => "11000010010", 76 => "11001010000", 77 => "11110111010",
+ 78 => "11000010100", 79 => "10001111010", 80 => "10100111100",
+ 81 => "10010111100", 82 => "10010011110", 83 => "10111100100",
+ 84 => "10011110100", 85 => "10011110010", 86 => "11110100100",
+ 87 => "11110010100", 88 => "11110010010", 89 => "11011011110",
+ 90 => "11011110110", 91 => "11110110110", 92 => "10101111000",
+ 93 => "10100011110", 94 => "10001011110", 95 => "10111101000",
+ 96 => "10111100010", 97 => "11110101000", 98 => "11110100010",
+ 99 => "10111011110", 100 => "10111101110", 101 => "11101011110",
+ 102 => "11110101110",
+ 103 => "11010000100", 104 => "11010010000", 105 => "11010011100",
+ 106 => "1100011101011");
+
+ /**
+ * Character sets ABC
+ * @var array
+ */
+ protected $_charSets = array(
+ 'A' => array(
+ ' ', '!', '"', '#', '$', '%', '&', "'",
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 'FNC3', 'FNC2', 'SHIFT', 'Code C', 'Code B', 'FNC4', 'FNC1',
+ 'START A', 'START B', 'START C', 'STOP'),
+ 'B' => array(
+ ' ', '!', '"', '#', '$', '%', '&', "'",
+ '(', ')', '*', '+', ',', '-', '.', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', ':', ';', '<', '=', '>', '?',
+ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+ 'x', 'y', 'z', '{', '|', '}', '~', 0x7F,
+ 'FNC3', 'FNC2', 'SHIFT', 'Code C', 'FNC4', 'Code A', 'FNC1',
+ 'START A', 'START B', 'START C', 'STOP',),
+ 'C' => array(
+ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09',
+ '10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
+ '20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
+ '30', '31', '32', '33', '34', '35', '36', '37', '38', '39',
+ '40', '41', '42', '43', '44', '45', '46', '47', '48', '49',
+ '50', '51', '52', '53', '54', '55', '56', '57', '58', '59',
+ '60', '61', '62', '63', '64', '65', '66', '67', '68', '69',
+ '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
+ '80', '81', '82', '83', '84', '85', '86', '87', '88', '89',
+ '90', '91', '92', '93', '94', '95', '96', '97', '98', '99',
+ 'Code B', 'Code A', 'FNC1', 'START A', 'START B', 'START C', 'STOP'));
+ /*'A' => array(
+ ' '=>0, '!'=>1, '"'=>2, '#'=>3, '$'=>4, '%'=>5, '&'=>6, "'"=>7,
+ '('=>8, ')'=>9, '*'=>10, '+'=>11, ','=>12, '-'=>13, '.'=>14, '/'=>15,
+ '0'=>16, '1'=>17, '2'=>18, '3'=>19, '4'=>20, '5'=>21, '6'=>22, '7'=>23,
+ '8'=>24, '9'=>25, ':'=>26, ';'=>27, '<'=>28, '='=>29, '>'=>30, '?'=>31,
+ '@'=>32, 'A'=>33, 'B'=>34, 'C'=>35, 'D'=>36, 'E'=>37, 'F'=>38, 'G'=>39,
+ 'H'=>40, 'I'=>41, 'J'=>42, 'K'=>43, 'L'=>44, 'M'=>45, 'N'=>46, 'O'=>47,
+ 'P'=>48, 'Q'=>49, 'R'=>50, 'S'=>51, 'T'=>52, 'U'=>53, 'V'=>54, 'W'=>55,
+ 'X'=>56, 'Y'=>57, 'Z'=>58, '['=>59, '\\'=>60, ']'=>61, '^'=>62, '_'=>63,
+ 0x00=>64, 0x01=>65, 0x02=>66, 0x03=>67, 0x04=>68, 0x05=>69, 0x06=>70, 0x07=>71,
+ 0x08=>72, 0x09=>73, 0x0A=>74, 0x0B=>75, 0x0C=>76, 0x0D=>77, 0x0E=>78, 0x0F=>79,
+ 0x10=>80, 0x11=>81, 0x12=>82, 0x13=>83, 0x14=>84, 0x15=>85, 0x16=>86, 0x17=>87,
+ 0x18=>88, 0x19=>89, 0x1A=>90, 0x1B=>91, 0x1C=>92, 0x1D=>93, 0x1E=>94, 0x1F=>95,
+ 'FNC3'=>96, 'FNC2'=>97, 'SHIFT'=>98, 'Code C'=>99, 'Code B'=>100, 'FNC4'=>101, 'FNC1'=>102, 'START A'=>103,
+ 'START B'=>104, 'START C'=>105, 'STOP'=>106),
+ 'B' => array(
+ ' '=>0, '!'=>1, '"'=>2, '#'=>3, '$'=>4, '%'=>5, '&'=>6, "'"=>7,
+ '('=>8, ')'=>9, '*'=>10, '+'=>11, ','=>12, '-'=>13, '.'=>14, '/'=>15,
+ '0'=>16, '1'=>17, '2'=>18, '3'=>19, '4'=>20, '5'=>21, '6'=>22, '7'=>23,
+ '8'=>24, '9'=>25, ':'=>26, ';'=>27, '<'=>28, '='=>29, '>'=>30, '?'=>31,
+ '@'=>32, 'A'=>33, 'B'=>34, 'C'=>35, 'D'=>36, 'E'=>37, 'F'=>38, 'G'=>39,
+ 'H'=>40, 'I'=>41, 'J'=>42, 'K'=>43, 'L'=>44, 'M'=>45, 'N'=>46, 'O'=>47,
+ 'P'=>48, 'Q'=>49, 'R'=>50, 'S'=>51, 'T'=>52, 'U'=>53, 'V'=>54, 'W'=>55,
+ 'X'=>56, 'Y'=>57, 'Z'=>58, '['=>59, '\\'=>60, ']'=>61, '^'=>62, '_'=>63,
+ '`' =>64, 'a'=>65, 'b'=>66, 'c'=>67, 'd'=>68, 'e'=>69, 'f'=>70, 'g'=>71,
+ 'h'=>72, 'i'=>73, 'j'=>74, 'k'=>75, 'l'=>76, 'm'=>77, 'n'=>78, 'o'=>79,
+ 'p'=>80, 'q'=>81, 'r'=>82, 's'=>83, 't'=>84, 'u'=>85, 'v'=>86, 'w'=>87,
+ 'x'=>88, 'y'=>89, 'z'=>90, '{'=>91, '|'=>92, '}'=>93, '~'=>94, 0x7F=>95,
+ 'FNC3'=>96, 'FNC2'=>97, 'SHIFT'=>98, 'Code C'=>99, 'FNC4'=>100, 'Code A'=>101, 'FNC1'=>102, 'START A'=>103,
+ 'START B'=>104, 'START C'=>105, 'STOP'=>106,),
+ 'C' => array(
+ '00'=>0, '01'=>1, '02'=>2, '03'=>3, '04'=>4, '05'=>5, '06'=>6, '07'=>7, '08'=>8, '09'=>9,
+ '10'=>10, '11'=>11, '12'=>12, '13'=>13, '14'=>14, '15'=>15, '16'=>16, '17'=>17, '18'=>18, '19'=>19,
+ '20'=>20, '21'=>21, '22'=>22, '23'=>23, '24'=>24, '25'=>25, '26'=>26, '27'=>27, '28'=>28, '29'=>29,
+ '30'=>30, '31'=>31, '32'=>32, '33'=>33, '34'=>34, '35'=>35, '36'=>36, '37'=>37, '38'=>38, '39'=>39,
+ '40'=>40, '41'=>41, '42'=>42, '43'=>43, '44'=>44, '45'=>45, '46'=>46, '47'=>47, '48'=>48, '49'=>49,
+ '50'=>50, '51'=>51, '52'=>52, '53'=>53, '54'=>54, '55'=>55, '56'=>56, '57'=>57, '58'=>58, '59'=>59,
+ '60'=>60, '61'=>61, '62'=>62, '63'=>63, '64'=>64, '65'=>65, '66'=>66, '67'=>67, '68'=>68, '69'=>69,
+ '70'=>70, '71'=>71, '72'=>72, '73'=>73, '74'=>74, '75'=>75, '76'=>76, '77'=>77, '78'=>78, '79'=>79,
+ '80'=>80, '81'=>81, '82'=>82, '83'=>83, '84'=>84, '85'=>85, '86'=>86, '87'=>87, '88'=>88, '89'=>89,
+ '90'=>90, '91'=>91, '92'=>92, '93'=>93, '94'=>94, '95'=>95, '96'=>96, '97'=>97, '98'=>98, '99'=>99,
+ 'Code B'=>100, 'Code A'=>101, 'FNC1'=>102, 'START A'=>103, 'START B'=>104, 'START C'=>105, 'STOP'=>106));*/
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ // Each characters contain 11 bars...
+ $characterLength = 11 * $this->_barThinWidth * $this->_factor;
+ $convertedChars = count($this->_convertToBarcodeChars($this->getText()));
+ if ($this->_withChecksum) {
+ $convertedChars++;
+ }
+ $encodedData = $convertedChars * $characterLength;
+ // ...except the STOP character (13)
+ $encodedData += $characterLength + 2 * $this->_barThinWidth * $this->_factor;
+ $width = $quietZone + $encodedData + $quietZone;
+ return $width;
+ }
+
+ /**
+ * Partial check of code128 barcode
+ * @return void
+ */
+ protected function _checkParams()
+ {
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+
+ $convertedChars = $this->_convertToBarcodeChars($this->getText());
+
+ if ($this->_withChecksum) {
+ $convertedChars[] = $this->getChecksum($this->getText());
+ }
+
+ // STOP CHARACTER
+ $convertedChars[] = 106;
+
+ foreach ($convertedChars as $barcodeChar) {
+ $barcodePattern = $this->_codingMap[$barcodeChar];
+ foreach (str_split($barcodePattern) as $c) {
+ $barcodeTable[] = array($c, $this->_barThinWidth, 0, 1);
+ }
+ }
+ return $barcodeTable;
+ }
+
+ /**
+ * Checks if the next $length chars of $string starting at $pos are numeric.
+ * Returns false if the end of the string is reached.
+ * @param string $string String to search
+ * @param int $pos Starting position
+ * @param int $length Length to search
+ * @return bool
+ */
+ protected static function _isDigit($string, $pos, $length = 2)
+ {
+ if ($pos + $length > strlen($string)) {
+ return false;
+ }
+
+ for ($i = $pos; $i < $pos + $length; $i++) {
+ if (!is_numeric($string[$i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Convert string to barcode string
+ * @return array
+ */
+ protected function _convertToBarcodeChars($string)
+ {
+ $string = (string) $string;
+ if (!strlen($string)) {
+ return array();
+ }
+
+ if (isset($this->_convertedText[md5($string)])) {
+ return $this->_convertedText[md5($string)];
+ }
+
+ $currentCharset = null;
+ $sum = 0;
+ $fak = 0;
+ $result = array();
+
+ for ($pos = 0; $pos < strlen($string); $pos++) {
+ $char = $string[$pos];
+ $code = null;
+
+ if (self::_isDigit($string, $pos, 4) && $currentCharset != 'C'
+ || self::_isDigit($string, $pos, 2) && $currentCharset == 'C') {
+ /**
+ * Switch to C if the next 4 chars are numeric or stay C if the next 2
+ * chars are numeric
+ */
+ if ($currentCharset != 'C') {
+ if ($pos == 0) {
+ $code = array_search("START C", $this->_charSets['C']);
+ } else {
+ $code = array_search("Code C", $this->_charSets[$currentCharset]);
+ }
+ $result[] = $code;
+ $currentCharset = 'C';
+ }
+ } else if (in_array($char, $this->_charSets['B']) && $currentCharset != 'B'
+ && !(in_array($char, $this->_charSets['A']) && $currentCharset == 'A')) {
+ /**
+ * Switch to B as B contains the char and B is not the current charset.
+ */
+ if ($pos == 0) {
+ $code = array_search("START B", $this->_charSets['B']);
+ } else {
+ $code = array_search("Code B", $this->_charSets[$currentCharset]);
+ }
+ $result[] = $code;
+ $currentCharset = 'B';
+ } else if (array_key_exists($char, $this->_charSets['A']) && $currentCharset != 'A'
+ && !(array_key_exists($char, $this->_charSets['B']) && $currentCharset == 'B')) {
+ /**
+ * Switch to C as C contains the char and C is not the current charset.
+ */
+ if ($pos == 0) {
+ $code = array_search("START A", $this->_charSets['A']);
+ } else {
+ $code =array_search("Code A", $this->_charSets[$currentCharset]);
+ }
+ $result[] = $code;
+ $currentCharset = 'A';
+ }
+
+ if ($currentCharset == 'C') {
+ $code = array_search(substr($string, $pos, 2), $this->_charSets['C']);
+ $pos++; //Two chars from input
+ } else {
+ $code = array_search($string[$pos], $this->_charSets[$currentCharset]);
+ }
+ $result[] = $code;
+ }
+
+ $this->_convertedText[md5($string)] = $result;
+ return $result;
+ }
+
+ /**
+ * Set text to encode
+ * @param string $value
+ * @return Zend_Barcode_Object
+ */
+ public function setText($value)
+ {
+ $this->_text = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve text to encode
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->_text;
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $tableOfChars = $this->_convertToBarcodeChars($text);
+
+ $sum = $tableOfChars[0];
+ unset($tableOfChars[0]);
+
+ $k = 1;
+ foreach ($tableOfChars as $char) {
+ $sum += ($k++) * $char;
+ }
+
+ $checksum = $sum % 103;
+
+ return $checksum;
+ }
+
+ /**
+ * Standard validation for most of barcode objects
+ * @param string $value
+ * @param array $options
+ */
+ protected function _validateText($value, $options = array())
+ {
+ // @TODO: add code128 validator
+ return true;
+ }
+}
diff --git a/library/Zend/Barcode/Object/Code25.php b/library/Zend/Barcode/Object/Code25.php
new file mode 100644
index 0000000..d90e37f
--- /dev/null
+++ b/library/Zend/Barcode/Object/Code25.php
@@ -0,0 +1,143 @@
+ '00110',
+ '1' => '10001',
+ '2' => '01001',
+ '3' => '11000',
+ '4' => '00101',
+ '5' => '10100',
+ '6' => '01100',
+ '7' => '00011',
+ '8' => '10010',
+ '9' => '01010',
+ );
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (2 * $this->_barThickWidth + 4 * $this->_barThinWidth) * $this->_factor;
+ $characterLength = (3 * $this->_barThinWidth + 2 * $this->_barThickWidth + 5 * $this->_barThinWidth)
+ * $this->_factor;
+ $encodedData = strlen($this->getText()) * $characterLength;
+ $stopCharacter = (2 * $this->_barThickWidth + 4 * $this->_barThinWidth) * $this->_factor;
+ return $quietZone + $startCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Partial check of interleaved 2 of 5 barcode
+ * @return void
+ */
+ protected function _checkParams()
+ {
+ $this->_checkRatio();
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+
+ // Start character (30301)
+ $barcodeTable[] = array(1 , $this->_barThickWidth , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThickWidth , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth);
+
+ $text = str_split($this->getText());
+ foreach ($text as $char) {
+ $barcodeChar = str_split($this->_codingMap[$char]);
+ foreach ($barcodeChar as $c) {
+ /* visible, width, top, length */
+ $width = $c ? $this->_barThickWidth : $this->_barThinWidth;
+ $barcodeTable[] = array(1 , $width , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth);
+ }
+ }
+
+ // Stop character (30103)
+ $barcodeTable[] = array(1 , $this->_barThickWidth , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThickWidth , 0 , 1);
+ return $barcodeTable;
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $this->_checkText($text);
+ $factor = 3;
+ $checksum = 0;
+
+ for ($i = strlen($text); $i > 0; $i --) {
+ $checksum += intval($text{$i - 1}) * $factor;
+ $factor = 4 - $factor;
+ }
+
+ $checksum = (10 - ($checksum % 10)) % 10;
+
+ return $checksum;
+ }
+}
diff --git a/library/Zend/Barcode/Object/Code25interleaved.php b/library/Zend/Barcode/Object/Code25interleaved.php
new file mode 100644
index 0000000..8e5b050
--- /dev/null
+++ b/library/Zend/Barcode/Object/Code25interleaved.php
@@ -0,0 +1,179 @@
+_barcodeLength = 'even';
+ }
+
+ /**
+ * Activate/deactivate drawing of bearer bars
+ * @param boolean $value
+ * @return Zend_Barcode_Object_Int25
+ */
+ public function setWithBearerBars($value)
+ {
+ $this->_withBearerBars = (bool) $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve if bearer bars are enabled
+ * @return boolean
+ */
+ public function getWithBearerBars()
+ {
+ return $this->_withBearerBars;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (4 * $this->_barThinWidth) * $this->_factor;
+ $characterLength = (3 * $this->_barThinWidth + 2 * $this->_barThickWidth) * $this->_factor;
+ $encodedData = strlen($this->getText()) * $characterLength;
+ $stopCharacter = ($this->_barThickWidth + 2 * $this->_barThinWidth) * $this->_factor;
+ return $quietZone + $startCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ if ($this->_withBearerBars) {
+ $this->_withBorder = false;
+ }
+
+ // Start character (0000)
+ $barcodeTable[] = array(1, $this->_barThinWidth, 0, 1);
+ $barcodeTable[] = array(0, $this->_barThinWidth, 0, 1);
+ $barcodeTable[] = array(1, $this->_barThinWidth, 0, 1);
+ $barcodeTable[] = array(0, $this->_barThinWidth, 0, 1);
+
+ // Encoded $text
+ $text = $this->getText();
+ for ($i = 0; $i < strlen($text); $i += 2) { // Draw 2 chars at a time
+ $char1 = substr($text, $i, 1);
+ $char2 = substr($text, $i + 1, 1);
+
+ // Interleave
+ for ($ibar = 0; $ibar < 5; $ibar ++) {
+ // Draws char1 bar (fore color)
+ $barWidth = (substr($this->_codingMap[$char1], $ibar, 1))
+ ? $this->_barThickWidth
+ : $this->_barThinWidth;
+
+ $barcodeTable[] = array(1, $barWidth, 0, 1);
+
+ // Left space corresponding to char2 (background color)
+ $barWidth = (substr($this->_codingMap[$char2], $ibar, 1))
+ ? $this->_barThickWidth
+ : $this->_barThinWidth;
+ $barcodeTable[] = array(0, $barWidth, 0 , 1);
+ }
+ }
+
+ // Stop character (100)
+ $barcodeTable[] = array(1 , $this->_barThickWidth, 0, 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth, 0, 1);
+ $barcodeTable[] = array(1 , $this->_barThinWidth, 0, 1);
+ return $barcodeTable;
+ }
+
+ /**
+ * Drawing of bearer bars (if enabled)
+ *
+ * @return void
+ */
+ protected function _postDrawBarcode()
+ {
+ if (!$this->_withBearerBars) {
+ return;
+ }
+
+ $width = $this->_barThickWidth * $this->_factor;
+ $point1 = $this->_rotate(-1, -1);
+ $point2 = $this->_rotate($this->_calculateWidth() - 1, -1);
+ $point3 = $this->_rotate($this->_calculateWidth() - 1, $width - 1);
+ $point4 = $this->_rotate(-1, $width - 1);
+ $this->_addPolygon(array(
+ $point1,
+ $point2,
+ $point3,
+ $point4,
+ ));
+ $point1 = $this->_rotate(
+ 0,
+ 0 + $this->_barHeight * $this->_factor - 1
+ );
+ $point2 = $this->_rotate(
+ $this->_calculateWidth() - 1,
+ 0 + $this->_barHeight * $this->_factor - 1
+ );
+ $point3 = $this->_rotate(
+ $this->_calculateWidth() - 1,
+ 0 + $this->_barHeight * $this->_factor - $width
+ );
+ $point4 = $this->_rotate(
+ 0,
+ 0 + $this->_barHeight * $this->_factor - $width
+ );
+ $this->_addPolygon(array(
+ $point1,
+ $point2,
+ $point3,
+ $point4,
+ ));
+ }
+}
diff --git a/library/Zend/Barcode/Object/Code39.php b/library/Zend/Barcode/Object/Code39.php
new file mode 100644
index 0000000..03544ea
--- /dev/null
+++ b/library/Zend/Barcode/Object/Code39.php
@@ -0,0 +1,188 @@
+ '000110100',
+ '1' => '100100001',
+ '2' => '001100001',
+ '3' => '101100000',
+ '4' => '000110001',
+ '5' => '100110000',
+ '6' => '001110000',
+ '7' => '000100101',
+ '8' => '100100100',
+ '9' => '001100100',
+ 'A' => '100001001',
+ 'B' => '001001001',
+ 'C' => '101001000',
+ 'D' => '000011001',
+ 'E' => '100011000',
+ 'F' => '001011000',
+ 'G' => '000001101',
+ 'H' => '100001100',
+ 'I' => '001001100',
+ 'J' => '000011100',
+ 'K' => '100000011',
+ 'L' => '001000011',
+ 'M' => '101000010',
+ 'N' => '000010011',
+ 'O' => '100010010',
+ 'P' => '001010010',
+ 'Q' => '000000111',
+ 'R' => '100000110',
+ 'S' => '001000110',
+ 'T' => '000010110',
+ 'U' => '110000001',
+ 'V' => '011000001',
+ 'W' => '111000000',
+ 'X' => '010010001',
+ 'Y' => '110010000',
+ 'Z' => '011010000',
+ '-' => '010000101',
+ '.' => '110000100',
+ ' ' => '011000100',
+ '$' => '010101000',
+ '/' => '010100010',
+ '+' => '010001010',
+ '%' => '000101010',
+ '*' => '010010100',
+ );
+
+ /**
+ * Partial check of Code39 barcode
+ * @return void
+ */
+ protected function _checkParams()
+ {
+ $this->_checkRatio();
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return int
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $characterLength = (6 * $this->_barThinWidth + 3 * $this->_barThickWidth + 1) * $this->_factor;
+ $encodedData = strlen($this->getText()) * $characterLength - $this->_factor;
+ return $quietZone + $encodedData + $quietZone;
+ }
+
+ /**
+ * Set text to encode
+ * @param string $value
+ * @return Zend_Barcode_Object
+ */
+ public function setText($value)
+ {
+ $this->_text = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve text to display
+ * @return string
+ */
+ public function getText()
+ {
+ return '*' . parent::getText() . '*';
+ }
+
+ /**
+ * Retrieve text to display
+ * @return string
+ */
+ public function getTextToDisplay()
+ {
+ $text = parent::getTextToDisplay();
+ if (substr($text, 0, 1) != '*' && substr($text, -1) != '*') {
+ return '*' . $text . '*';
+ } else {
+ return $text;
+ }
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $text = str_split($this->getText());
+ $barcodeTable = array();
+ foreach ($text as $char) {
+ $barcodeChar = str_split($this->_codingMap[$char]);
+ $visible = true;
+ foreach ($barcodeChar as $c) {
+ /* visible, width, top, length */
+ $width = $c ? $this->_barThickWidth : $this->_barThinWidth;
+ $barcodeTable[] = array((int) $visible, $width, 0, 1);
+ $visible = ! $visible;
+ }
+ $barcodeTable[] = array(0 , $this->_barThinWidth);
+ }
+ return $barcodeTable;
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $this->_checkText($text);
+ $text = str_split($text);
+ $charset = array_flip(array_keys($this->_codingMap));
+ $checksum = 0;
+ foreach ($text as $character) {
+ $checksum += $charset[$character];
+ }
+ return array_search(($checksum % 43), $charset);
+ }
+}
diff --git a/library/Zend/Barcode/Object/Ean13.php b/library/Zend/Barcode/Object/Ean13.php
new file mode 100644
index 0000000..9b636e3
--- /dev/null
+++ b/library/Zend/Barcode/Object/Ean13.php
@@ -0,0 +1,225 @@
+ array(
+ 0 => "0001101", 1 => "0011001", 2 => "0010011", 3 => "0111101", 4 => "0100011",
+ 5 => "0110001", 6 => "0101111", 7 => "0111011", 8 => "0110111", 9 => "0001011"
+ ),
+ 'B' => array(
+ 0 => "0100111", 1 => "0110011", 2 => "0011011", 3 => "0100001", 4 => "0011101",
+ 5 => "0111001", 6 => "0000101", 7 => "0010001", 8 => "0001001", 9 => "0010111"
+ ),
+ 'C' => array(
+ 0 => "1110010", 1 => "1100110", 2 => "1101100", 3 => "1000010", 4 => "1011100",
+ 5 => "1001110", 6 => "1010000", 7 => "1000100", 8 => "1001000", 9 => "1110100"
+ ));
+
+ protected $_parities = array(
+ 0 => array('A','A','A','A','A','A'),
+ 1 => array('A','A','B','A','B','B'),
+ 2 => array('A','A','B','B','A','B'),
+ 3 => array('A','A','B','B','B','A'),
+ 4 => array('A','B','A','A','B','B'),
+ 5 => array('A','B','B','A','A','B'),
+ 6 => array('A','B','B','B','A','A'),
+ 7 => array('A','B','A','B','A','B'),
+ 8 => array('A','B','A','B','B','A'),
+ 9 => array('A','B','B','A','B','A')
+ );
+
+ /**
+ * Default options for Postnet barcode
+ * @return void
+ */
+ protected function _getDefaultOptions()
+ {
+ $this->_barcodeLength = 13;
+ $this->_mandatoryChecksum = true;
+ $this->_mandatoryQuietZones = true;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (3 * $this->_barThinWidth) * $this->_factor;
+ $middleCharacter = (5 * $this->_barThinWidth) * $this->_factor;
+ $stopCharacter = (3 * $this->_barThinWidth) * $this->_factor;
+ $encodedData = (7 * $this->_barThinWidth) * $this->_factor * 12;
+ return $quietZone + $startCharacter + $middleCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Partial check of interleaved EAN/UPC barcode
+ * @return void
+ */
+ protected function _checkParams()
+ {}
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+ $height = ($this->_drawText) ? 1.1 : 1;
+
+ // Start character (101)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+
+ $textTable = str_split($this->getText());
+ $parity = $this->_parities[$textTable[0]];
+
+ // First part
+ for ($i = 1; $i < 7; $i++) {
+ $bars = str_split($this->_codingMap[$parity[$i - 1]][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Middle character (01010)
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+
+ // Second part
+ for ($i = 7; $i < 13; $i++) {
+ $bars = str_split($this->_codingMap['C'][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Stop character (101)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ return $barcodeTable;
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $this->_checkText($text);
+ $factor = 3;
+ $checksum = 0;
+
+ for ($i = strlen($text); $i > 0; $i --) {
+ $checksum += intval($text{$i - 1}) * $factor;
+ $factor = 4 - $factor;
+ }
+
+ $checksum = (10 - ($checksum % 10)) % 10;
+
+ return $checksum;
+ }
+
+ /**
+ * Partial function to draw text
+ * @return void
+ */
+ protected function _drawText()
+ {
+ if (get_class($this) == 'Zend_Barcode_Object_Ean13') {
+ $this->_drawEan13Text();
+ } else {
+ parent::_drawText();
+ }
+ }
+
+ protected function _drawEan13Text()
+ {
+ if ($this->_drawText) {
+ $text = $this->getTextToDisplay();
+ $characterWidth = (7 * $this->_barThinWidth) * $this->_factor;
+ $leftPosition = $this->getQuietZone() - $characterWidth;
+ for ($i = 0; $i < $this->_barcodeLength; $i ++) {
+ $this->_addText(
+ $text{$i},
+ $this->_fontSize * $this->_factor,
+ $this->_rotate(
+ $leftPosition,
+ (int) $this->_withBorder * 2
+ + $this->_factor * ($this->_barHeight + $this->_fontSize) + 1
+ ),
+ $this->_font,
+ $this->_foreColor,
+ 'left',
+ - $this->_orientation
+ );
+ switch ($i) {
+ case 0:
+ $factor = 3;
+ break;
+ case 6:
+ $factor = 4;
+ break;
+ default:
+ $factor = 0;
+ }
+ $leftPosition = $leftPosition + $characterWidth + ($factor * $this->_barThinWidth * $this->_factor);
+ }
+ }
+ }
+}
diff --git a/library/Zend/Barcode/Object/Ean2.php b/library/Zend/Barcode/Object/Ean2.php
new file mode 100644
index 0000000..df470ce
--- /dev/null
+++ b/library/Zend/Barcode/Object/Ean2.php
@@ -0,0 +1,65 @@
+ array('A','A'),
+ 1 => array('A','B'),
+ 2 => array('B','A'),
+ 3 => array('B','B')
+ );
+
+ /**
+ * Default options for Ean2 barcode
+ * @return void
+ */
+ protected function _getDefaultOptions()
+ {
+ $this->_barcodeLength = 2;
+ }
+
+ protected function _getParity($i)
+ {
+ $modulo = $this->getText() % 4;
+ return $this->_parities[$modulo][$i];
+ }
+}
diff --git a/library/Zend/Barcode/Object/Ean5.php b/library/Zend/Barcode/Object/Ean5.php
new file mode 100644
index 0000000..7f7ca9c
--- /dev/null
+++ b/library/Zend/Barcode/Object/Ean5.php
@@ -0,0 +1,147 @@
+ array('B','B','A','A','A'),
+ 1 => array('B','A','B','A','A'),
+ 2 => array('B','A','A','B','A'),
+ 3 => array('B','A','A','A','B'),
+ 4 => array('A','B','B','A','A'),
+ 5 => array('A','A','B','B','A'),
+ 6 => array('A','A','A','B','B'),
+ 7 => array('A','B','A','B','A'),
+ 8 => array('A','B','A','A','B'),
+ 9 => array('A','A','B','A','B')
+ );
+
+ /**
+ * Default options for Ean5 barcode
+ * @return void
+ */
+ protected function _getDefaultOptions()
+ {
+ $this->_barcodeLength = 5;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (5 * $this->_barThinWidth) * $this->_factor;
+ $middleCharacter = (2 * $this->_barThinWidth) * $this->_factor;
+ $encodedData = (7 * $this->_barThinWidth) * $this->_factor;
+ return $quietZone + $startCharacter + ($this->_barcodeLength - 1) * $middleCharacter + $this->_barcodeLength * $encodedData + $quietZone;
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+
+ // Start character (01011)
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+
+ $firstCharacter = true;
+ $textTable = str_split($this->getText());
+
+ // Characters
+ for ($i = 0; $i < $this->_barcodeLength; $i++) {
+ if ($firstCharacter) {
+ $firstCharacter = false;
+ } else {
+ // Intermediate character (01)
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ }
+ $bars = str_split($this->_codingMap[$this->_getParity($i)][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ return $barcodeTable;
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $this->_checkText($text);
+ $checksum = 0;
+
+ for ($i = 0 ; $i < $this->_barcodeLength; $i ++) {
+ $checksum += intval($text{$i}) * ($i % 2 ? 9 : 3);
+ }
+
+ return ($checksum % 10);
+ }
+
+ protected function _getParity($i)
+ {
+ $checksum = $this->getChecksum($this->getText());
+ return $this->_parities[$checksum][$i];
+ }
+
+ /**
+ * Retrieve text to encode
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->_addLeadingZeros($this->_text);
+ }
+}
diff --git a/library/Zend/Barcode/Object/Ean8.php b/library/Zend/Barcode/Object/Ean8.php
new file mode 100644
index 0000000..1589d9b
--- /dev/null
+++ b/library/Zend/Barcode/Object/Ean8.php
@@ -0,0 +1,175 @@
+_barcodeLength = 8;
+ $this->_mandatoryChecksum = true;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (3 * $this->_barThinWidth) * $this->_factor;
+ $middleCharacter = (5 * $this->_barThinWidth) * $this->_factor;
+ $stopCharacter = (3 * $this->_barThinWidth) * $this->_factor;
+ $encodedData = (7 * $this->_barThinWidth) * $this->_factor * 8;
+ return $quietZone + $startCharacter + $middleCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+ $height = ($this->_drawText) ? 1.1 : 1;
+
+ // Start character (101)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+
+ $textTable = str_split($this->getText());
+
+ // First part
+ for ($i = 0; $i < 4; $i++) {
+ $bars = str_split($this->_codingMap['A'][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Middle character (01010)
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+
+ // Second part
+ for ($i = 4; $i < 8; $i++) {
+ $bars = str_split($this->_codingMap['C'][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Stop character (101)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ return $barcodeTable;
+ }
+
+ /**
+ * Partial function to draw text
+ * @return void
+ */
+ protected function _drawText()
+ {
+ if ($this->_drawText) {
+ $text = $this->getTextToDisplay();
+ $characterWidth = (7 * $this->_barThinWidth) * $this->_factor;
+ $leftPosition = $this->getQuietZone() + (3 * $this->_barThinWidth) * $this->_factor;
+ for ($i = 0; $i < $this->_barcodeLength; $i ++) {
+ $this->_addText(
+ $text{$i},
+ $this->_fontSize * $this->_factor,
+ $this->_rotate(
+ $leftPosition,
+ (int) $this->_withBorder * 2
+ + $this->_factor * ($this->_barHeight + $this->_fontSize) + 1
+ ),
+ $this->_font,
+ $this->_foreColor,
+ 'left',
+ - $this->_orientation
+ );
+ switch ($i) {
+ case 3:
+ $factor = 4;
+ break;
+ default:
+ $factor = 0;
+ }
+ $leftPosition = $leftPosition + $characterWidth + ($factor * $this->_barThinWidth * $this->_factor);
+ }
+ }
+ }
+
+ /**
+ * Particular validation for Ean8 barcode objects
+ * (to suppress checksum character substitution)
+ * @param string $value
+ * @param array $options
+ */
+ protected function _validateText($value, $options = array())
+ {
+ $validator = new Zend_Validate_Barcode(array(
+ 'adapter' => 'ean8',
+ 'checksum' => false,
+ ));
+
+ $value = $this->_addLeadingZeros($value, true);
+
+ if (!$validator->isValid($value)) {
+ $message = implode("\n", $validator->getMessages());
+
+ /**
+ * @see Zend_Barcode_Object_Exception
+ */
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception($message);
+ }
+ }
+}
diff --git a/library/Zend/Barcode/Object/Error.php b/library/Zend/Barcode/Object/Error.php
new file mode 100644
index 0000000..a9c4c73
--- /dev/null
+++ b/library/Zend/Barcode/Object/Error.php
@@ -0,0 +1,100 @@
+_instructions = array();
+ $this->_addText('ERROR:', 10, array(5 , 18), $this->_font, 0, 'left');
+ $this->_addText($this->_text, 10, array(5 , 32), $this->_font, 0, 'left');
+ return $this->_instructions;
+ }
+
+ /**
+ * For compatibility reason
+ * @return void
+ */
+ protected function _prepareBarcode()
+ {
+ }
+
+ /**
+ * For compatibility reason
+ * @return void
+ */
+ protected function _checkParams()
+ {
+ }
+
+ /**
+ * For compatibility reason
+ * @return void
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ }
+}
diff --git a/library/Zend/Barcode/Object/Exception.php b/library/Zend/Barcode/Object/Exception.php
new file mode 100644
index 0000000..dab3153
--- /dev/null
+++ b/library/Zend/Barcode/Object/Exception.php
@@ -0,0 +1,35 @@
+_barcodeLength = 12;
+ $this->_mandatoryChecksum = true;
+ }
+
+ /**
+ * Retrieve text to display
+ * @return string
+ */
+ public function getTextToDisplay()
+ {
+ return preg_replace('/([0-9]{2})([0-9]{3})([0-9]{3})([0-9]{3})([0-9])/',
+ '$1.$2 $3.$4 $5',
+ $this->getText());
+ }
+
+ /**
+ * Check allowed characters
+ * @param string $value
+ * @return string
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function validateText($value)
+ {
+ $this->_validateText($value, array('validator' => $this->getType()));
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $this->_checkText($text);
+ $checksum = 0;
+
+ for ($i = strlen($text); $i > 0; $i --) {
+ $checksum += intval($text{$i - 1}) * (($i % 2) ? 4 : 9);
+ }
+
+ $checksum = (10 - ($checksum % 10)) % 10;
+
+ return $checksum;
+ }
+}
diff --git a/library/Zend/Barcode/Object/Itf14.php b/library/Zend/Barcode/Object/Itf14.php
new file mode 100644
index 0000000..e88db24
--- /dev/null
+++ b/library/Zend/Barcode/Object/Itf14.php
@@ -0,0 +1,49 @@
+_barcodeLength = 14;
+ $this->_mandatoryChecksum = true;
+ }
+}
diff --git a/library/Zend/Barcode/Object/Leitcode.php b/library/Zend/Barcode/Object/Leitcode.php
new file mode 100644
index 0000000..a94c904
--- /dev/null
+++ b/library/Zend/Barcode/Object/Leitcode.php
@@ -0,0 +1,64 @@
+_barcodeLength = 14;
+ $this->_mandatoryChecksum = true;
+ }
+
+ /**
+ * Retrieve text to display
+ * @return string
+ */
+ public function getTextToDisplay()
+ {
+ return preg_replace('/([0-9]{5})([0-9]{3})([0-9]{3})([0-9]{2})([0-9])/',
+ '$1.$2.$3.$4 $5',
+ $this->getText());
+ }
+}
diff --git a/library/Zend/Barcode/Object/ObjectAbstract.php b/library/Zend/Barcode/Object/ObjectAbstract.php
new file mode 100644
index 0000000..b79c711
--- /dev/null
+++ b/library/Zend/Barcode/Object/ObjectAbstract.php
@@ -0,0 +1,1317 @@
+_getDefaultOptions();
+ if (self::$_staticFont !== null) {
+ $this->_font = self::$_staticFont;
+ }
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ }
+ if (is_array($options)) {
+ $this->setOptions($options);
+ }
+ $this->_type = strtolower(substr(get_class($this), strlen($this->_barcodeNamespace) + 1));
+ if ($this->_mandatoryChecksum) {
+ $this->_withChecksum = true;
+ $this->_withChecksumInText = true;
+ }
+ }
+
+ /**
+ * Set default options for particular object
+ * @return void
+ */
+ protected function _getDefaultOptions()
+ {
+ }
+
+ /**
+ * Set barcode state from options array
+ * @param array $options
+ * @return Zend_Barcode_Object
+ */
+ public function setOptions($options)
+ {
+ foreach ($options as $key => $value) {
+ $method = 'set' . $key;
+ if (method_exists($this, $method)) {
+ $this->$method($value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Set barcode state from config object
+ * @param Zend_Config $config
+ * @return Zend_Barcode_Object
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ return $this->setOptions($config->toArray());
+ }
+
+ /**
+ * Set barcode namespace for autoloading
+ *
+ * @param string $namespace
+ * @return Zend_Barcode_Object
+ */
+ public function setBarcodeNamespace($namespace)
+ {
+ $this->_barcodeNamespace = $namespace;
+ return $this;
+ }
+
+ /**
+ * Retrieve barcode namespace
+ *
+ * @return string
+ */
+ public function getBarcodeNamespace()
+ {
+ return $this->_barcodeNamespace;
+ }
+
+ /**
+ * Retrieve type of barcode
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * Set height of the barcode bar
+ * @param integer $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setBarHeight($value)
+ {
+ if (intval($value) <= 0) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Bar height must be greater than 0'
+ );
+ }
+ $this->_barHeight = intval($value);
+ return $this;
+ }
+
+ /**
+ * Get height of the barcode bar
+ * @return integer
+ */
+ public function getBarHeight()
+ {
+ return $this->_barHeight;
+ }
+
+ /**
+ * Set thickness of thin bar
+ * @param integer $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setBarThinWidth($value)
+ {
+ if (intval($value) <= 0) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Bar width must be greater than 0'
+ );
+ }
+ $this->_barThinWidth = intval($value);
+ return $this;
+ }
+
+ /**
+ * Get thickness of thin bar
+ * @return integer
+ */
+ public function getBarThinWidth()
+ {
+ return $this->_barThinWidth;
+ }
+
+ /**
+ * Set thickness of thick bar
+ * @param integer $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setBarThickWidth($value)
+ {
+ if (intval($value) <= 0) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Bar width must be greater than 0'
+ );
+ }
+ $this->_barThickWidth = intval($value);
+ return $this;
+ }
+
+ /**
+ * Get thickness of thick bar
+ * @return integer
+ */
+ public function getBarThickWidth()
+ {
+ return $this->_barThickWidth;
+ }
+
+ /**
+ * Set factor applying to
+ * thinBarWidth - thickBarWidth - barHeight - fontSize
+ * @param float $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setFactor($value)
+ {
+ if (floatval($value) <= 0) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Factor must be greater than 0'
+ );
+ }
+ $this->_factor = floatval($value);
+ return $this;
+ }
+
+ /**
+ * Get factor applying to
+ * thinBarWidth - thickBarWidth - barHeight - fontSize
+ * @return integer
+ */
+ public function getFactor()
+ {
+ return $this->_factor;
+ }
+
+ /**
+ * Set color of the barcode and text
+ * @param string $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setForeColor($value)
+ {
+ if (preg_match('`\#[0-9A-F]{6}`', $value)) {
+ $this->_foreColor = hexdec($value);
+ } elseif (is_numeric($value) && $value >= 0 && $value <= 16777125) {
+ $this->_foreColor = intval($value);
+ } else {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Text color must be set as #[0-9A-F]{6}'
+ );
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve color of the barcode and text
+ * @return unknown
+ */
+ public function getForeColor()
+ {
+ return $this->_foreColor;
+ }
+
+ /**
+ * Set the color of the background
+ * @param integer $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setBackgroundColor($value)
+ {
+ if (preg_match('`\#[0-9A-F]{6}`', $value)) {
+ $this->_backgroundColor = hexdec($value);
+ } elseif (is_numeric($value) && $value >= 0 && $value <= 16777125) {
+ $this->_backgroundColor = intval($value);
+ } else {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Background color must be set as #[0-9A-F]{6}'
+ );
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve background color of the image
+ * @return integer
+ */
+ public function getBackgroundColor()
+ {
+ return $this->_backgroundColor;
+ }
+
+ /**
+ * Activate/deactivate drawing of the bar
+ * @param boolean $value
+ * @return Zend_Barcode_Object
+ */
+ public function setWithBorder($value)
+ {
+ $this->_withBorder = (bool) $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve if border are draw or not
+ * @return boolean
+ */
+ public function getWithBorder()
+ {
+ return $this->_withBorder;
+ }
+
+ /**
+ * Activate/deactivate drawing of the quiet zones
+ * @param boolean $value
+ * @return Zend_Barcode_Object
+ */
+ public function setWithQuietZones($value)
+ {
+ $this->_withQuietZones = (bool) $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve if quiet zones are draw or not
+ * @return boolean
+ */
+ public function getWithQuietZones()
+ {
+ return $this->_withQuietZones;
+ }
+
+ /**
+ * Allow fast inversion of font/bars color and background color
+ * @return Zend_Barcode_Object
+ */
+ public function setReverseColor()
+ {
+ $tmp = $this->_foreColor;
+ $this->_foreColor = $this->_backgroundColor;
+ $this->_backgroundColor = $tmp;
+ return $this;
+ }
+
+ /**
+ * Set orientation of barcode and text
+ * @param float $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setOrientation($value)
+ {
+ $this->_orientation = floatval($value) - floor(floatval($value) / 360) * 360;
+ return $this;
+ }
+
+ /**
+ * Retrieve orientation of barcode and text
+ * @return float
+ */
+ public function getOrientation()
+ {
+ return $this->_orientation;
+ }
+
+ /**
+ * Set text to encode
+ * @param string $value
+ * @return Zend_Barcode_Object
+ */
+ public function setText($value)
+ {
+ $this->_text = trim($value);
+ return $this;
+ }
+
+ /**
+ * Retrieve text to encode
+ * @return string
+ */
+ public function getText()
+ {
+ $text = $this->_text;
+ if ($this->_withChecksum) {
+ $text .= $this->getChecksum($this->_text);
+ }
+ return $this->_addLeadingZeros($text);
+ }
+
+ /**
+ * Automatically add leading zeros if barcode length is fixed
+ * @param string $text
+ * @param boolean $withoutChecksum
+ */
+ protected function _addLeadingZeros($text, $withoutChecksum = false)
+ {
+ if ($this->_barcodeLength && $this->_addLeadingZeros) {
+ $omitChecksum = (int) ($this->_withChecksum && $withoutChecksum);
+ if (is_int($this->_barcodeLength)) {
+ $length = $this->_barcodeLength - $omitChecksum;
+ if (strlen($text) < $length) {
+ $text = str_repeat('0', $length - strlen($text)) . $text;
+ }
+ } else {
+ if ($this->_barcodeLength == 'even') {
+ $text = ((strlen($text) - $omitChecksum) % 2 ? '0' . $text : $text);
+ }
+ }
+ }
+ return $text;
+ }
+
+ /**
+ * Retrieve text to encode
+ * @return string
+ */
+ public function getRawText()
+ {
+ return $this->_text;
+ }
+
+ /**
+ * Retrieve text to display
+ * @return string
+ */
+ public function getTextToDisplay()
+ {
+ if ($this->_withChecksumInText) {
+ return $this->getText();
+ } else {
+ return $this->_addLeadingZeros($this->_text, true);
+ }
+ }
+
+ /**
+ * Activate/deactivate drawing of text to encode
+ * @param boolean $value
+ * @return Zend_Barcode_Object
+ */
+ public function setDrawText($value)
+ {
+ $this->_drawText = (bool) $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve if drawing of text to encode is enabled
+ * @return boolean
+ */
+ public function getDrawText()
+ {
+ return $this->_drawText;
+ }
+
+ /**
+ * Activate/deactivate the adjustment of the position
+ * of the characters to the position of the bars
+ * @param boolean $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setStretchText($value)
+ {
+ $this->_stretchText = (bool) $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve if the adjustment of the position of the characters
+ * to the position of the bars is enabled
+ * @return boolean
+ */
+ public function getStretchText()
+ {
+ return $this->_stretchText;
+ }
+
+ /**
+ * Activate/deactivate the automatic generation
+ * of the checksum character
+ * added to the barcode text
+ * @param boolean $value
+ * @return Zend_Barcode_Object
+ */
+ public function setWithChecksum($value)
+ {
+ if (!$this->_mandatoryChecksum) {
+ $this->_withChecksum = (bool) $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve if the checksum character is automatically
+ * added to the barcode text
+ * @return boolean
+ */
+ public function getWithChecksum()
+ {
+ return $this->_withChecksum;
+ }
+
+ /**
+ * Activate/deactivate the automatic generation
+ * of the checksum character
+ * added to the barcode text
+ * @param boolean $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setWithChecksumInText($value)
+ {
+ if (!$this->_mandatoryChecksum) {
+ $this->_withChecksumInText = (bool) $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve if the checksum character is automatically
+ * added to the barcode text
+ * @return boolean
+ */
+ public function getWithChecksumInText()
+ {
+ return $this->_withChecksumInText;
+ }
+
+ /**
+ * Set the font for all instances of barcode
+ * @param string $font
+ * @return void
+ */
+ public static function setBarcodeFont($font)
+ {
+ if (is_string($font) || (is_int($font) && $font >= 1 && $font <= 5)) {
+ self::$_staticFont = $font;
+ }
+ }
+
+ /**
+ * Set the font:
+ * - if integer between 1 and 5, use gd built-in fonts
+ * - if string, $value is assumed to be the path to a TTF font
+ * @param integer|string $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setFont($value)
+ {
+ if (is_int($value) && $value >= 1 && $value <= 5) {
+ if (!extension_loaded('gd')) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'GD extension is required to use numeric font'
+ );
+ }
+
+ // Case of numeric font with GD
+ $this->_font = $value;
+
+ // In this case font size is given by:
+ $this->_fontSize = imagefontheight($value);
+ } elseif (is_string($value)) {
+ $this->_font = $value;
+ } else {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(sprintf(
+ 'Invalid font "%s" provided to setFont()',
+ $value
+ ));
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve the font
+ * @return integer|string
+ */
+ public function getFont()
+ {
+ return $this->_font;
+ }
+
+ /**
+ * Set the size of the font in case of TTF
+ * @param float $value
+ * @return Zend_Barcode_Object
+ * @throw Zend_Barcode_Object_Exception
+ */
+ public function setFontSize($value)
+ {
+ if (is_numeric($this->_font)) {
+ // Case of numeric font with GD
+ return $this;
+ }
+
+ if (!is_numeric($value)) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Font size must be a numeric value'
+ );
+ }
+
+ $this->_fontSize = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve the size of the font in case of TTF
+ * @return float
+ */
+ public function getFontSize()
+ {
+ return $this->_fontSize;
+ }
+
+ /**
+ * Quiet zone before first bar
+ * and after the last bar
+ * @return integer
+ */
+ public function getQuietZone()
+ {
+ if ($this->_withQuietZones || $this->_mandatoryQuietZones) {
+ return 10 * $this->_barThinWidth * $this->_factor;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Add an instruction in the array of instructions
+ * @param array $instruction
+ */
+ protected function _addInstruction(array $instruction)
+ {
+ $this->_instructions[] = $instruction;
+ }
+
+ /**
+ * Retrieve the set of drawing instructions
+ * @return array
+ */
+ public function getInstructions()
+ {
+ return $this->_instructions;
+ }
+
+ /**
+ * Add a polygon drawing instruction in the set of instructions
+ * @param array $points
+ * @param integer $color
+ * @param boolean $filled
+ */
+ protected function _addPolygon(array $points, $color = null, $filled = true)
+ {
+ if ($color === null) {
+ $color = $this->_foreColor;
+ }
+ $this->_addInstruction(array(
+ 'type' => 'polygon',
+ 'points' => $points,
+ 'color' => $color,
+ 'filled' => $filled,
+ ));
+ }
+
+ /**
+ * Add a text drawing instruction in the set of instructions
+ * @param string $text
+ * @param float $size
+ * @param array $position
+ * @param string $font
+ * @param integer $color
+ * @param string $alignment
+ * @param float $orientation
+ */
+ protected function _addText(
+ $text,
+ $size,
+ $position,
+ $font,
+ $color,
+ $alignment = 'center',
+ $orientation = 0
+ ) {
+ if ($color === null) {
+ $color = $this->_foreColor;
+ }
+ $this->_addInstruction(array(
+ 'type' => 'text',
+ 'text' => $text,
+ 'size' => $size,
+ 'position' => $position,
+ 'font' => $font,
+ 'color' => $color,
+ 'alignment' => $alignment,
+ 'orientation' => $orientation,
+ ));
+ }
+
+ /**
+ * Checking of parameters after all settings
+ * @return void
+ */
+ public function checkParams()
+ {
+ $this->_checkText();
+ $this->_checkFontAndOrientation();
+ $this->_checkParams();
+ return true;
+ }
+
+ /**
+ * Check if a text is really provided to barcode
+ * @return void
+ * @throw Zend_Barcode_Object_Exception
+ */
+ protected function _checkText($value = null)
+ {
+ if ($value === null) {
+ $value = $this->_text;
+ }
+ if (!strlen($value)) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'A text must be provide to Barcode before drawing'
+ );
+ }
+ $this->validateText($value);
+ }
+
+ /**
+ * Check the ratio between the thick and the thin bar
+ * @param integer $min
+ * @param integer $max
+ * @return void
+ * @throw Zend_Barcode_Object_Exception
+ */
+ protected function _checkRatio($min = 2, $max = 3)
+ {
+ $ratio = $this->_barThickWidth / $this->_barThinWidth;
+ if (!($ratio >= $min && $ratio <= $max)) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(sprintf(
+ 'Ratio thick/thin bar must be between %0.1f and %0.1f (actual %0.3f)',
+ $min,
+ $max,
+ $ratio
+ ));
+ }
+ }
+
+ /**
+ * Drawing with an angle is just allow TTF font
+ * @return void
+ * @throw Zend_Barcode_Object_Exception
+ */
+ protected function _checkFontAndOrientation()
+ {
+ if (is_numeric($this->_font) && $this->_orientation != 0) {
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception(
+ 'Only drawing with TTF font allow orientation of the barcode.'
+ );
+ }
+ }
+
+ /**
+ * Width of the result image
+ * (before any rotation)
+ * @return integer
+ */
+ protected function _calculateWidth()
+ {
+ return (int) $this->_withBorder
+ + $this->_calculateBarcodeWidth()
+ + (int) $this->_withBorder;
+ }
+
+ /**
+ * Calculate the width of the barcode
+ * @return integer
+ */
+ abstract protected function _calculateBarcodeWidth();
+
+ /**
+ * Height of the result object
+ * @return integer
+ */
+ protected function _calculateHeight()
+ {
+ return (int) $this->_withBorder * 2
+ + $this->_calculateBarcodeHeight()
+ + (int) $this->_withBorder * 2;
+ }
+
+ /**
+ * Height of the barcode
+ * @return integer
+ */
+ protected function _calculateBarcodeHeight()
+ {
+ $textHeight = 0;
+ $extraHeight = 0;
+ if ($this->_drawText) {
+ $textHeight += $this->_fontSize;
+ $extraHeight = 2;
+ }
+ return ($this->_barHeight + $textHeight) * $this->_factor + $extraHeight;
+ }
+
+ /**
+ * Get height of the result object
+ * @return integer
+ */
+ public function getHeight($recalculate = false)
+ {
+ if ($this->_height === null || $recalculate) {
+ $this->_height =
+ abs($this->_calculateHeight() * cos($this->_orientation / 180 * pi()))
+ + abs($this->_calculateWidth() * sin($this->_orientation / 180 * pi()));
+ }
+ return $this->_height;
+ }
+
+ /**
+ * Get width of the result object
+ * @return integer
+ */
+ public function getWidth($recalculate = false)
+ {
+ if ($this->_width === null || $recalculate) {
+ $this->_width =
+ abs($this->_calculateWidth() * cos($this->_orientation / 180 * pi()))
+ + abs($this->_calculateHeight() * sin($this->_orientation / 180 * pi()));
+ }
+ return $this->_width;
+ }
+
+ /**
+ * Calculate the offset from the left of the object
+ * if an orientation is activated
+ * @param boolean $recalculate
+ * @return float
+ */
+ public function getOffsetLeft($recalculate = false)
+ {
+ if ($this->_offsetLeft === null || $recalculate) {
+ $this->_offsetLeft = - min(array(
+ 0 * cos(
+ $this->_orientation / 180 * pi()) - 0 * sin(
+ $this->_orientation / 180 * pi()),
+ 0 * cos(
+ $this->_orientation / 180 * pi()) - $this->_calculateBarcodeHeight() * sin(
+ $this->_orientation / 180 * pi()),
+ $this->_calculateBarcodeWidth() * cos(
+ $this->_orientation / 180 * pi()) - $this->_calculateBarcodeHeight() * sin(
+ $this->_orientation / 180 * pi()),
+ $this->_calculateBarcodeWidth() * cos(
+ $this->_orientation / 180 * pi()) - 0 * sin(
+ $this->_orientation / 180 * pi()),
+ ));
+ }
+ return $this->_offsetLeft;
+ }
+
+ /**
+ * Calculate the offset from the top of the object
+ * if an orientation is activated
+ * @param boolean $recalculate
+ * @return float
+ */
+ public function getOffsetTop($recalculate = false)
+ {
+ if ($this->_offsetTop === null || $recalculate) {
+ $this->_offsetTop = - min(array(
+ 0 * cos(
+ $this->_orientation / 180 * pi()) + 0 * sin(
+ $this->_orientation / 180 * pi()),
+ $this->_calculateBarcodeHeight() * cos(
+ $this->_orientation / 180 * pi()) + 0 * sin(
+ $this->_orientation / 180 * pi()),
+ $this->_calculateBarcodeHeight() * cos(
+ $this->_orientation / 180 * pi()) + $this->_calculateBarcodeWidth() * sin(
+ $this->_orientation / 180 * pi()),
+ 0 * cos(
+ $this->_orientation / 180 * pi()) + $this->_calculateBarcodeWidth() * sin(
+ $this->_orientation / 180 * pi()),
+ ));
+ }
+ return $this->_offsetTop;
+ }
+
+ /**
+ * Apply rotation on a point in X/Y dimensions
+ * @param float $x1 x-position before rotation
+ * @param float $y1 y-position before rotation
+ * @return array Array of two elements corresponding to the new XY point
+ */
+ protected function _rotate($x1, $y1)
+ {
+ $x2 = $x1 * cos($this->_orientation / 180 * pi())
+ - $y1 * sin($this->_orientation / 180 * pi())
+ + $this->getOffsetLeft();
+ $y2 = $y1 * cos($this->_orientation / 180 * pi())
+ + $x1 * sin($this->_orientation / 180 * pi())
+ + $this->getOffsetTop();
+ return array(intval($x2) , intval($y2));
+ }
+
+ /**
+ * Complete drawing of the barcode
+ * @return array Table of instructions
+ */
+ public function draw()
+ {
+ $this->checkParams();
+ $this->_drawBarcode();
+ $this->_drawBorder();
+ $this->_drawText();
+ return $this->getInstructions();
+ }
+
+ /**
+ * Draw the barcode
+ * @return void
+ */
+ protected function _drawBarcode()
+ {
+ $barcodeTable = $this->_prepareBarcode();
+
+ $this->_preDrawBarcode();
+
+ $xpos = (int) $this->_withBorder;
+ $ypos = (int) $this->_withBorder;
+
+ $point1 = $this->_rotate(0, 0);
+ $point2 = $this->_rotate(0, $this->_calculateHeight() - 1);
+ $point3 = $this->_rotate(
+ $this->_calculateWidth() - 1,
+ $this->_calculateHeight() - 1
+ );
+ $point4 = $this->_rotate($this->_calculateWidth() - 1, 0);
+
+ $this->_addPolygon(array(
+ $point1,
+ $point2,
+ $point3,
+ $point4
+ ), $this->_backgroundColor);
+
+ $xpos += $this->getQuietZone();
+ $barLength = $this->_barHeight * $this->_factor;
+
+ foreach ($barcodeTable as $bar) {
+ $width = $bar[1] * $this->_factor;
+ if ($bar[0]) {
+ $point1 = $this->_rotate($xpos, $ypos + $bar[2] * $barLength);
+ $point2 = $this->_rotate($xpos, $ypos + $bar[3] * $barLength);
+ $point3 = $this->_rotate(
+ $xpos + $width - 1,
+ $ypos + $bar[3] * $barLength
+ );
+ $point4 = $this->_rotate(
+ $xpos + $width - 1,
+ $ypos + $bar[2] * $barLength
+ );
+ $this->_addPolygon(array(
+ $point1,
+ $point2,
+ $point3,
+ $point4,
+ ));
+ }
+ $xpos += $width;
+ }
+
+ $this->_postDrawBarcode();
+ }
+
+ /**
+ * Partial function to draw border
+ * @return void
+ */
+ protected function _drawBorder()
+ {
+ if ($this->_withBorder) {
+ $point1 = $this->_rotate(0, 0);
+ $point2 = $this->_rotate($this->_calculateWidth() - 1, 0);
+ $point3 = $this->_rotate(
+ $this->_calculateWidth() - 1,
+ $this->_calculateHeight() - 1
+ );
+ $point4 = $this->_rotate(0, $this->_calculateHeight() - 1);
+ $this->_addPolygon(array(
+ $point1,
+ $point2,
+ $point3,
+ $point4,
+ $point1,
+ ), $this->_foreColor, false);
+ }
+ }
+
+ /**
+ * Partial function to draw text
+ * @return void
+ */
+ protected function _drawText()
+ {
+ if ($this->_drawText) {
+ $text = $this->getTextToDisplay();
+ if ($this->_stretchText) {
+ $textLength = strlen($text);
+ $space = ($this->_calculateWidth() - 2 * $this->getQuietZone()) / $textLength;
+ for ($i = 0; $i < $textLength; $i ++) {
+ $leftPosition = $this->getQuietZone() + $space * ($i + 0.5);
+ $this->_addText(
+ $text{$i},
+ $this->_fontSize * $this->_factor,
+ $this->_rotate(
+ $leftPosition,
+ (int) $this->_withBorder * 2
+ + $this->_factor * ($this->_barHeight + $this->_fontSize) + 1
+ ),
+ $this->_font,
+ $this->_foreColor,
+ 'center',
+ - $this->_orientation
+ );
+ }
+ } else {
+ $this->_addText(
+ $text,
+ $this->_fontSize * $this->_factor,
+ $this->_rotate(
+ $this->_calculateWidth() / 2,
+ (int) $this->_withBorder * 2
+ + $this->_factor * ($this->_barHeight + $this->_fontSize) + 1
+ ),
+ $this->_font,
+ $this->_foreColor,
+ 'center',
+ - $this->_orientation
+ );
+ }
+ }
+ }
+
+ /**
+ * Check for invalid characters
+ * @param string $value Text to be ckecked
+ * @return void
+ */
+ public function validateText($value)
+ {
+ $this->_validateText($value);
+ }
+
+ /**
+ * Standard validation for most of barcode objects
+ * @param string $value
+ * @param array $options
+ */
+ protected function _validateText($value, $options = array())
+ {
+ $validatorName = (isset($options['validator'])) ? $options['validator'] : $this->getType();
+
+ $validator = new Zend_Validate_Barcode(array(
+ 'adapter' => $validatorName,
+ 'checksum' => false,
+ ));
+
+ $checksumCharacter = '';
+ $withChecksum = false;
+ if ($this->_mandatoryChecksum) {
+ $checksumCharacter = $this->_substituteChecksumCharacter;
+ $withChecksum = true;
+ }
+
+ $value = $this->_addLeadingZeros($value, $withChecksum) . $checksumCharacter;
+
+ if (!$validator->isValid($value)) {
+ $message = implode("\n", $validator->getMessages());
+
+ /**
+ * @see Zend_Barcode_Object_Exception
+ */
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception($message);
+ }
+ }
+
+ /**
+ * Each child must prepare the barcode and return
+ * a table like array(
+ * 0 => array(
+ * 0 => int (visible(black) or not(white))
+ * 1 => int (width of the bar)
+ * 2 => float (0->1 position from the top of the beginning of the bar in %)
+ * 3 => float (0->1 position from the top of the end of the bar in %)
+ * ),
+ * 1 => ...
+ * )
+ *
+ * @return array
+ */
+ abstract protected function _prepareBarcode();
+
+ /**
+ * Checking of parameters after all settings
+ *
+ * @return void
+ */
+ abstract protected function _checkParams();
+
+ /**
+ * Allow each child to draw something else
+ *
+ * @return void
+ */
+ protected function _preDrawBarcode()
+ {
+ }
+
+ /**
+ * Allow each child to draw something else
+ * (ex: bearer bars in interleaved 2 of 5 code)
+ *
+ * @return void
+ */
+ protected function _postDrawBarcode()
+ {
+ }
+}
diff --git a/library/Zend/Barcode/Object/Planet.php b/library/Zend/Barcode/Object/Planet.php
new file mode 100644
index 0000000..20952e5
--- /dev/null
+++ b/library/Zend/Barcode/Object/Planet.php
@@ -0,0 +1,62 @@
+ "00111",
+ 1 => "11100",
+ 2 => "11010",
+ 3 => "11001",
+ 4 => "10110",
+ 5 => "10101",
+ 6 => "10011",
+ 7 => "01110",
+ 8 => "01101",
+ 9 => "01011"
+ );
+}
\ No newline at end of file
diff --git a/library/Zend/Barcode/Object/Postnet.php b/library/Zend/Barcode/Object/Postnet.php
new file mode 100644
index 0000000..5613dc6
--- /dev/null
+++ b/library/Zend/Barcode/Object/Postnet.php
@@ -0,0 +1,136 @@
+ "11000",
+ 1 => "00011",
+ 2 => "00101",
+ 3 => "00110",
+ 4 => "01001",
+ 5 => "01010",
+ 6 => "01100",
+ 7 => "10001",
+ 8 => "10010",
+ 9 => "10100"
+ );
+
+ /**
+ * Default options for Postnet barcode
+ * @return void
+ */
+ protected function _getDefaultOptions()
+ {
+ $this->_barThinWidth = 2;
+ $this->_barHeight = 20;
+ $this->_drawText = false;
+ $this->_stretchText = true;
+ $this->_mandatoryChecksum = true;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (2 * $this->_barThinWidth) * $this->_factor;
+ $stopCharacter = (1 * $this->_barThinWidth) * $this->_factor;
+ $encodedData = (10 * $this->_barThinWidth) * $this->_factor * strlen($this->getText());
+ return $quietZone + $startCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Partial check of interleaved Postnet barcode
+ * @return void
+ */
+ protected function _checkParams()
+ {}
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+
+ // Start character (1)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+
+ // Text to encode
+ $textTable = str_split($this->getText());
+ foreach ($textTable as $char) {
+ $bars = str_split($this->_codingMap[$char]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0.5 - $b * 0.5 , 1);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Stop character (1)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ return $barcodeTable;
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $this->_checkText($text);
+ $sum = array_sum(str_split($text));
+ $checksum = (10 - ($sum % 10)) % 10;
+ return $checksum;
+ }
+}
diff --git a/library/Zend/Barcode/Object/Royalmail.php b/library/Zend/Barcode/Object/Royalmail.php
new file mode 100644
index 0000000..534a1d1
--- /dev/null
+++ b/library/Zend/Barcode/Object/Royalmail.php
@@ -0,0 +1,163 @@
+ '3300', '1' => '3210', '2' => '3201', '3' => '2310', '4' => '2301', '5' => '2211',
+ '6' => '3120', '7' => '3030', '8' => '3021', '9' => '2130', 'A' => '2121', 'B' => '2031',
+ 'C' => '3102', 'D' => '3012', 'E' => '3003', 'F' => '2112', 'G' => '2103', 'H' => '2013',
+ 'I' => '1320', 'J' => '1230', 'K' => '1221', 'L' => '0330', 'M' => '0321', 'N' => '0231',
+ 'O' => '1302', 'P' => '1212', 'Q' => '1203', 'R' => '0312', 'S' => '0303', 'T' => '0213',
+ 'U' => '1122', 'V' => '1032', 'W' => '1023', 'X' => '0132', 'Y' => '0123', 'Z' => '0033'
+ );
+
+ protected $_rows = array(
+ '0' => 1, '1' => 1, '2' => 1, '3' => 1, '4' => 1, '5' => 1,
+ '6' => 2, '7' => 2, '8' => 2, '9' => 2, 'A' => 2, 'B' => 2,
+ 'C' => 3, 'D' => 3, 'E' => 3, 'F' => 3, 'G' => 3, 'H' => 3,
+ 'I' => 4, 'J' => 4, 'K' => 4, 'L' => 4, 'M' => 4, 'N' => 4,
+ 'O' => 5, 'P' => 5, 'Q' => 5, 'R' => 5, 'S' => 5, 'T' => 5,
+ 'U' => 0, 'V' => 0, 'W' => 0, 'X' => 0, 'Y' => 0, 'Z' => 0,
+ );
+
+ protected $_columns = array(
+ '0' => 1, '1' => 2, '2' => 3, '3' => 4, '4' => 5, '5' => 0,
+ '6' => 1, '7' => 2, '8' => 3, '9' => 4, 'A' => 5, 'B' => 0,
+ 'C' => 1, 'D' => 2, 'E' => 3, 'F' => 4, 'G' => 5, 'H' => 0,
+ 'I' => 1, 'J' => 2, 'K' => 3, 'L' => 4, 'M' => 5, 'N' => 0,
+ 'O' => 1, 'P' => 2, 'Q' => 3, 'R' => 4, 'S' => 5, 'T' => 0,
+ 'U' => 1, 'V' => 2, 'W' => 3, 'X' => 4, 'Y' => 5, 'Z' => 0,
+ );
+
+ /**
+ * Default options for Postnet barcode
+ * @return void
+ */
+ protected function _getDefaultOptions()
+ {
+ $this->_barThinWidth = 2;
+ $this->_barHeight = 20;
+ $this->_drawText = false;
+ $this->_stretchText = true;
+ $this->_mandatoryChecksum = true;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (2 * $this->_barThinWidth) * $this->_factor;
+ $stopCharacter = (1 * $this->_barThinWidth) * $this->_factor;
+ $encodedData = (8 * $this->_barThinWidth) * $this->_factor * strlen($this->getText());
+ return $quietZone + $startCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Partial check of interleaved Postnet barcode
+ * @return void
+ */
+ protected function _checkParams()
+ {}
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+
+ // Start character (1)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 5/8);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+
+ // Text to encode
+ $textTable = str_split($this->getText());
+ foreach ($textTable as $char) {
+ $bars = str_split($this->_codingMap[$char]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array(1 , $this->_barThinWidth , ($b > 1 ? 3/8 : 0) , ($b % 2 ? 5/8 : 1));
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Stop character (1)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , 1);
+ return $barcodeTable;
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $this->_checkText($text);
+ $values = str_split($text);
+ $rowvalue = 0;
+ $colvalue = 0;
+ foreach($values as $row) {
+ $rowvalue += $this->_rows[$row];
+ $colvalue += $this->_columns[$row];
+ }
+
+ $rowvalue %= 6;
+ $colvalue %= 6;
+
+ $rowchkvalue = array_keys($this->_rows, $rowvalue);
+ $colchkvalue = array_keys($this->_columns, $colvalue);
+ return current(array_intersect($rowchkvalue, $colchkvalue));
+ }
+}
diff --git a/library/Zend/Barcode/Object/Upca.php b/library/Zend/Barcode/Object/Upca.php
new file mode 100644
index 0000000..9f82144
--- /dev/null
+++ b/library/Zend/Barcode/Object/Upca.php
@@ -0,0 +1,172 @@
+_barcodeLength = 12;
+ $this->_mandatoryChecksum = true;
+ $this->_mandatoryQuietZones = true;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (3 * $this->_barThinWidth) * $this->_factor;
+ $middleCharacter = (5 * $this->_barThinWidth) * $this->_factor;
+ $stopCharacter = (3 * $this->_barThinWidth) * $this->_factor;
+ $encodedData = (7 * $this->_barThinWidth) * $this->_factor * 12;
+ return $quietZone + $startCharacter + $middleCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+ $height = ($this->_drawText) ? 1.1 : 1;
+
+ // Start character (101)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+
+ $textTable = str_split($this->getText());
+
+ // First character
+ $bars = str_split($this->_codingMap['A'][$textTable[0]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , $height);
+ }
+
+ // First part
+ for ($i = 1; $i < 6; $i++) {
+ $bars = str_split($this->_codingMap['A'][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Middle character (01010)
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+
+ // Second part
+ for ($i = 6; $i < 11; $i++) {
+ $bars = str_split($this->_codingMap['C'][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Last character
+ $bars = str_split($this->_codingMap['C'][$textTable[11]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , $height);
+ }
+
+ // Stop character (101)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ return $barcodeTable;
+ }
+
+ /**
+ * Partial function to draw text
+ * @return void
+ */
+ protected function _drawText()
+ {
+ if ($this->_drawText) {
+ $text = $this->getTextToDisplay();
+ $characterWidth = (7 * $this->_barThinWidth) * $this->_factor;
+ $leftPosition = $this->getQuietZone() - $characterWidth;
+ for ($i = 0; $i < $this->_barcodeLength; $i ++) {
+ $fontSize = $this->_fontSize;
+ if ($i == 0 || $i == 11) {
+ $fontSize *= 0.8;
+ }
+ $this->_addText(
+ $text{$i},
+ $fontSize * $this->_factor,
+ $this->_rotate(
+ $leftPosition,
+ (int) $this->_withBorder * 2
+ + $this->_factor * ($this->_barHeight + $fontSize) + 1
+ ),
+ $this->_font,
+ $this->_foreColor,
+ 'left',
+ - $this->_orientation
+ );
+ switch ($i) {
+ case 0:
+ $factor = 10;
+ break;
+ case 5:
+ $factor = 4;
+ break;
+ case 10:
+ $factor = 11;
+ break;
+ default:
+ $factor = 0;
+ }
+ $leftPosition = $leftPosition + $characterWidth + ($factor * $this->_barThinWidth * $this->_factor);
+ }
+ }
+ }
+}
diff --git a/library/Zend/Barcode/Object/Upce.php b/library/Zend/Barcode/Object/Upce.php
new file mode 100644
index 0000000..59f799f
--- /dev/null
+++ b/library/Zend/Barcode/Object/Upce.php
@@ -0,0 +1,228 @@
+ array(
+ 0 => array('B','B','B','A','A','A'),
+ 1 => array('B','B','A','B','A','A'),
+ 2 => array('B','B','A','A','B','A'),
+ 3 => array('B','B','A','A','A','B'),
+ 4 => array('B','A','B','B','A','A'),
+ 5 => array('B','A','A','B','B','A'),
+ 6 => array('B','A','A','A','B','B'),
+ 7 => array('B','A','B','A','B','A'),
+ 8 => array('B','A','B','A','A','B'),
+ 9 => array('B','A','A','B','A','B')),
+ 1 => array(
+ 0 => array('A','A','A','B','B','B'),
+ 1 => array('A','A','B','A','B','B'),
+ 2 => array('A','A','B','B','A','B'),
+ 3 => array('A','A','B','B','B','A'),
+ 4 => array('A','B','A','A','B','B'),
+ 5 => array('A','B','B','A','A','B'),
+ 6 => array('A','B','B','B','A','A'),
+ 7 => array('A','B','A','B','A','B'),
+ 8 => array('A','B','A','B','B','A'),
+ 9 => array('A','B','B','A','B','A'))
+ );
+
+ /**
+ * Default options for Postnet barcode
+ * @return void
+ */
+ protected function _getDefaultOptions()
+ {
+ $this->_barcodeLength = 8;
+ $this->_mandatoryChecksum = true;
+ $this->_mandatoryQuietZones = true;
+ }
+
+ /**
+ * Retrieve text to encode
+ * @return string
+ */
+ public function getText()
+ {
+ $text = parent::getText();
+ if ($text{0} != 1) {
+ $text{0} = 0;
+ }
+ return $text;
+ }
+
+ /**
+ * Width of the barcode (in pixels)
+ * @return integer
+ */
+ protected function _calculateBarcodeWidth()
+ {
+ $quietZone = $this->getQuietZone();
+ $startCharacter = (3 * $this->_barThinWidth) * $this->_factor;
+ $stopCharacter = (6 * $this->_barThinWidth) * $this->_factor;
+ $encodedData = (7 * $this->_barThinWidth) * $this->_factor * 6;
+ return $quietZone + $startCharacter + $encodedData + $stopCharacter + $quietZone;
+ }
+
+ /**
+ * Prepare array to draw barcode
+ * @return array
+ */
+ protected function _prepareBarcode()
+ {
+ $barcodeTable = array();
+ $height = ($this->_drawText) ? 1.1 : 1;
+
+ // Start character (101)
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+
+ $textTable = str_split($this->getText());
+ $system = 0;
+ if ($textTable[0] == 1) {
+ $system = 1;
+ }
+ $checksum = $textTable[7];
+ $parity = $this->_parities[$system][$checksum];
+
+ for ($i = 1; $i < 7; $i++) {
+ $bars = str_split($this->_codingMap[$parity[$i - 1]][$textTable[$i]]);
+ foreach ($bars as $b) {
+ $barcodeTable[] = array($b , $this->_barThinWidth , 0 , 1);
+ }
+ }
+
+ // Stop character (10101)
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(0 , $this->_barThinWidth , 0 , $height);
+ $barcodeTable[] = array(1 , $this->_barThinWidth , 0 , $height);
+ return $barcodeTable;
+ }
+
+ /**
+ * Partial function to draw text
+ * @return void
+ */
+ protected function _drawText()
+ {
+ if ($this->_drawText) {
+ $text = $this->getTextToDisplay();
+ $characterWidth = (7 * $this->_barThinWidth) * $this->_factor;
+ $leftPosition = $this->getQuietZone() - $characterWidth;
+ for ($i = 0; $i < $this->_barcodeLength; $i ++) {
+ $fontSize = $this->_fontSize;
+ if ($i == 0 || $i == 7) {
+ $fontSize *= 0.8;
+ }
+ $this->_addText(
+ $text{$i},
+ $fontSize * $this->_factor,
+ $this->_rotate(
+ $leftPosition,
+ (int) $this->_withBorder * 2
+ + $this->_factor * ($this->_barHeight + $fontSize) + 1
+ ),
+ $this->_font,
+ $this->_foreColor,
+ 'left',
+ - $this->_orientation
+ );
+ switch ($i) {
+ case 0:
+ $factor = 3;
+ break;
+ case 6:
+ $factor = 5;
+ break;
+ default:
+ $factor = 0;
+ }
+ $leftPosition = $leftPosition + $characterWidth + ($factor * $this->_barThinWidth * $this->_factor);
+ }
+ }
+ }
+
+ /**
+ * Particular validation for Upce barcode objects
+ * (to suppress checksum character substitution)
+ * @param string $value
+ * @param array $options
+ */
+ protected function _validateText($value, $options = array())
+ {
+ $validator = new Zend_Validate_Barcode(array(
+ 'adapter' => 'upce',
+ 'checksum' => false,
+ ));
+
+ $value = $this->_addLeadingZeros($value, true);
+
+ if (!$validator->isValid($value)) {
+ $message = implode("\n", $validator->getMessages());
+
+ /**
+ * @see Zend_Barcode_Object_Exception
+ */
+ require_once 'Zend/Barcode/Object/Exception.php';
+ throw new Zend_Barcode_Object_Exception($message);
+ }
+ }
+
+ /**
+ * Get barcode checksum
+ *
+ * @param string $text
+ * @return int
+ */
+ public function getChecksum($text)
+ {
+ $text = $this->_addLeadingZeros($text, true);
+ if ($text{0} != 1) {
+ $text{0} = 0;
+ }
+ return parent::getChecksum($text);
+ }
+}
diff --git a/library/Zend/Barcode/Renderer/Exception.php b/library/Zend/Barcode/Renderer/Exception.php
new file mode 100644
index 0000000..d042351
--- /dev/null
+++ b/library/Zend/Barcode/Renderer/Exception.php
@@ -0,0 +1,35 @@
+_userHeight = intval($value);
+ return $this;
+ }
+
+ /**
+ * Get barcode height
+ *
+ * @return int
+ */
+ public function getHeight()
+ {
+ return $this->_userHeight;
+ }
+
+ /**
+ * Set barcode width
+ *
+ * @param mixed $value
+ * @return void
+ */
+ public function setWidth($value)
+ {
+ if (!is_numeric($value) || intval($value) < 0) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Image width must be greater than or equals 0'
+ );
+ }
+ $this->_userWidth = intval($value);
+ return $this;
+ }
+
+ /**
+ * Get barcode width
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ return $this->_userWidth;
+ }
+
+ /**
+ * Set an image resource to draw the barcode inside
+ *
+ * @param resource $value
+ * @return Zend_Barcode_Renderer
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setResource($image)
+ {
+ if (gettype($image) != 'resource' || get_resource_type($image) != 'gd') {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Invalid image resource provided to setResource()'
+ );
+ }
+ $this->_resource = $image;
+ return $this;
+ }
+
+ /**
+ * Set the image type to produce (png, jpeg, gif)
+ *
+ * @param string $value
+ * @return Zend_Barcode_RendererAbstract
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setImageType($value)
+ {
+ if ($value == 'jpg') {
+ $value = 'jpeg';
+ }
+
+ if (!in_array($value, $this->_allowedImageType)) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(sprintf(
+ 'Invalid type "%s" provided to setImageType()',
+ $value
+ ));
+ }
+
+ $this->_imageType = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve the image type to produce
+ *
+ * @return string
+ */
+ public function getImageType()
+ {
+ return $this->_imageType;
+ }
+
+ /**
+ * Initialize the image resource
+ *
+ * @return void
+ */
+ protected function _initRenderer()
+ {
+ if (!extension_loaded('gd')) {
+ require_once 'Zend/Barcode/Exception.php';
+ $e = new Zend_Barcode_Exception(
+ 'Gd extension must be loaded to render barcode as image'
+ );
+ $e->setIsRenderable(false);
+ throw $e;
+ }
+
+ $barcodeWidth = $this->_barcode->getWidth(true);
+ $barcodeHeight = $this->_barcode->getHeight(true);
+
+ if ($this->_resource !== null) {
+ $foreColor = $this->_barcode->getForeColor();
+ $backgroundColor = $this->_barcode->getBackgroundColor();
+ $this->_imageBackgroundColor = imagecolorallocate(
+ $this->_resource,
+ ($backgroundColor & 0xFF0000) >> 16,
+ ($backgroundColor & 0x00FF00) >> 8,
+ $backgroundColor & 0x0000FF
+ );
+ $this->_imageForeColor = imagecolorallocate(
+ $this->_resource,
+ ($foreColor & 0xFF0000) >> 16,
+ ($foreColor & 0x00FF00) >> 8,
+ $foreColor & 0x0000FF
+ );
+ } else {
+ $width = $barcodeWidth;
+ $height = $barcodeHeight;
+ if ($this->_userWidth && $this->_barcode->getType() != 'error') {
+ $width = $this->_userWidth;
+ }
+ if ($this->_userHeight && $this->_barcode->getType() != 'error') {
+ $height = $this->_userHeight;
+ }
+
+ $foreColor = $this->_barcode->getForeColor();
+ $backgroundColor = $this->_barcode->getBackgroundColor();
+ $this->_resource = imagecreatetruecolor($width, $height);
+
+ $this->_imageBackgroundColor = imagecolorallocate(
+ $this->_resource,
+ ($backgroundColor & 0xFF0000) >> 16,
+ ($backgroundColor & 0x00FF00) >> 8,
+ $backgroundColor & 0x0000FF
+ );
+ $this->_imageForeColor = imagecolorallocate(
+ $this->_resource,
+ ($foreColor & 0xFF0000) >> 16,
+ ($foreColor & 0x00FF00) >> 8,
+ $foreColor & 0x0000FF
+ );
+ $white = imagecolorallocate($this->_resource, 255, 255, 255);
+ imagefilledrectangle($this->_resource, 0, 0, $width - 1, $height - 1, $white);
+ }
+ $this->_adjustPosition(imagesy($this->_resource), imagesx($this->_resource));
+ imagefilledrectangle(
+ $this->_resource,
+ $this->_leftOffset,
+ $this->_topOffset,
+ $this->_leftOffset + $barcodeWidth - 1,
+ $this->_topOffset + $barcodeHeight - 1,
+ $this->_imageBackgroundColor
+ );
+ }
+
+ /**
+ * Check barcode parameters
+ *
+ * @return void
+ */
+ protected function _checkParams()
+ {
+ $this->_checkDimensions();
+ }
+
+ /**
+ * Check barcode dimensions
+ *
+ * @return void
+ */
+ protected function _checkDimensions()
+ {
+ if ($this->_resource !== null) {
+ if (imagesy($this->_resource) < $this->_barcode->getHeight(true)) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Barcode is define outside the image (height)'
+ );
+ }
+ } else {
+ if ($this->_userHeight) {
+ $height = $this->_barcode->getHeight(true);
+ if ($this->_userHeight < $height) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(sprintf(
+ "Barcode is define outside the image (calculated: '%d', provided: '%d')",
+ $height,
+ $this->_userHeight
+ ));
+ }
+ }
+ }
+ if ($this->_resource !== null) {
+ if (imagesx($this->_resource) < $this->_barcode->getWidth(true)) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Barcode is define outside the image (width)'
+ );
+ }
+ } else {
+ if ($this->_userWidth) {
+ $width = $this->_barcode->getWidth(true);
+ if ($this->_userWidth < $width) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(sprintf(
+ "Barcode is define outside the image (calculated: '%d', provided: '%d')",
+ $width,
+ $this->_userWidth
+ ));
+ }
+ }
+ }
+ }
+
+ /**
+ * Draw and render the barcode with correct headers
+ *
+ * @return mixed
+ */
+ public function render()
+ {
+ $this->draw();
+ header("Content-Type: image/" . $this->_imageType);
+ $functionName = 'image' . $this->_imageType;
+ call_user_func($functionName, $this->_resource);
+ @imagedestroy($this->_resource);
+ }
+
+ /**
+ * Draw a polygon in the image resource
+ *
+ * @param array $points
+ * @param integer $color
+ * @param boolean $filled
+ */
+ protected function _drawPolygon($points, $color, $filled = true)
+ {
+ $newPoints = array(
+ $points[0][0] + $this->_leftOffset,
+ $points[0][1] + $this->_topOffset,
+ $points[1][0] + $this->_leftOffset,
+ $points[1][1] + $this->_topOffset,
+ $points[2][0] + $this->_leftOffset,
+ $points[2][1] + $this->_topOffset,
+ $points[3][0] + $this->_leftOffset,
+ $points[3][1] + $this->_topOffset,
+ );
+
+ $allocatedColor = imagecolorallocate(
+ $this->_resource,
+ ($color & 0xFF0000) >> 16,
+ ($color & 0x00FF00) >> 8,
+ $color & 0x0000FF
+ );
+
+ if ($filled) {
+ imagefilledpolygon($this->_resource, $newPoints, 4, $allocatedColor);
+ } else {
+ imagepolygon($this->_resource, $newPoints, 4, $allocatedColor);
+ }
+ }
+
+ /**
+ * Draw a polygon in the image resource
+ *
+ * @param string $text
+ * @param float $size
+ * @param array $position
+ * @param string $font
+ * @param integer $color
+ * @param string $alignment
+ * @param float $orientation
+ */
+ protected function _drawText($text, $size, $position, $font, $color, $alignment = 'center', $orientation = 0)
+ {
+ $allocatedColor = imagecolorallocate(
+ $this->_resource,
+ ($color & 0xFF0000) >> 16,
+ ($color & 0x00FF00) >> 8,
+ $color & 0x0000FF
+ );
+
+ if ($font == null) {
+ $font = 3;
+ }
+ $position[0] += $this->_leftOffset;
+ $position[1] += $this->_topOffset;
+
+ if (is_numeric($font)) {
+ if ($orientation) {
+ /**
+ * imagestring() doesn't allow orientation, if orientation
+ * needed: a TTF font is required.
+ * Throwing an exception here, allow to use automaticRenderError
+ * to informe user of the problem instead of simply not drawing
+ * the text
+ */
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'No orientation possible with GD internal font'
+ );
+ }
+ $fontWidth = imagefontwidth($font);
+ $positionY = $position[1] - imagefontheight($font) + 1;
+ switch ($alignment) {
+ case 'left':
+ $positionX = $position[0];
+ break;
+ case 'center':
+ $positionX = $position[0] - ceil(($fontWidth * strlen($text)) / 2);
+ break;
+ case 'right':
+ $positionX = $position[0] - ($fontWidth * strlen($text));
+ break;
+ }
+ imagestring($this->_resource, $font, $positionX, $positionY, $text, $color);
+ } else {
+
+ if (!function_exists('imagettfbbox')) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'A font was provided, but this instance of PHP does not have TTF (FreeType) support'
+ );
+ }
+
+ $box = imagettfbbox($size, 0, $font, $text);
+ switch ($alignment) {
+ case 'left':
+ $width = 0;
+ break;
+ case 'center':
+ $width = ($box[2] - $box[0]) / 2;
+ break;
+ case 'right':
+ $width = ($box[2] - $box[0]);
+ break;
+ }
+ imagettftext(
+ $this->_resource,
+ $size,
+ $orientation,
+ $position[0] - ($width * cos(pi() * $orientation / 180)),
+ $position[1] + ($width * sin(pi() * $orientation / 180)),
+ $allocatedColor,
+ $font,
+ $text
+ );
+ }
+ }
+}
diff --git a/library/Zend/Barcode/Renderer/Pdf.php b/library/Zend/Barcode/Renderer/Pdf.php
new file mode 100644
index 0000000..89bd9be
--- /dev/null
+++ b/library/Zend/Barcode/Renderer/Pdf.php
@@ -0,0 +1,242 @@
+_resource = $pdf;
+ $this->_page = intval($page);
+
+ if (!count($this->_resource->pages)) {
+ $this->_page = 0;
+ $this->_resource->pages[] = new Zend_Pdf_Page(
+ Zend_Pdf_Page::SIZE_A4
+ );
+ }
+ return $this;
+ }
+
+ /**
+ * Check renderer parameters
+ *
+ * @return void
+ */
+ protected function _checkParams()
+ {
+ }
+
+ /**
+ * Draw the barcode in the PDF, send headers and the PDF
+ * @return mixed
+ */
+ public function render()
+ {
+ $this->draw();
+ header("Content-Type: application/pdf");
+ echo $this->_resource->render();
+ }
+
+ /**
+ * Initialize the PDF resource
+ * @return void
+ */
+ protected function _initRenderer()
+ {
+ if ($this->_resource === null) {
+ $this->_resource = new Zend_Pdf();
+ $this->_resource->pages[] = new Zend_Pdf_Page(
+ Zend_Pdf_Page::SIZE_A4
+ );
+ }
+
+ $pdfPage = $this->_resource->pages[$this->_page];
+ $this->_adjustPosition($pdfPage->getHeight(), $pdfPage->getWidth());
+ }
+
+ /**
+ * Draw a polygon in the rendering resource
+ * @param array $points
+ * @param integer $color
+ * @param boolean $filled
+ */
+ protected function _drawPolygon($points, $color, $filled = true)
+ {
+ $page = $this->_resource->pages[$this->_page];
+ foreach ($points as $point) {
+ $x[] = $point[0] * $this->_moduleSize + $this->_leftOffset;
+ $y[] = $page->getHeight() - $point[1] * $this->_moduleSize - $this->_topOffset;
+ }
+ if (count($y) == 4) {
+ if ($x[0] != $x[3] && $y[0] == $y[3]) {
+ $y[0] -= ($this->_moduleSize / 2);
+ $y[3] -= ($this->_moduleSize / 2);
+ }
+ if ($x[1] != $x[2] && $y[1] == $y[2]) {
+ $y[1] += ($this->_moduleSize / 2);
+ $y[2] += ($this->_moduleSize / 2);
+ }
+ }
+
+ $color = new Zend_Pdf_Color_Rgb(
+ (($color & 0xFF0000) >> 16) / 255.0,
+ (($color & 0x00FF00) >> 8) / 255.0,
+ ($color & 0x0000FF) / 255.0
+ );
+
+ $page->setLineColor($color);
+ $page->setFillColor($color);
+ $page->setLineWidth($this->_moduleSize);
+
+ $fillType = ($filled)
+ ? Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE
+ : Zend_Pdf_Page::SHAPE_DRAW_STROKE;
+
+ $page->drawPolygon($x, $y, $fillType);
+ }
+
+ /**
+ * Draw a text in the rendering resource
+ * @param string $text
+ * @param float $size
+ * @param array $position
+ * @param string $font
+ * @param integer $color
+ * @param string $alignment
+ * @param float $orientation
+ */
+ protected function _drawText(
+ $text,
+ $size,
+ $position,
+ $font,
+ $color,
+ $alignment = 'center',
+ $orientation = 0
+ ) {
+ $page = $this->_resource->pages[$this->_page];
+ $color = new Zend_Pdf_Color_Rgb(
+ (($color & 0xFF0000) >> 16) / 255.0,
+ (($color & 0x00FF00) >> 8) / 255.0,
+ ($color & 0x0000FF) / 255.0
+ );
+
+ $page->setLineColor($color);
+ $page->setFillColor($color);
+ $page->setFont(Zend_Pdf_Font::fontWithPath($font), $size * $this->_moduleSize * 1.2);
+
+ $width = $this->widthForStringUsingFontSize(
+ $text,
+ Zend_Pdf_Font::fontWithPath($font),
+ $size * $this->_moduleSize
+ );
+
+ $angle = pi() * $orientation / 180;
+ $left = $position[0] * $this->_moduleSize + $this->_leftOffset;
+ $top = $page->getHeight() - $position[1] * $this->_moduleSize - $this->_topOffset;
+
+ switch ($alignment) {
+ case 'center':
+ $left -= ($width / 2) * cos($angle);
+ $top -= ($width / 2) * sin($angle);
+ break;
+ case 'right':
+ $left -= $width;
+ break;
+ }
+ $page->rotate($left, $top, $angle);
+ $page->drawText($text, $left, $top);
+ $page->rotate($left, $top, - $angle);
+ }
+
+ /**
+ * Calculate the width of a string:
+ * in case of using alignment parameter in drawText
+ * @param string $text
+ * @param Zend_Pdf_Font $font
+ * @param float $fontSize
+ * @return float
+ */
+ public function widthForStringUsingFontSize($text, $font, $fontSize)
+ {
+ $drawingString = iconv('UTF-8', 'UTF-16BE//IGNORE', $text);
+ $characters = array();
+ for ($i = 0; $i < strlen($drawingString); $i ++) {
+ $characters[] = (ord($drawingString[$i ++]) << 8) | ord($drawingString[$i]);
+ }
+ $glyphs = $font->glyphNumbersForCharacters($characters);
+ $widths = $font->widthsForGlyphs($glyphs);
+ $stringWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
+ return $stringWidth;
+ }
+}
diff --git a/library/Zend/Barcode/Renderer/RendererAbstract.php b/library/Zend/Barcode/Renderer/RendererAbstract.php
new file mode 100644
index 0000000..b7ef94a
--- /dev/null
+++ b/library/Zend/Barcode/Renderer/RendererAbstract.php
@@ -0,0 +1,540 @@
+toArray();
+ }
+ if (is_array($options)) {
+ $this->setOptions($options);
+ }
+ $this->_type = strtolower(substr(
+ get_class($this),
+ strlen($this->_rendererNamespace) + 1
+ ));
+ }
+
+ /**
+ * Set renderer state from options array
+ * @param array $options
+ * @return Zend_Renderer_Object
+ */
+ public function setOptions($options)
+ {
+ foreach ($options as $key => $value) {
+ $method = 'set' . $key;
+ if (method_exists($this, $method)) {
+ $this->$method($value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Set renderer state from config object
+ * @param Zend_Config $config
+ * @return Zend_Renderer_Object
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ return $this->setOptions($config->toArray());
+ }
+
+ /**
+ * Set renderer namespace for autoloading
+ *
+ * @param string $namespace
+ * @return Zend_Renderer_Object
+ */
+ public function setRendererNamespace($namespace)
+ {
+ $this->_rendererNamespace = $namespace;
+ return $this;
+ }
+
+ /**
+ * Retrieve renderer namespace
+ *
+ * @return string
+ */
+ public function getRendererNamespace()
+ {
+ return $this->_rendererNamespace;
+ }
+
+ /**
+ * Retrieve renderer type
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * Manually adjust top position
+ * @param integer $value
+ * @return Zend_Barcode_Renderer
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setTopOffset($value)
+ {
+ if (!is_numeric($value) || intval($value) < 0) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Vertical position must be greater than or equals 0'
+ );
+ }
+ $this->_topOffset = intval($value);
+ return $this;
+ }
+
+ /**
+ * Retrieve vertical adjustment
+ * @return integer
+ */
+ public function getTopOffset()
+ {
+ return $this->_topOffset;
+ }
+
+ /**
+ * Manually adjust left position
+ * @param integer $value
+ * @return Zend_Barcode_Renderer
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setLeftOffset($value)
+ {
+ if (!is_numeric($value) || intval($value) < 0) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Horizontal position must be greater than or equals 0'
+ );
+ }
+ $this->_leftOffset = intval($value);
+ return $this;
+ }
+
+ /**
+ * Retrieve vertical adjustment
+ * @return integer
+ */
+ public function getLeftOffset()
+ {
+ return $this->_leftOffset;
+ }
+
+ /**
+ * Activate/Deactivate the automatic rendering of exception
+ * @param boolean $value
+ */
+ public function setAutomaticRenderError($value)
+ {
+ $this->_automaticRenderError = (bool) $value;
+ return $this;
+ }
+
+ /**
+ * Horizontal position of the barcode in the rendering resource
+ * @param string $value
+ * @return Zend_Barcode_Renderer
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setHorizontalPosition($value)
+ {
+ if (!in_array($value, array('left' , 'center' , 'right'))) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ "Invalid barcode position provided must be 'left', 'center' or 'right'"
+ );
+ }
+ $this->_horizontalPosition = $value;
+ return $this;
+ }
+
+ /**
+ * Horizontal position of the barcode in the rendering resource
+ * @return string
+ */
+ public function getHorizontalPosition()
+ {
+ return $this->_horizontalPosition;
+ }
+
+ /**
+ * Vertical position of the barcode in the rendering resource
+ * @param string $value
+ * @return Zend_Barcode_Renderer
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setVerticalPosition($value)
+ {
+ if (!in_array($value, array('top' , 'middle' , 'bottom'))) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ "Invalid barcode position provided must be 'top', 'middle' or 'bottom'"
+ );
+ }
+ $this->_verticalPosition = $value;
+ return $this;
+ }
+
+ /**
+ * Vertical position of the barcode in the rendering resource
+ * @return string
+ */
+ public function getVerticalPosition()
+ {
+ return $this->_verticalPosition;
+ }
+
+ /**
+ * Set the size of a module
+ * @param float $value
+ * @return Zend_Barcode_Renderer
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setModuleSize($value)
+ {
+ if (!is_numeric($value) || floatval($value) <= 0) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Float size must be greater than 0'
+ );
+ }
+ $this->_moduleSize = floatval($value);
+ return $this;
+ }
+
+
+ /**
+ * Set the size of a module
+ * @return float
+ */
+ public function getModuleSize()
+ {
+ return $this->_moduleSize;
+ }
+
+ /**
+ * Retrieve the automatic rendering of exception
+ * @return boolean
+ */
+ public function getAutomaticRenderError()
+ {
+ return $this->_automaticRenderError;
+ }
+
+ /**
+ * Set the barcode object
+ * @param Zend_Barcode_Object $barcode
+ * @return Zend_Barcode_Renderer
+ */
+ public function setBarcode($barcode)
+ {
+ if (!$barcode instanceof Zend_Barcode_Object_ObjectAbstract) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Invalid barcode object provided to setBarcode()'
+ );
+ }
+ $this->_barcode = $barcode;
+ return $this;
+ }
+
+ /**
+ * Retrieve the barcode object
+ * @return Zend_Barcode_Object
+ */
+ public function getBarcode()
+ {
+ return $this->_barcode;
+ }
+
+ /**
+ * Checking of parameters after all settings
+ * @return boolean
+ */
+ public function checkParams()
+ {
+ $this->_checkBarcodeObject();
+ $this->_checkParams();
+ return true;
+ }
+
+ /**
+ * Check if a barcode object is correctly provided
+ * @return void
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ protected function _checkBarcodeObject()
+ {
+ if ($this->_barcode === null) {
+ /**
+ * @see Zend_Barcode_Renderer_Exception
+ */
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'No barcode object provided'
+ );
+ }
+ }
+
+ /**
+ * Calculate the left and top offset of the barcode in the
+ * rendering support
+ *
+ * @param float $supportHeight
+ * @param float $supportWidth
+ * @return void
+ */
+ protected function _adjustPosition($supportHeight, $supportWidth)
+ {
+ $barcodeHeight = $this->_barcode->getHeight(true) * $this->_moduleSize;
+ if ($barcodeHeight != $supportHeight && $this->_topOffset == 0) {
+ switch ($this->_verticalPosition) {
+ case 'middle':
+ $this->_topOffset = floor(
+ ($supportHeight - $barcodeHeight) / 2);
+ break;
+ case 'bottom':
+ $this->_topOffset = $supportHeight - $barcodeHeight;
+ break;
+ case 'top':
+ default:
+ $this->_topOffset = 0;
+ break;
+ }
+ }
+ $barcodeWidth = $this->_barcode->getWidth(true) * $this->_moduleSize;
+ if ($barcodeWidth != $supportWidth && $this->_leftOffset == 0) {
+ switch ($this->_horizontalPosition) {
+ case 'center':
+ $this->_leftOffset = floor(
+ ($supportWidth - $barcodeWidth) / 2);
+ break;
+ case 'right':
+ $this->_leftOffset = $supportWidth - $barcodeWidth;
+ break;
+ case 'left':
+ default:
+ $this->_leftOffset = 0;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Draw the barcode in the rendering resource
+ * @return mixed
+ */
+ public function draw()
+ {
+ try {
+ $this->checkParams();
+ $this->_initRenderer();
+ $this->_drawInstructionList();
+ } catch (Zend_Exception $e) {
+ $renderable = false;
+ if ($e instanceof Zend_Barcode_Exception) {
+ $renderable = $e->isRenderable();
+ }
+ if ($this->_automaticRenderError && $renderable) {
+ $barcode = Zend_Barcode::makeBarcode(
+ 'error',
+ array('text' => $e->getMessage())
+ );
+ $this->setBarcode($barcode);
+ $this->_resource = null;
+ $this->_initRenderer();
+ $this->_drawInstructionList();
+ } else {
+ if ($e instanceof Zend_Barcode_Exception) {
+ $e->setIsRenderable(false);
+ }
+ throw $e;
+ }
+ }
+ return $this->_resource;
+ }
+
+ /**
+ * Sub process to draw the barcode instructions
+ * Needed by the automatic error rendering
+ */
+ private function _drawInstructionList()
+ {
+ $instructionList = $this->_barcode->draw();
+ foreach ($instructionList as $instruction) {
+ switch ($instruction['type']) {
+ case 'polygon':
+ $this->_drawPolygon(
+ $instruction['points'],
+ $instruction['color'],
+ $instruction['filled']
+ );
+ break;
+ case 'text': //$text, $size, $position, $font, $color, $alignment = 'center', $orientation = 0)
+ $this->_drawText(
+ $instruction['text'],
+ $instruction['size'],
+ $instruction['position'],
+ $instruction['font'],
+ $instruction['color'],
+ $instruction['alignment'],
+ $instruction['orientation']
+ );
+ break;
+ default:
+ /**
+ * @see Zend_Barcode_Renderer_Exception
+ */
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Unkown drawing command'
+ );
+ }
+ }
+ }
+
+ /**
+ * Checking of parameters after all settings
+ * @return void
+ */
+ abstract protected function _checkParams();
+
+ /**
+ * Render the resource by sending headers and drawed resource
+ * @return mixed
+ */
+ abstract public function render();
+
+ /**
+ * Initialize the rendering resource
+ * @return void
+ */
+ abstract protected function _initRenderer();
+
+ /**
+ * Draw a polygon in the rendering resource
+ * @param array $points
+ * @param integer $color
+ * @param boolean $filled
+ */
+ abstract protected function _drawPolygon($points, $color, $filled = true);
+
+ /**
+ * Draw a polygon in the rendering resource
+ * @param string $text
+ * @param float $size
+ * @param array $position
+ * @param string $font
+ * @param integer $color
+ * @param string $alignment
+ * @param float $orientation
+ */
+ abstract protected function _drawText(
+ $text,
+ $size,
+ $position,
+ $font,
+ $color,
+ $alignment = 'center',
+ $orientation = 0
+ );
+}
diff --git a/library/Zend/Barcode/Renderer/Svg.php b/library/Zend/Barcode/Renderer/Svg.php
new file mode 100644
index 0000000..301dc84
--- /dev/null
+++ b/library/Zend/Barcode/Renderer/Svg.php
@@ -0,0 +1,382 @@
+_userHeight = intval($value);
+ return $this;
+ }
+
+ /**
+ * Get barcode height
+ *
+ * @return int
+ */
+ public function getHeight()
+ {
+ return $this->_userHeight;
+ }
+
+ /**
+ * Set barcode width
+ *
+ * @param mixed $value
+ * @return void
+ */
+ public function setWidth($value)
+ {
+ if (!is_numeric($value) || intval($value) < 0) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Svg width must be greater than or equals 0'
+ );
+ }
+ $this->_userWidth = intval($value);
+ return $this;
+ }
+
+ /**
+ * Get barcode width
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ return $this->_userWidth;
+ }
+
+ /**
+ * Set an image resource to draw the barcode inside
+ *
+ * @param DOMDocument $value
+ * @return Zend_Barcode_Renderer
+ * @throw Zend_Barcode_Renderer_Exception
+ */
+ public function setResource($svg)
+ {
+ if (!$svg instanceof DOMDocument) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Invalid DOMDocument resource provided to setResource()'
+ );
+ }
+ $this->_resource = $svg;
+ return $this;
+ }
+
+ /**
+ * Initialize the image resource
+ *
+ * @return void
+ */
+ protected function _initRenderer()
+ {
+ $barcodeWidth = $this->_barcode->getWidth(true);
+ $barcodeHeight = $this->_barcode->getHeight(true);
+
+ $backgroundColor = $this->_barcode->getBackgroundColor();
+ $imageBackgroundColor = 'rgb(' . implode(', ', array(($backgroundColor & 0xFF0000) >> 16,
+ ($backgroundColor & 0x00FF00) >> 8,
+ ($backgroundColor & 0x0000FF))) . ')';
+
+ $width = $barcodeWidth;
+ $height = $barcodeHeight;
+ if ($this->_userWidth && $this->_barcode->getType() != 'error') {
+ $width = $this->_userWidth;
+ }
+ if ($this->_userHeight && $this->_barcode->getType() != 'error') {
+ $height = $this->_userHeight;
+ }
+ if ($this->_resource === null) {
+ $this->_resource = new DOMDocument('1.0', 'utf-8');
+ $this->_resource->formatOutput = true;
+ $this->_rootElement = $this->_resource->createElement('svg');
+ $this->_rootElement->setAttribute('xmlns', "http://www.w3.org/2000/svg");
+ $this->_rootElement->setAttribute('version', '1.1');
+ $this->_rootElement->setAttribute('width', $width);
+ $this->_rootElement->setAttribute('height', $height);
+
+ $this->_appendRootElement('title',
+ array(),
+ "Barcode " . strtoupper($this->_barcode->getType()) . " " . $this->_barcode->getText());
+ } else {
+ $this->_readRootElement();
+ $width = $this->_rootElement->getAttribute('width');
+ $height = $this->_rootElement->getAttribute('height');
+ }
+ $this->_adjustPosition($height, $width);
+
+ $this->_appendRootElement('rect',
+ array('x' => $this->_leftOffset,
+ 'y' => $this->_topOffset,
+ 'width' => ($this->_leftOffset + $barcodeWidth - 1),
+ 'height' => ($this->_topOffset + $barcodeHeight - 1),
+ 'fill' => $imageBackgroundColor));
+ }
+
+ protected function _readRootElement()
+ {
+ if ($this->_resource !== null) {
+ $this->_rootElement = $this->_resource->documentElement;
+ }
+ }
+
+ /**
+ * Append a new DOMElement to the root element
+ *
+ * @param string $tagName
+ * @param array $attributes
+ * @param string $textContent
+ */
+ protected function _appendRootElement($tagName, $attributes = array(), $textContent = null)
+ {
+ $newElement = $this->_createElement($tagName, $attributes, $textContent);
+ $this->_rootElement->appendChild($newElement);
+ }
+
+ /**
+ * Create DOMElement
+ *
+ * @param string $tagName
+ * @param array $attributes
+ * @param string $textContent
+ * @return DOMElement
+ */
+ protected function _createElement($tagName, $attributes = array(), $textContent = null)
+ {
+ $element = $this->_resource->createElement($tagName);
+ foreach ($attributes as $k =>$v) {
+ $element->setAttribute($k, $v);
+ }
+ if ($textContent !== null) {
+ $element->appendChild(new DOMText((string) $textContent));
+ }
+ return $element;
+ }
+
+ /**
+ * Check barcode parameters
+ *
+ * @return void
+ */
+ protected function _checkParams()
+ {
+ $this->_checkDimensions();
+ }
+
+ /**
+ * Check barcode dimensions
+ *
+ * @return void
+ */
+ protected function _checkDimensions()
+ {
+ if ($this->_resource !== null) {
+ $this->_readRootElement();
+ $height = (float) $this->_rootElement->getAttribute('height');
+ if ($height < $this->_barcode->getHeight(true)) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Barcode is define outside the image (height)'
+ );
+ }
+ } else {
+ if ($this->_userHeight) {
+ $height = $this->_barcode->getHeight(true);
+ if ($this->_userHeight < $height) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(sprintf(
+ "Barcode is define outside the image (calculated: '%d', provided: '%d')",
+ $height,
+ $this->_userHeight
+ ));
+ }
+ }
+ }
+ if ($this->_resource !== null) {
+ $this->_readRootElement();
+ $width = $this->_rootElement->getAttribute('width');
+ if ($width < $this->_barcode->getWidth(true)) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(
+ 'Barcode is define outside the image (width)'
+ );
+ }
+ } else {
+ if ($this->_userWidth) {
+ $width = (float) $this->_barcode->getWidth(true);
+ if ($this->_userWidth < $width) {
+ require_once 'Zend/Barcode/Renderer/Exception.php';
+ throw new Zend_Barcode_Renderer_Exception(sprintf(
+ "Barcode is define outside the image (calculated: '%d', provided: '%d')",
+ $width,
+ $this->_userWidth
+ ));
+ }
+ }
+ }
+ }
+
+ /**
+ * Draw the barcode in the rendering resource
+ * @return mixed
+ */
+ public function draw()
+ {
+ parent::draw();
+ $this->_resource->appendChild($this->_rootElement);
+ return $this->_resource;
+ }
+
+ /**
+ * Draw and render the barcode with correct headers
+ *
+ * @return mixed
+ */
+ public function render()
+ {
+ $this->draw();
+ header("Content-Type: image/svg+xml");
+ echo $this->_resource->saveXML();
+ }
+
+ /**
+ * Draw a polygon in the svg resource
+ *
+ * @param array $points
+ * @param integer $color
+ * @param boolean $filled
+ */
+ protected function _drawPolygon($points, $color, $filled = true)
+ {
+ $color = 'rgb(' . implode(', ', array(($color & 0xFF0000) >> 16,
+ ($color & 0x00FF00) >> 8,
+ ($color & 0x0000FF))) . ')';
+ $orientation = $this->getBarcode()->getOrientation();
+ $newPoints = array(
+ $points[0][0] + $this->_leftOffset,
+ $points[0][1] + $this->_topOffset,
+ $points[1][0] + $this->_leftOffset,
+ $points[1][1] + $this->_topOffset,
+ $points[2][0] + $this->_leftOffset + cos(-$orientation),
+ $points[2][1] + $this->_topOffset - sin($orientation),
+ $points[3][0] + $this->_leftOffset + cos(-$orientation),
+ $points[3][1] + $this->_topOffset - sin($orientation),
+ );
+ $newPoints = implode(' ', $newPoints);
+ $attributes['points'] = $newPoints;
+ $attributes['fill'] = $color;
+ $this->_appendRootElement('polygon', $attributes);
+ }
+
+ /**
+ * Draw a polygon in the svg resource
+ *
+ * @param string $text
+ * @param float $size
+ * @param array $position
+ * @param string $font
+ * @param integer $color
+ * @param string $alignment
+ * @param float $orientation
+ */
+ protected function _drawText($text, $size, $position, $font, $color, $alignment = 'center', $orientation = 0)
+ {
+ $color = 'rgb(' . implode(', ', array(($color & 0xFF0000) >> 16,
+ ($color & 0x00FF00) >> 8,
+ ($color & 0x0000FF))) . ')';
+ $attributes['x'] = $position[0] + $this->_leftOffset;
+ $attributes['y'] = $position[1] + $this->_topOffset;
+ //$attributes['font-family'] = $font;
+ $attributes['color'] = $color;
+ $attributes['font-size'] = $size * 1.2;
+ switch ($alignment) {
+ case 'left':
+ $textAnchor = 'start';
+ break;
+ case 'right':
+ $textAnchor = 'end';
+ break;
+ case 'center':
+ default:
+ $textAnchor = 'middle';
+ }
+ $attributes['style'] = 'text-anchor: ' . $textAnchor;
+ $attributes['transform'] = 'rotate('
+ . (- $orientation)
+ . ', '
+ . ($position[0] + $this->_leftOffset)
+ . ', ' . ($position[1] + $this->_topOffset)
+ . ')';
+ $this->_appendRootElement('text', $attributes, $text);
+ }
+}
diff --git a/library/Zend/Cache.php b/library/Zend/Cache.php
new file mode 100644
index 0000000..9ecbe8e
--- /dev/null
+++ b/library/Zend/Cache.php
@@ -0,0 +1,250 @@
+setBackend($backendObject);
+ return $frontendObject;
+ }
+
+ /**
+ * Backend Constructor
+ *
+ * @param string $backend
+ * @param array $backendOptions
+ * @param boolean $customBackendNaming
+ * @param boolean $autoload
+ * @return Zend_Cache_Backend
+ */
+ public static function _makeBackend($backend, $backendOptions, $customBackendNaming = false, $autoload = false)
+ {
+ if (!$customBackendNaming) {
+ $backend = self::_normalizeName($backend);
+ }
+ if (in_array($backend, Zend_Cache::$standardBackends)) {
+ // we use a standard backend
+ $backendClass = 'Zend_Cache_Backend_' . $backend;
+ // security controls are explicit
+ require_once str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php';
+ } else {
+ // we use a custom backend
+ if (!preg_match('~^[\w]+$~D', $backend)) {
+ Zend_Cache::throwException("Invalid backend name [$backend]");
+ }
+ if (!$customBackendNaming) {
+ // we use this boolean to avoid an API break
+ $backendClass = 'Zend_Cache_Backend_' . $backend;
+ } else {
+ $backendClass = $backend;
+ }
+ if (!$autoload) {
+ $file = str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php';
+ if (!(self::_isReadable($file))) {
+ self::throwException("file $file not found in include_path");
+ }
+ require_once $file;
+ }
+ }
+ return new $backendClass($backendOptions);
+ }
+
+ /**
+ * Frontend Constructor
+ *
+ * @param string $frontend
+ * @param array $frontendOptions
+ * @param boolean $customFrontendNaming
+ * @param boolean $autoload
+ * @return Zend_Cache_Core|Zend_Cache_Frontend
+ */
+ public static function _makeFrontend($frontend, $frontendOptions = array(), $customFrontendNaming = false, $autoload = false)
+ {
+ if (!$customFrontendNaming) {
+ $frontend = self::_normalizeName($frontend);
+ }
+ if (in_array($frontend, self::$standardFrontends)) {
+ // we use a standard frontend
+ // For perfs reasons, with frontend == 'Core', we can interact with the Core itself
+ $frontendClass = 'Zend_Cache_' . ($frontend != 'Core' ? 'Frontend_' : '') . $frontend;
+ // security controls are explicit
+ require_once str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php';
+ } else {
+ // we use a custom frontend
+ if (!preg_match('~^[\w]+$~D', $frontend)) {
+ Zend_Cache::throwException("Invalid frontend name [$frontend]");
+ }
+ if (!$customFrontendNaming) {
+ // we use this boolean to avoid an API break
+ $frontendClass = 'Zend_Cache_Frontend_' . $frontend;
+ } else {
+ $frontendClass = $frontend;
+ }
+ if (!$autoload) {
+ $file = str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php';
+ if (!(self::_isReadable($file))) {
+ self::throwException("file $file not found in include_path");
+ }
+ require_once $file;
+ }
+ }
+ return new $frontendClass($frontendOptions);
+ }
+
+ /**
+ * Throw an exception
+ *
+ * Note : for perf reasons, the "load" of Zend/Cache/Exception is dynamic
+ * @param string $msg Message for the exception
+ * @throws Zend_Cache_Exception
+ */
+ public static function throwException($msg, Exception $e = null)
+ {
+ // For perfs reasons, we use this dynamic inclusion
+ require_once 'Zend/Cache/Exception.php';
+ throw new Zend_Cache_Exception($msg, 0, $e);
+ }
+
+ /**
+ * Normalize frontend and backend names to allow multiple words TitleCased
+ *
+ * @param string $name Name to normalize
+ * @return string
+ */
+ protected static function _normalizeName($name)
+ {
+ $name = ucfirst(strtolower($name));
+ $name = str_replace(array('-', '_', '.'), ' ', $name);
+ $name = ucwords($name);
+ $name = str_replace(' ', '', $name);
+ if (stripos($name, 'ZendServer') === 0) {
+ $name = 'ZendServer_' . substr($name, strlen('ZendServer'));
+ }
+
+ return $name;
+ }
+
+ /**
+ * Returns TRUE if the $filename is readable, or FALSE otherwise.
+ * This function uses the PHP include_path, where PHP's is_readable()
+ * does not.
+ *
+ * Note : this method comes from Zend_Loader (see #ZF-2891 for details)
+ *
+ * @param string $filename
+ * @return boolean
+ */
+ private static function _isReadable($filename)
+ {
+ if (!$fh = @fopen($filename, 'r', true)) {
+ return false;
+ }
+ @fclose($fh);
+ return true;
+ }
+
+}
diff --git a/library/Zend/Cache/Backend.php b/library/Zend/Cache/Backend.php
new file mode 100644
index 0000000..469180b
--- /dev/null
+++ b/library/Zend/Cache/Backend.php
@@ -0,0 +1,268 @@
+ (int) lifetime :
+ * - Cache lifetime (in seconds)
+ * - If null, the cache is valid forever
+ *
+ * =====> (int) logging :
+ * - if set to true, a logging is activated throw Zend_Log
+ *
+ * @var array directives
+ */
+ protected $_directives = array(
+ 'lifetime' => 3600,
+ 'logging' => false,
+ 'logger' => null
+ );
+
+ /**
+ * Available options
+ *
+ * @var array available options
+ */
+ protected $_options = array();
+
+ /**
+ * Constructor
+ *
+ * @param array $options Associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ while (list($name, $value) = each($options)) {
+ $this->setOption($name, $value);
+ }
+ }
+
+ /**
+ * Set the frontend directives
+ *
+ * @param array $directives Assoc of directives
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setDirectives($directives)
+ {
+ if (!is_array($directives)) Zend_Cache::throwException('Directives parameter must be an array');
+ while (list($name, $value) = each($directives)) {
+ if (!is_string($name)) {
+ Zend_Cache::throwException("Incorrect option name : $name");
+ }
+ $name = strtolower($name);
+ if (array_key_exists($name, $this->_directives)) {
+ $this->_directives[$name] = $value;
+ }
+
+ }
+
+ $this->_loggerSanity();
+ }
+
+ /**
+ * Set an option
+ *
+ * @param string $name
+ * @param mixed $value
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setOption($name, $value)
+ {
+ if (!is_string($name)) {
+ Zend_Cache::throwException("Incorrect option name : $name");
+ }
+ $name = strtolower($name);
+ if (array_key_exists($name, $this->_options)) {
+ $this->_options[$name] = $value;
+ }
+ }
+
+ /**
+ * Get the life time
+ *
+ * if $specificLifetime is not false, the given specific life time is used
+ * else, the global lifetime is used
+ *
+ * @param int $specificLifetime
+ * @return int Cache life time
+ */
+ public function getLifetime($specificLifetime)
+ {
+ if ($specificLifetime === false) {
+ return $this->_directives['lifetime'];
+ }
+ return $specificLifetime;
+ }
+
+ /**
+ * Return true if the automatic cleaning is available for the backend
+ *
+ * DEPRECATED : use getCapabilities() instead
+ *
+ * @deprecated
+ * @return boolean
+ */
+ public function isAutomaticCleaningAvailable()
+ {
+ return true;
+ }
+
+ /**
+ * Determine system TMP directory and detect if we have read access
+ *
+ * inspired from Zend_File_Transfer_Adapter_Abstract
+ *
+ * @return string
+ * @throws Zend_Cache_Exception if unable to determine directory
+ */
+ public function getTmpDir()
+ {
+ $tmpdir = array();
+ foreach (array($_ENV, $_SERVER) as $tab) {
+ foreach (array('TMPDIR', 'TEMP', 'TMP', 'windir', 'SystemRoot') as $key) {
+ if (isset($tab[$key])) {
+ if (($key == 'windir') or ($key == 'SystemRoot')) {
+ $dir = realpath($tab[$key] . '\\temp');
+ } else {
+ $dir = realpath($tab[$key]);
+ }
+ if ($this->_isGoodTmpDir($dir)) {
+ return $dir;
+ }
+ }
+ }
+ }
+ $upload = ini_get('upload_tmp_dir');
+ if ($upload) {
+ $dir = realpath($upload);
+ if ($this->_isGoodTmpDir($dir)) {
+ return $dir;
+ }
+ }
+ if (function_exists('sys_get_temp_dir')) {
+ $dir = sys_get_temp_dir();
+ if ($this->_isGoodTmpDir($dir)) {
+ return $dir;
+ }
+ }
+ // Attemp to detect by creating a temporary file
+ $tempFile = tempnam(md5(uniqid(rand(), TRUE)), '');
+ if ($tempFile) {
+ $dir = realpath(dirname($tempFile));
+ unlink($tempFile);
+ if ($this->_isGoodTmpDir($dir)) {
+ return $dir;
+ }
+ }
+ if ($this->_isGoodTmpDir('/tmp')) {
+ return '/tmp';
+ }
+ if ($this->_isGoodTmpDir('\\temp')) {
+ return '\\temp';
+ }
+ Zend_Cache::throwException('Could not determine temp directory, please specify a cache_dir manually');
+ }
+
+ /**
+ * Verify if the given temporary directory is readable and writable
+ *
+ * @param string $dir temporary directory
+ * @return boolean true if the directory is ok
+ */
+ protected function _isGoodTmpDir($dir)
+ {
+ if (is_readable($dir)) {
+ if (is_writable($dir)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Make sure if we enable logging that the Zend_Log class
+ * is available.
+ * Create a default log object if none is set.
+ *
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected function _loggerSanity()
+ {
+ if (!isset($this->_directives['logging']) || !$this->_directives['logging']) {
+ return;
+ }
+
+ if (isset($this->_directives['logger'])) {
+ if ($this->_directives['logger'] instanceof Zend_Log) {
+ return;
+ }
+ Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.');
+ }
+
+ // Create a default logger to the standard output stream
+ require_once 'Zend/Log.php';
+ require_once 'Zend/Log/Writer/Stream.php';
+ require_once 'Zend/Log/Filter/Priority.php';
+ $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
+ $logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<='));
+ $this->_directives['logger'] = $logger;
+ }
+
+ /**
+ * Log a message at the WARN (4) priority.
+ *
+ * @param string $message
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected function _log($message, $priority = 4)
+ {
+ if (!$this->_directives['logging']) {
+ return;
+ }
+
+ if (!isset($this->_directives['logger'])) {
+ Zend_Cache::throwException('Logging is enabled but logger is not set.');
+ }
+ $logger = $this->_directives['logger'];
+ if (!$logger instanceof Zend_Log) {
+ Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.');
+ }
+ $logger->log($message, $priority);
+ }
+}
diff --git a/library/Zend/Cache/Backend/Apc.php b/library/Zend/Cache/Backend/Apc.php
new file mode 100644
index 0000000..7616742
--- /dev/null
+++ b/library/Zend/Cache/Backend/Apc.php
@@ -0,0 +1,355 @@
+ infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $lifetime = $this->getLifetime($specificLifetime);
+ $result = apc_store($id, array($data, time(), $lifetime), $lifetime);
+ if (count($tags) > 0) {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND);
+ }
+ return $result;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id cache id
+ * @return boolean true if no problem
+ */
+ public function remove($id)
+ {
+ return apc_delete($id);
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => unsupported
+ * 'matchingTag' => unsupported
+ * 'notMatchingTag' => unsupported
+ * 'matchingAnyTag' => unsupported
+ *
+ * @param string $mode clean mode
+ * @param array $tags array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ return apc_clear_cache('user');
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $this->_log("Zend_Cache_Backend_Apc::clean() : CLEANING_MODE_OLD is unsupported by the Apc backend");
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_APC_BACKEND);
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+
+ /**
+ * Return true if the automatic cleaning is available for the backend
+ *
+ * DEPRECATED : use getCapabilities() instead
+ *
+ * @deprecated
+ * @return boolean
+ */
+ public function isAutomaticCleaningAvailable()
+ {
+ return false;
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @throws Zend_Cache_Exception
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ $mem = apc_sma_info(true);
+ $memSize = $mem['num_seg'] * $mem['seg_size'];
+ $memAvailable= $mem['avail_mem'];
+ $memUsed = $memSize - $memAvailable;
+ if ($memSize == 0) {
+ Zend_Cache::throwException('can\'t get apc memory size');
+ }
+ if ($memUsed > $memSize) {
+ return 100;
+ }
+ return ((int) (100. * ($memUsed / $memSize)));
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_APC_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ $res = array();
+ $array = apc_cache_info('user', false);
+ $records = $array['cache_list'];
+ foreach ($records as $record) {
+ $res[] = $record['info'];
+ }
+ return $res;
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ $tmp = apc_fetch($id);
+ if (is_array($tmp)) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ if (!isset($tmp[2])) {
+ // because this record is only with 1.7 release
+ // if old cache records are still there...
+ return false;
+ }
+ $lifetime = $tmp[2];
+ return array(
+ 'expire' => $mtime + $lifetime,
+ 'tags' => array(),
+ 'mtime' => $mtime
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ $tmp = apc_fetch($id);
+ if (is_array($tmp)) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ if (!isset($tmp[2])) {
+ // because this record is only with 1.7 release
+ // if old cache records are still there...
+ return false;
+ }
+ $lifetime = $tmp[2];
+ $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime;
+ if ($newLifetime <=0) {
+ return false;
+ }
+ apc_store($id, array($data, time(), $newLifetime), $newLifetime);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => false,
+ 'tags' => false,
+ 'expired_read' => false,
+ 'priority' => false,
+ 'infinite_lifetime' => false,
+ 'get_list' => true
+ );
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/BlackHole.php b/library/Zend/Cache/Backend/BlackHole.php
new file mode 100644
index 0000000..577fcda
--- /dev/null
+++ b/library/Zend/Cache/Backend/BlackHole.php
@@ -0,0 +1,250 @@
+ infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ return true;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id cache id
+ * @return boolean true if no problem
+ */
+ public function remove($id)
+ {
+ return true;
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => remove too old cache entries ($tags is not used)
+ * 'matchingTag' => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * 'notMatchingTag' => remove cache entries not matching one of the given tags
+ * ($tags can be an array of strings or a single string)
+ * 'matchingAnyTag' => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode clean mode
+ * @param tags array $tags array of tags
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ return true;
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ return array();
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ return array();
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @return int integer between 0 and 100
+ * @throws Zend_Cache_Exception
+ */
+ public function getFillingPercentage()
+ {
+ return 0;
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ return false;
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ return false;
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => true,
+ 'tags' => true,
+ 'expired_read' => true,
+ 'priority' => true,
+ 'infinite_lifetime' => true,
+ 'get_list' => true,
+ );
+ }
+
+ /**
+ * PUBLIC METHOD FOR UNIT TESTING ONLY !
+ *
+ * Force a cache record to expire
+ *
+ * @param string $id cache id
+ */
+ public function ___expire($id)
+ {
+ }
+}
diff --git a/library/Zend/Cache/Backend/ExtendedInterface.php b/library/Zend/Cache/Backend/ExtendedInterface.php
new file mode 100644
index 0000000..d403917
--- /dev/null
+++ b/library/Zend/Cache/Backend/ExtendedInterface.php
@@ -0,0 +1,126 @@
+ (string) cache_dir :
+ * - Directory where to put the cache files
+ *
+ * =====> (boolean) file_locking :
+ * - Enable / disable file_locking
+ * - Can avoid cache corruption under bad circumstances but it doesn't work on multithread
+ * webservers and on NFS filesystems for example
+ *
+ * =====> (boolean) read_control :
+ * - Enable / disable read control
+ * - If enabled, a control key is embeded in cache file and this key is compared with the one
+ * calculated after the reading.
+ *
+ * =====> (string) read_control_type :
+ * - Type of read control (only if read control is enabled). Available values are :
+ * 'md5' for a md5 hash control (best but slowest)
+ * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice)
+ * 'adler32' for an adler32 hash control (excellent choice too, faster than crc32)
+ * 'strlen' for a length only test (fastest)
+ *
+ * =====> (int) hashed_directory_level :
+ * - Hashed directory level
+ * - Set the hashed directory structure level. 0 means "no hashed directory
+ * structure", 1 means "one level of directory", 2 means "two levels"...
+ * This option can speed up the cache only when you have many thousands of
+ * cache file. Only specific benchs can help you to choose the perfect value
+ * for you. Maybe, 1 or 2 is a good start.
+ *
+ * =====> (int) hashed_directory_umask :
+ * - Umask for hashed directory structure
+ *
+ * =====> (string) file_name_prefix :
+ * - prefix for cache files
+ * - be really carefull with this option because a too generic value in a system cache dir
+ * (like /tmp) can cause disasters when cleaning the cache
+ *
+ * =====> (int) cache_file_umask :
+ * - Umask for cache files
+ *
+ * =====> (int) metatadatas_array_max_size :
+ * - max size for the metadatas array (don't change this value unless you
+ * know what you are doing)
+ *
+ * @var array available options
+ */
+ protected $_options = array(
+ 'cache_dir' => null,
+ 'file_locking' => true,
+ 'read_control' => true,
+ 'read_control_type' => 'crc32',
+ 'hashed_directory_level' => 0,
+ 'hashed_directory_umask' => 0700,
+ 'file_name_prefix' => 'zend_cache',
+ 'cache_file_umask' => 0600,
+ 'metadatas_array_max_size' => 100
+ );
+
+ /**
+ * Array of metadatas (each item is an associative array)
+ *
+ * @var array
+ */
+ protected $_metadatasArray = array();
+
+
+ /**
+ * Constructor
+ *
+ * @param array $options associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ parent::__construct($options);
+ if ($this->_options['cache_dir'] !== null) { // particular case for this option
+ $this->setCacheDir($this->_options['cache_dir']);
+ } else {
+ $this->setCacheDir(self::getTmpDir() . DIRECTORY_SEPARATOR, false);
+ }
+ if (isset($this->_options['file_name_prefix'])) { // particular case for this option
+ if (!preg_match('~^[a-zA-Z0-9_]+$~D', $this->_options['file_name_prefix'])) {
+ Zend_Cache::throwException('Invalid file_name_prefix : must use only [a-zA-Z0-9_]');
+ }
+ }
+ if ($this->_options['metadatas_array_max_size'] < 10) {
+ Zend_Cache::throwException('Invalid metadatas_array_max_size, must be > 10');
+ }
+ if (isset($options['hashed_directory_umask']) && is_string($options['hashed_directory_umask'])) {
+ // See #ZF-4422
+ $this->_options['hashed_directory_umask'] = octdec($this->_options['hashed_directory_umask']);
+ }
+ if (isset($options['cache_file_umask']) && is_string($options['cache_file_umask'])) {
+ // See #ZF-4422
+ $this->_options['cache_file_umask'] = octdec($this->_options['cache_file_umask']);
+ }
+ }
+
+ /**
+ * Set the cache_dir (particular case of setOption() method)
+ *
+ * @param string $value
+ * @param boolean $trailingSeparator If true, add a trailing separator is necessary
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setCacheDir($value, $trailingSeparator = true)
+ {
+ if (!is_dir($value)) {
+ Zend_Cache::throwException('cache_dir must be a directory');
+ }
+ if (!is_writable($value)) {
+ Zend_Cache::throwException('cache_dir is not writable');
+ }
+ if ($trailingSeparator) {
+ // add a trailing DIRECTORY_SEPARATOR if necessary
+ $value = rtrim(realpath($value), '\\/') . DIRECTORY_SEPARATOR;
+ }
+ $this->_options['cache_dir'] = $value;
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * @param string $id cache id
+ * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
+ * @return string|false cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ if (!($this->_test($id, $doNotTestCacheValidity))) {
+ // The cache is not hit !
+ return false;
+ }
+ $metadatas = $this->_getMetadatas($id);
+ $file = $this->_file($id);
+ $data = $this->_fileGetContents($file);
+ if ($this->_options['read_control']) {
+ $hashData = $this->_hash($data, $this->_options['read_control_type']);
+ $hashControl = $metadatas['hash'];
+ if ($hashData != $hashControl) {
+ // Problem detected by the read control !
+ $this->_log('Zend_Cache_Backend_File::load() / read_control : stored hash and computed hash do not match');
+ $this->remove($id);
+ return false;
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id cache id
+ * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ clearstatcache();
+ return $this->_test($id, false);
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data Datas to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ clearstatcache();
+ $file = $this->_file($id);
+ $path = $this->_path($id);
+ if ($this->_options['hashed_directory_level'] > 0) {
+ if (!is_writable($path)) {
+ // maybe, we just have to build the directory structure
+ $this->_recursiveMkdirAndChmod($id);
+ }
+ if (!is_writable($path)) {
+ return false;
+ }
+ }
+ if ($this->_options['read_control']) {
+ $hash = $this->_hash($data, $this->_options['read_control_type']);
+ } else {
+ $hash = '';
+ }
+ $metadatas = array(
+ 'hash' => $hash,
+ 'mtime' => time(),
+ 'expire' => $this->_expireTime($this->getLifetime($specificLifetime)),
+ 'tags' => $tags
+ );
+ $res = $this->_setMetadatas($id, $metadatas);
+ if (!$res) {
+ $this->_log('Zend_Cache_Backend_File::save() / error on saving metadata');
+ return false;
+ }
+ $res = $this->_filePutContents($file, $data);
+ return $res;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id cache id
+ * @return boolean true if no problem
+ */
+ public function remove($id)
+ {
+ $file = $this->_file($id);
+ $boolRemove = $this->_remove($file);
+ $boolMetadata = $this->_delMetadatas($id);
+ return $boolMetadata && $boolRemove;
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ *
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode clean mode
+ * @param tags array $tags array of tags
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ // We use this protected method to hide the recursive stuff
+ clearstatcache();
+ return $this->_clean($this->_options['cache_dir'], $mode, $tags);
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ return $this->_get($this->_options['cache_dir'], 'ids', array());
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ return $this->_get($this->_options['cache_dir'], 'tags', array());
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ return $this->_get($this->_options['cache_dir'], 'matching', $tags);
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ return $this->_get($this->_options['cache_dir'], 'notMatching', $tags);
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ return $this->_get($this->_options['cache_dir'], 'matchingAny', $tags);
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @throws Zend_Cache_Exception
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ $free = disk_free_space($this->_options['cache_dir']);
+ $total = disk_total_space($this->_options['cache_dir']);
+ if ($total == 0) {
+ Zend_Cache::throwException('can\'t get disk_total_space');
+ } else {
+ if ($free >= $total) {
+ return 100;
+ }
+ return ((int) (100. * ($total - $free) / $total));
+ }
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ $metadatas = $this->_getMetadatas($id);
+ if (!$metadatas) {
+ return false;
+ }
+ if (time() > $metadatas['expire']) {
+ return false;
+ }
+ return array(
+ 'expire' => $metadatas['expire'],
+ 'tags' => $metadatas['tags'],
+ 'mtime' => $metadatas['mtime']
+ );
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ $metadatas = $this->_getMetadatas($id);
+ if (!$metadatas) {
+ return false;
+ }
+ if (time() > $metadatas['expire']) {
+ return false;
+ }
+ $newMetadatas = array(
+ 'hash' => $metadatas['hash'],
+ 'mtime' => time(),
+ 'expire' => $metadatas['expire'] + $extraLifetime,
+ 'tags' => $metadatas['tags']
+ );
+ $res = $this->_setMetadatas($id, $newMetadatas);
+ if (!$res) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => true,
+ 'tags' => true,
+ 'expired_read' => true,
+ 'priority' => false,
+ 'infinite_lifetime' => true,
+ 'get_list' => true
+ );
+ }
+
+ /**
+ * PUBLIC METHOD FOR UNIT TESTING ONLY !
+ *
+ * Force a cache record to expire
+ *
+ * @param string $id cache id
+ */
+ public function ___expire($id)
+ {
+ $metadatas = $this->_getMetadatas($id);
+ if ($metadatas) {
+ $metadatas['expire'] = 1;
+ $this->_setMetadatas($id, $metadatas);
+ }
+ }
+
+ /**
+ * Get a metadatas record
+ *
+ * @param string $id Cache id
+ * @return array|false Associative array of metadatas
+ */
+ protected function _getMetadatas($id)
+ {
+ if (isset($this->_metadatasArray[$id])) {
+ return $this->_metadatasArray[$id];
+ } else {
+ $metadatas = $this->_loadMetadatas($id);
+ if (!$metadatas) {
+ return false;
+ }
+ $this->_setMetadatas($id, $metadatas, false);
+ return $metadatas;
+ }
+ }
+
+ /**
+ * Set a metadatas record
+ *
+ * @param string $id Cache id
+ * @param array $metadatas Associative array of metadatas
+ * @param boolean $save optional pass false to disable saving to file
+ * @return boolean True if no problem
+ */
+ protected function _setMetadatas($id, $metadatas, $save = true)
+ {
+ if (count($this->_metadatasArray) >= $this->_options['metadatas_array_max_size']) {
+ $n = (int) ($this->_options['metadatas_array_max_size'] / 10);
+ $this->_metadatasArray = array_slice($this->_metadatasArray, $n);
+ }
+ if ($save) {
+ $result = $this->_saveMetadatas($id, $metadatas);
+ if (!$result) {
+ return false;
+ }
+ }
+ $this->_metadatasArray[$id] = $metadatas;
+ return true;
+ }
+
+ /**
+ * Drop a metadata record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ protected function _delMetadatas($id)
+ {
+ if (isset($this->_metadatasArray[$id])) {
+ unset($this->_metadatasArray[$id]);
+ }
+ $file = $this->_metadatasFile($id);
+ return $this->_remove($file);
+ }
+
+ /**
+ * Clear the metadatas array
+ *
+ * @return void
+ */
+ protected function _cleanMetadatas()
+ {
+ $this->_metadatasArray = array();
+ }
+
+ /**
+ * Load metadatas from disk
+ *
+ * @param string $id Cache id
+ * @return array|false Metadatas associative array
+ */
+ protected function _loadMetadatas($id)
+ {
+ $file = $this->_metadatasFile($id);
+ $result = $this->_fileGetContents($file);
+ if (!$result) {
+ return false;
+ }
+ $tmp = @unserialize($result);
+ return $tmp;
+ }
+
+ /**
+ * Save metadatas to disk
+ *
+ * @param string $id Cache id
+ * @param array $metadatas Associative array
+ * @return boolean True if no problem
+ */
+ protected function _saveMetadatas($id, $metadatas)
+ {
+ $file = $this->_metadatasFile($id);
+ $result = $this->_filePutContents($file, serialize($metadatas));
+ if (!$result) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Make and return a file name (with path) for metadatas
+ *
+ * @param string $id Cache id
+ * @return string Metadatas file name (with path)
+ */
+ protected function _metadatasFile($id)
+ {
+ $path = $this->_path($id);
+ $fileName = $this->_idToFileName('internal-metadatas---' . $id);
+ return $path . $fileName;
+ }
+
+ /**
+ * Check if the given filename is a metadatas one
+ *
+ * @param string $fileName File name
+ * @return boolean True if it's a metadatas one
+ */
+ protected function _isMetadatasFile($fileName)
+ {
+ $id = $this->_fileNameToId($fileName);
+ if (substr($id, 0, 21) == 'internal-metadatas---') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Remove a file
+ *
+ * If we can't remove the file (because of locks or any problem), we will touch
+ * the file to invalidate it
+ *
+ * @param string $file Complete file path
+ * @return boolean True if ok
+ */
+ protected function _remove($file)
+ {
+ if (!is_file($file)) {
+ return false;
+ }
+ if (!@unlink($file)) {
+ # we can't remove the file (because of locks or any problem)
+ $this->_log("Zend_Cache_Backend_File::_remove() : we can't remove $file");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Clean some cache records (protected method used for recursive stuff)
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $dir Directory to clean
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean True if no problem
+ */
+ protected function _clean($dir, $mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ if (!is_dir($dir)) {
+ return false;
+ }
+ $result = true;
+ $prefix = $this->_options['file_name_prefix'];
+ $glob = @glob($dir . $prefix . '--*');
+ if ($glob === false) {
+ // On some systems it is impossible to distinguish between empty match and an error.
+ return true;
+ }
+ foreach ($glob as $file) {
+ if (is_file($file)) {
+ $fileName = basename($file);
+ if ($this->_isMetadatasFile($fileName)) {
+ // in CLEANING_MODE_ALL, we drop anything, even remainings old metadatas files
+ if ($mode != Zend_Cache::CLEANING_MODE_ALL) {
+ continue;
+ }
+ }
+ $id = $this->_fileNameToId($fileName);
+ $metadatas = $this->_getMetadatas($id);
+ if ($metadatas === FALSE) {
+ $metadatas = array('expire' => 1, 'tags' => array());
+ }
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ $res = $this->remove($id);
+ if (!$res) {
+ // in this case only, we accept a problem with the metadatas file drop
+ $res = $this->_remove($file);
+ }
+ $result = $result && $res;
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ if (time() > $metadatas['expire']) {
+ $result = $this->remove($id) && $result;
+ }
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ $matching = true;
+ foreach ($tags as $tag) {
+ if (!in_array($tag, $metadatas['tags'])) {
+ $matching = false;
+ break;
+ }
+ }
+ if ($matching) {
+ $result = $this->remove($id) && $result;
+ }
+ break;
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ $matching = false;
+ foreach ($tags as $tag) {
+ if (in_array($tag, $metadatas['tags'])) {
+ $matching = true;
+ break;
+ }
+ }
+ if (!$matching) {
+ $result = $this->remove($id) && $result;
+ }
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $matching = false;
+ foreach ($tags as $tag) {
+ if (in_array($tag, $metadatas['tags'])) {
+ $matching = true;
+ break;
+ }
+ }
+ if ($matching) {
+ $result = $this->remove($id) && $result;
+ }
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+ if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) {
+ // Recursive call
+ $result = $this->_clean($file . DIRECTORY_SEPARATOR, $mode, $tags) && $result;
+ if ($mode == Zend_Cache::CLEANING_MODE_ALL) {
+ // we try to drop the structure too
+ @rmdir($file);
+ }
+ }
+ }
+ return $result;
+ }
+
+ protected function _get($dir, $mode, $tags = array())
+ {
+ if (!is_dir($dir)) {
+ return false;
+ }
+ $result = array();
+ $prefix = $this->_options['file_name_prefix'];
+ $glob = @glob($dir . $prefix . '--*');
+ if ($glob === false) {
+ // On some systems it is impossible to distinguish between empty match and an error.
+ return array();
+ }
+ foreach ($glob as $file) {
+ if (is_file($file)) {
+ $fileName = basename($file);
+ $id = $this->_fileNameToId($fileName);
+ $metadatas = $this->_getMetadatas($id);
+ if ($metadatas === FALSE) {
+ continue;
+ }
+ if (time() > $metadatas['expire']) {
+ continue;
+ }
+ switch ($mode) {
+ case 'ids':
+ $result[] = $id;
+ break;
+ case 'tags':
+ $result = array_unique(array_merge($result, $metadatas['tags']));
+ break;
+ case 'matching':
+ $matching = true;
+ foreach ($tags as $tag) {
+ if (!in_array($tag, $metadatas['tags'])) {
+ $matching = false;
+ break;
+ }
+ }
+ if ($matching) {
+ $result[] = $id;
+ }
+ break;
+ case 'notMatching':
+ $matching = false;
+ foreach ($tags as $tag) {
+ if (in_array($tag, $metadatas['tags'])) {
+ $matching = true;
+ break;
+ }
+ }
+ if (!$matching) {
+ $result[] = $id;
+ }
+ break;
+ case 'matchingAny':
+ $matching = false;
+ foreach ($tags as $tag) {
+ if (in_array($tag, $metadatas['tags'])) {
+ $matching = true;
+ break;
+ }
+ }
+ if ($matching) {
+ $result[] = $id;
+ }
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for _get() method');
+ break;
+ }
+ }
+ if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) {
+ // Recursive call
+ $recursiveRs = $this->_get($file . DIRECTORY_SEPARATOR, $mode, $tags);
+ if ($recursiveRs === false) {
+ $this->_log('Zend_Cache_Backend_File::_get() / recursive call : can\'t list entries of "'.$file.'"');
+ } else {
+ $result = array_unique(array_merge($result, $recursiveRs));
+ }
+ }
+ }
+ return array_unique($result);
+ }
+
+ /**
+ * Compute & return the expire time
+ *
+ * @return int expire time (unix timestamp)
+ */
+ protected function _expireTime($lifetime)
+ {
+ if ($lifetime === null) {
+ return 9999999999;
+ }
+ return time() + $lifetime;
+ }
+
+ /**
+ * Make a control key with the string containing datas
+ *
+ * @param string $data Data
+ * @param string $controlType Type of control 'md5', 'crc32' or 'strlen'
+ * @throws Zend_Cache_Exception
+ * @return string Control key
+ */
+ protected function _hash($data, $controlType)
+ {
+ switch ($controlType) {
+ case 'md5':
+ return md5($data);
+ case 'crc32':
+ return crc32($data);
+ case 'strlen':
+ return strlen($data);
+ case 'adler32':
+ return hash('adler32', $data);
+ default:
+ Zend_Cache::throwException("Incorrect hash function : $controlType");
+ }
+ }
+
+ /**
+ * Transform a cache id into a file name and return it
+ *
+ * @param string $id Cache id
+ * @return string File name
+ */
+ protected function _idToFileName($id)
+ {
+ $prefix = $this->_options['file_name_prefix'];
+ $result = $prefix . '---' . $id;
+ return $result;
+ }
+
+ /**
+ * Make and return a file name (with path)
+ *
+ * @param string $id Cache id
+ * @return string File name (with path)
+ */
+ protected function _file($id)
+ {
+ $path = $this->_path($id);
+ $fileName = $this->_idToFileName($id);
+ return $path . $fileName;
+ }
+
+ /**
+ * Return the complete directory path of a filename (including hashedDirectoryStructure)
+ *
+ * @param string $id Cache id
+ * @param boolean $parts if true, returns array of directory parts instead of single string
+ * @return string Complete directory path
+ */
+ protected function _path($id, $parts = false)
+ {
+ $partsArray = array();
+ $root = $this->_options['cache_dir'];
+ $prefix = $this->_options['file_name_prefix'];
+ if ($this->_options['hashed_directory_level']>0) {
+ $hash = hash('adler32', $id);
+ for ($i=0 ; $i < $this->_options['hashed_directory_level'] ; $i++) {
+ $root = $root . $prefix . '--' . substr($hash, 0, $i + 1) . DIRECTORY_SEPARATOR;
+ $partsArray[] = $root;
+ }
+ }
+ if ($parts) {
+ return $partsArray;
+ } else {
+ return $root;
+ }
+ }
+
+ /**
+ * Make the directory strucuture for the given id
+ *
+ * @param string $id cache id
+ * @return boolean true
+ */
+ protected function _recursiveMkdirAndChmod($id)
+ {
+ if ($this->_options['hashed_directory_level'] <=0) {
+ return true;
+ }
+ $partsArray = $this->_path($id, true);
+ foreach ($partsArray as $part) {
+ if (!is_dir($part)) {
+ @mkdir($part, $this->_options['hashed_directory_umask']);
+ @chmod($part, $this->_options['hashed_directory_umask']); // see #ZF-320 (this line is required in some configurations)
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Test if the given cache id is available (and still valid as a cache record)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return boolean|mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ protected function _test($id, $doNotTestCacheValidity)
+ {
+ $metadatas = $this->_getMetadatas($id);
+ if (!$metadatas) {
+ return false;
+ }
+ if ($doNotTestCacheValidity || (time() <= $metadatas['expire'])) {
+ return $metadatas['mtime'];
+ }
+ return false;
+ }
+
+ /**
+ * Return the file content of the given file
+ *
+ * @param string $file File complete path
+ * @return string File content (or false if problem)
+ */
+ protected function _fileGetContents($file)
+ {
+ $result = false;
+ if (!is_file($file)) {
+ return false;
+ }
+ $f = @fopen($file, 'rb');
+ if ($f) {
+ if ($this->_options['file_locking']) @flock($f, LOCK_SH);
+ $result = stream_get_contents($f);
+ if ($this->_options['file_locking']) @flock($f, LOCK_UN);
+ @fclose($f);
+ }
+ return $result;
+ }
+
+ /**
+ * Put the given string into the given file
+ *
+ * @param string $file File complete path
+ * @param string $string String to put in file
+ * @return boolean true if no problem
+ */
+ protected function _filePutContents($file, $string)
+ {
+ $result = false;
+ $f = @fopen($file, 'ab+');
+ if ($f) {
+ if ($this->_options['file_locking']) @flock($f, LOCK_EX);
+ fseek($f, 0);
+ ftruncate($f, 0);
+ $tmp = @fwrite($f, $string);
+ if (!($tmp === FALSE)) {
+ $result = true;
+ }
+ @fclose($f);
+ }
+ @chmod($file, $this->_options['cache_file_umask']);
+ return $result;
+ }
+
+ /**
+ * Transform a file name into cache id and return it
+ *
+ * @param string $fileName File name
+ * @return string Cache id
+ */
+ protected function _fileNameToId($fileName)
+ {
+ $prefix = $this->_options['file_name_prefix'];
+ return preg_replace('~^' . $prefix . '---(.*)$~', '$1', $fileName);
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/Interface.php b/library/Zend/Cache/Backend/Interface.php
new file mode 100644
index 0000000..1d58363
--- /dev/null
+++ b/library/Zend/Cache/Backend/Interface.php
@@ -0,0 +1,99 @@
+ infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false);
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id);
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array());
+
+}
diff --git a/library/Zend/Cache/Backend/Libmemcached.php b/library/Zend/Cache/Backend/Libmemcached.php
new file mode 100644
index 0000000..b3a530a
--- /dev/null
+++ b/library/Zend/Cache/Backend/Libmemcached.php
@@ -0,0 +1,484 @@
+ (array) servers :
+ * an array of memcached server ; each memcached server is described by an associative array :
+ * 'host' => (string) : the name of the memcached server
+ * 'port' => (int) : the port of the memcached server
+ * 'weight' => (int) : number of buckets to create for this server which in turn control its
+ * probability of it being selected. The probability is relative to the total
+ * weight of all servers.
+ * =====> (array) client :
+ * an array of memcached client options ; the memcached client is described by an associative array :
+ * @see http://php.net/manual/memcached.constants.php
+ * - The option name can be the name of the constant without the prefix 'OPT_'
+ * or the integer value of this option constant
+ *
+ * @var array available options
+ */
+ protected $_options = array(
+ 'servers' => array(array(
+ 'host' => self::DEFAULT_HOST,
+ 'port' => self::DEFAULT_PORT,
+ 'weight' => self::DEFAULT_WEIGHT,
+ )),
+ 'client' => array()
+ );
+
+ /**
+ * Memcached object
+ *
+ * @var mixed memcached object
+ */
+ protected $_memcache = null;
+
+ /**
+ * Constructor
+ *
+ * @param array $options associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ if (!extension_loaded('memcached')) {
+ Zend_Cache::throwException('The memcached extension must be loaded for using this backend !');
+ }
+
+ // override default client options
+ $this->_options['client'] = array(
+ Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
+ Memcached::OPT_HASH => Memcached::HASH_MD5,
+ Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
+ );
+
+ parent::__construct($options);
+
+ if (isset($this->_options['servers'])) {
+ $value = $this->_options['servers'];
+ if (isset($value['host'])) {
+ // in this case, $value seems to be a simple associative array (one server only)
+ $value = array(0 => $value); // let's transform it into a classical array of associative arrays
+ }
+ $this->setOption('servers', $value);
+ }
+ $this->_memcache = new Memcached;
+
+ // setup memcached client options
+ foreach ($this->_options['client'] as $name => $value) {
+ $optId = null;
+ if (is_int($name)) {
+ $optId = $name;
+ } else {
+ $optConst = 'Memcached::OPT_' . strtoupper($name);
+ if (defined($optConst)) {
+ $optId = constant($optConst);
+ } else {
+ $this->_log("Unknown memcached client option '{$name}' ({$optConst})");
+ }
+ }
+ if ($optId) {
+ if (!$this->_memcache->setOption($optId, $value)) {
+ $this->_log("Setting memcached client option '{$optId}' failed");
+ }
+ }
+ }
+
+ // setup memcached servers
+ $servers = array();
+ foreach ($this->_options['servers'] as $server) {
+ if (!array_key_exists('port', $server)) {
+ $server['port'] = self::DEFAULT_PORT;
+ }
+ if (!array_key_exists('weight', $server)) {
+ $server['weight'] = self::DEFAULT_WEIGHT;
+ }
+
+ $servers[] = array($server['host'], $server['port'], $server['weight']);
+ }
+ $this->_memcache->addServers($servers);
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return string|false cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ $tmp = $this->_memcache->get($id);
+ if (isset($tmp[0])) {
+ return $tmp[0];
+ }
+ return false;
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id Cache id
+ * @return int|false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ $tmp = $this->_memcache->get($id);
+ if (isset($tmp[0], $tmp[1])) {
+ return (int)$tmp[1];
+ }
+ return false;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data Datas to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean True if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $lifetime = $this->getLifetime($specificLifetime);
+
+ // ZF-8856: using set because add needs a second request if item already exists
+ $result = @$this->_memcache->set($id, array($data, time(), $lifetime), $lifetime);
+ if ($result === false) {
+ $rsCode = $this->_memcache->getResultCode();
+ $rsMsg = $this->_memcache->getResultMessage();
+ $this->_log("Memcached::set() failed: [{$rsCode}] {$rsMsg}");
+ }
+
+ if (count($tags) > 0) {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id)
+ {
+ return $this->_memcache->delete($id);
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => unsupported
+ * 'matchingTag' => unsupported
+ * 'notMatchingTag' => unsupported
+ * 'matchingAnyTag' => unsupported
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean True if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ return $this->_memcache->flush();
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $this->_log("Zend_Cache_Backend_Libmemcached::clean() : CLEANING_MODE_OLD is unsupported by the Libmemcached backend");
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_LIBMEMCACHED_BACKEND);
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+
+ /**
+ * Return true if the automatic cleaning is available for the backend
+ *
+ * @return boolean
+ */
+ public function isAutomaticCleaningAvailable()
+ {
+ return false;
+ }
+
+ /**
+ * Set the frontend directives
+ *
+ * @param array $directives Assoc of directives
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setDirectives($directives)
+ {
+ parent::setDirectives($directives);
+ $lifetime = $this->getLifetime(false);
+ if ($lifetime > 2592000) {
+ // #ZF-3490 : For the memcached backend, there is a lifetime limit of 30 days (2592000 seconds)
+ $this->_log('memcached backend has a limit of 30 days (2592000 seconds) for the lifetime');
+ }
+ if ($lifetime === null) {
+ // #ZF-4614 : we tranform null to zero to get the maximal lifetime
+ parent::setDirectives(array('lifetime' => 0));
+ }
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ $this->_log("Zend_Cache_Backend_Libmemcached::save() : getting the list of cache ids is unsupported by the Libmemcached backend");
+ return array();
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_LIBMEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @throws Zend_Cache_Exception
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ $mems = $this->_memcache->getStats();
+ if ($mems === false) {
+ return 0;
+ }
+
+ $memSize = null;
+ $memUsed = null;
+ foreach ($mems as $key => $mem) {
+ if ($mem === false) {
+ $this->_log('can\'t get stat from ' . $key);
+ continue;
+ }
+
+ $eachSize = $mem['limit_maxbytes'];
+ $eachUsed = $mem['bytes'];
+ if ($eachUsed > $eachSize) {
+ $eachUsed = $eachSize;
+ }
+
+ $memSize += $eachSize;
+ $memUsed += $eachUsed;
+ }
+
+ if ($memSize === null || $memUsed === null) {
+ Zend_Cache::throwException('Can\'t get filling percentage');
+ }
+
+ return ((int) (100. * ($memUsed / $memSize)));
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ $tmp = $this->_memcache->get($id);
+ if (isset($tmp[0], $tmp[1], $tmp[2])) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ $lifetime = $tmp[2];
+ return array(
+ 'expire' => $mtime + $lifetime,
+ 'tags' => array(),
+ 'mtime' => $mtime
+ );
+ }
+
+ return false;
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ $tmp = $this->_memcache->get($id);
+ if (isset($tmp[0], $tmp[1], $tmp[2])) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ $lifetime = $tmp[2];
+ $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime;
+ if ($newLifetime <=0) {
+ return false;
+ }
+ // #ZF-5702 : we try replace() first becase set() seems to be slower
+ if (!($result = $this->_memcache->replace($id, array($data, time(), $newLifetime), $newLifetime))) {
+ $result = $this->_memcache->set($id, array($data, time(), $newLifetime), $newLifetime);
+ if ($result === false) {
+ $rsCode = $this->_memcache->getResultCode();
+ $rsMsg = $this->_memcache->getResultMessage();
+ $this->_log("Memcached::set() failed: [{$rsCode}] {$rsMsg}");
+ }
+ }
+ return $result;
+ }
+ return false;
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => false,
+ 'tags' => false,
+ 'expired_read' => false,
+ 'priority' => false,
+ 'infinite_lifetime' => false,
+ 'get_list' => false
+ );
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/Memcached.php b/library/Zend/Cache/Backend/Memcached.php
new file mode 100644
index 0000000..f8e228f
--- /dev/null
+++ b/library/Zend/Cache/Backend/Memcached.php
@@ -0,0 +1,504 @@
+ (array) servers :
+ * an array of memcached server ; each memcached server is described by an associative array :
+ * 'host' => (string) : the name of the memcached server
+ * 'port' => (int) : the port of the memcached server
+ * 'persistent' => (bool) : use or not persistent connections to this memcached server
+ * 'weight' => (int) : number of buckets to create for this server which in turn control its
+ * probability of it being selected. The probability is relative to the total
+ * weight of all servers.
+ * 'timeout' => (int) : value in seconds which will be used for connecting to the daemon. Think twice
+ * before changing the default value of 1 second - you can lose all the
+ * advantages of caching if your connection is too slow.
+ * 'retry_interval' => (int) : controls how often a failed server will be retried, the default value
+ * is 15 seconds. Setting this parameter to -1 disables automatic retry.
+ * 'status' => (bool) : controls if the server should be flagged as online.
+ * 'failure_callback' => (callback) : Allows the user to specify a callback function to run upon
+ * encountering an error. The callback is run before failover
+ * is attempted. The function takes two parameters, the hostname
+ * and port of the failed server.
+ *
+ * =====> (boolean) compression :
+ * true if you want to use on-the-fly compression
+ *
+ * =====> (boolean) compatibility :
+ * true if you use old memcache server or extension
+ *
+ * @var array available options
+ */
+ protected $_options = array(
+ 'servers' => array(array(
+ 'host' => self::DEFAULT_HOST,
+ 'port' => self::DEFAULT_PORT,
+ 'persistent' => self::DEFAULT_PERSISTENT,
+ 'weight' => self::DEFAULT_WEIGHT,
+ 'timeout' => self::DEFAULT_TIMEOUT,
+ 'retry_interval' => self::DEFAULT_RETRY_INTERVAL,
+ 'status' => self::DEFAULT_STATUS,
+ 'failure_callback' => self::DEFAULT_FAILURE_CALLBACK
+ )),
+ 'compression' => false,
+ 'compatibility' => false,
+ );
+
+ /**
+ * Memcache object
+ *
+ * @var mixed memcache object
+ */
+ protected $_memcache = null;
+
+ /**
+ * Constructor
+ *
+ * @param array $options associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ if (!extension_loaded('memcache')) {
+ Zend_Cache::throwException('The memcache extension must be loaded for using this backend !');
+ }
+ parent::__construct($options);
+ if (isset($this->_options['servers'])) {
+ $value= $this->_options['servers'];
+ if (isset($value['host'])) {
+ // in this case, $value seems to be a simple associative array (one server only)
+ $value = array(0 => $value); // let's transform it into a classical array of associative arrays
+ }
+ $this->setOption('servers', $value);
+ }
+ $this->_memcache = new Memcache;
+ foreach ($this->_options['servers'] as $server) {
+ if (!array_key_exists('port', $server)) {
+ $server['port'] = self::DEFAULT_PORT;
+ }
+ if (!array_key_exists('persistent', $server)) {
+ $server['persistent'] = self::DEFAULT_PERSISTENT;
+ }
+ if (!array_key_exists('weight', $server)) {
+ $server['weight'] = self::DEFAULT_WEIGHT;
+ }
+ if (!array_key_exists('timeout', $server)) {
+ $server['timeout'] = self::DEFAULT_TIMEOUT;
+ }
+ if (!array_key_exists('retry_interval', $server)) {
+ $server['retry_interval'] = self::DEFAULT_RETRY_INTERVAL;
+ }
+ if (!array_key_exists('status', $server)) {
+ $server['status'] = self::DEFAULT_STATUS;
+ }
+ if (!array_key_exists('failure_callback', $server)) {
+ $server['failure_callback'] = self::DEFAULT_FAILURE_CALLBACK;
+ }
+ if ($this->_options['compatibility']) {
+ // No status for compatibility mode (#ZF-5887)
+ $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'],
+ $server['weight'], $server['timeout'],
+ $server['retry_interval']);
+ } else {
+ $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'],
+ $server['weight'], $server['timeout'],
+ $server['retry_interval'],
+ $server['status'], $server['failure_callback']);
+ }
+ }
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return string|false cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ $tmp = $this->_memcache->get($id);
+ if (is_array($tmp) && isset($tmp[0])) {
+ return $tmp[0];
+ }
+ return false;
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id Cache id
+ * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ $tmp = $this->_memcache->get($id);
+ if (is_array($tmp)) {
+ return $tmp[1];
+ }
+ return false;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data Datas to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean True if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $lifetime = $this->getLifetime($specificLifetime);
+ if ($this->_options['compression']) {
+ $flag = MEMCACHE_COMPRESSED;
+ } else {
+ $flag = 0;
+ }
+
+ // ZF-8856: using set because add needs a second request if item already exists
+ $result = @$this->_memcache->set($id, array($data, time(), $lifetime), $flag, $lifetime);
+
+ if (count($tags) > 0) {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id)
+ {
+ return $this->_memcache->delete($id, 0);
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => unsupported
+ * 'matchingTag' => unsupported
+ * 'notMatchingTag' => unsupported
+ * 'matchingAnyTag' => unsupported
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean True if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ return $this->_memcache->flush();
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $this->_log("Zend_Cache_Backend_Memcached::clean() : CLEANING_MODE_OLD is unsupported by the Memcached backend");
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_MEMCACHED_BACKEND);
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+
+ /**
+ * Return true if the automatic cleaning is available for the backend
+ *
+ * @return boolean
+ */
+ public function isAutomaticCleaningAvailable()
+ {
+ return false;
+ }
+
+ /**
+ * Set the frontend directives
+ *
+ * @param array $directives Assoc of directives
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setDirectives($directives)
+ {
+ parent::setDirectives($directives);
+ $lifetime = $this->getLifetime(false);
+ if ($lifetime > 2592000) {
+ // #ZF-3490 : For the memcached backend, there is a lifetime limit of 30 days (2592000 seconds)
+ $this->_log('memcached backend has a limit of 30 days (2592000 seconds) for the lifetime');
+ }
+ if ($lifetime === null) {
+ // #ZF-4614 : we tranform null to zero to get the maximal lifetime
+ parent::setDirectives(array('lifetime' => 0));
+ }
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ $this->_log("Zend_Cache_Backend_Memcached::save() : getting the list of cache ids is unsupported by the Memcache backend");
+ return array();
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_MEMCACHED_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @throws Zend_Cache_Exception
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ $mems = $this->_memcache->getExtendedStats();
+
+ $memSize = null;
+ $memUsed = null;
+ foreach ($mems as $key => $mem) {
+ if ($mem === false) {
+ $this->_log('can\'t get stat from ' . $key);
+ continue;
+ }
+
+ $eachSize = $mem['limit_maxbytes'];
+ $eachUsed = $mem['bytes'];
+ if ($eachUsed > $eachSize) {
+ $eachUsed = $eachSize;
+ }
+
+ $memSize += $eachSize;
+ $memUsed += $eachUsed;
+ }
+
+ if ($memSize === null || $memUsed === null) {
+ Zend_Cache::throwException('Can\'t get filling percentage');
+ }
+
+ return ((int) (100. * ($memUsed / $memSize)));
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ $tmp = $this->_memcache->get($id);
+ if (is_array($tmp)) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ if (!isset($tmp[2])) {
+ // because this record is only with 1.7 release
+ // if old cache records are still there...
+ return false;
+ }
+ $lifetime = $tmp[2];
+ return array(
+ 'expire' => $mtime + $lifetime,
+ 'tags' => array(),
+ 'mtime' => $mtime
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ if ($this->_options['compression']) {
+ $flag = MEMCACHE_COMPRESSED;
+ } else {
+ $flag = 0;
+ }
+ $tmp = $this->_memcache->get($id);
+ if (is_array($tmp)) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ if (!isset($tmp[2])) {
+ // because this record is only with 1.7 release
+ // if old cache records are still there...
+ return false;
+ }
+ $lifetime = $tmp[2];
+ $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime;
+ if ($newLifetime <=0) {
+ return false;
+ }
+ // #ZF-5702 : we try replace() first becase set() seems to be slower
+ if (!($result = $this->_memcache->replace($id, array($data, time(), $newLifetime), $flag, $newLifetime))) {
+ $result = $this->_memcache->set($id, array($data, time(), $newLifetime), $flag, $newLifetime);
+ }
+ return $result;
+ }
+ return false;
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => false,
+ 'tags' => false,
+ 'expired_read' => false,
+ 'priority' => false,
+ 'infinite_lifetime' => false,
+ 'get_list' => false
+ );
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/Sqlite.php b/library/Zend/Cache/Backend/Sqlite.php
new file mode 100644
index 0000000..5b964a1
--- /dev/null
+++ b/library/Zend/Cache/Backend/Sqlite.php
@@ -0,0 +1,679 @@
+ (string) cache_db_complete_path :
+ * - the complete path (filename included) of the SQLITE database
+ *
+ * ====> (int) automatic_vacuum_factor :
+ * - Disable / Tune the automatic vacuum process
+ * - The automatic vacuum process defragment the database file (and make it smaller)
+ * when a clean() or delete() is called
+ * 0 => no automatic vacuum
+ * 1 => systematic vacuum (when delete() or clean() methods are called)
+ * x (integer) > 1 => automatic vacuum randomly 1 times on x clean() or delete()
+ *
+ * @var array Available options
+ */
+ protected $_options = array(
+ 'cache_db_complete_path' => null,
+ 'automatic_vacuum_factor' => 10
+ );
+
+ /**
+ * DB ressource
+ *
+ * @var mixed $_db
+ */
+ private $_db = null;
+
+ /**
+ * Boolean to store if the structure has benn checked or not
+ *
+ * @var boolean $_structureChecked
+ */
+ private $_structureChecked = false;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Associative array of options
+ * @throws Zend_cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ parent::__construct($options);
+ if ($this->_options['cache_db_complete_path'] === null) {
+ Zend_Cache::throwException('cache_db_complete_path option has to set');
+ }
+ if (!extension_loaded('sqlite')) {
+ Zend_Cache::throwException("Cannot use SQLite storage because the 'sqlite' extension is not loaded in the current PHP environment");
+ }
+ $this->_getConnection();
+ }
+
+ /**
+ * Destructor
+ *
+ * @return void
+ */
+ public function __destruct()
+ {
+ @sqlite_close($this->_getConnection());
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return string|false Cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ $this->_checkAndBuildStructure();
+ $sql = "SELECT content FROM cache WHERE id='$id'";
+ if (!$doNotTestCacheValidity) {
+ $sql = $sql . " AND (expire=0 OR expire>" . time() . ')';
+ }
+ $result = $this->_query($sql);
+ $row = @sqlite_fetch_array($result);
+ if ($row) {
+ return $row['content'];
+ }
+ return false;
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id Cache id
+ * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ $this->_checkAndBuildStructure();
+ $sql = "SELECT lastModified FROM cache WHERE id='$id' AND (expire=0 OR expire>" . time() . ')';
+ $result = $this->_query($sql);
+ $row = @sqlite_fetch_array($result);
+ if ($row) {
+ return ((int) $row['lastModified']);
+ }
+ return false;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data Datas to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @throws Zend_Cache_Exception
+ * @return boolean True if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $this->_checkAndBuildStructure();
+ $lifetime = $this->getLifetime($specificLifetime);
+ $data = @sqlite_escape_string($data);
+ $mktime = time();
+ if ($lifetime === null) {
+ $expire = 0;
+ } else {
+ $expire = $mktime + $lifetime;
+ }
+ $this->_query("DELETE FROM cache WHERE id='$id'");
+ $sql = "INSERT INTO cache (id, content, lastModified, expire) VALUES ('$id', '$data', $mktime, $expire)";
+ $res = $this->_query($sql);
+ if (!$res) {
+ $this->_log("Zend_Cache_Backend_Sqlite::save() : impossible to store the cache id=$id");
+ return false;
+ }
+ $res = true;
+ foreach ($tags as $tag) {
+ $res = $this->_registerTag($id, $tag) && $res;
+ }
+ return $res;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id)
+ {
+ $this->_checkAndBuildStructure();
+ $res = $this->_query("SELECT COUNT(*) AS nbr FROM cache WHERE id='$id'");
+ $result1 = @sqlite_fetch_single($res);
+ $result2 = $this->_query("DELETE FROM cache WHERE id='$id'");
+ $result3 = $this->_query("DELETE FROM tag WHERE id='$id'");
+ $this->_automaticVacuum();
+ return ($result1 && $result2 && $result3);
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @return boolean True if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ $this->_checkAndBuildStructure();
+ $return = $this->_clean($mode, $tags);
+ $this->_automaticVacuum();
+ return $return;
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ $this->_checkAndBuildStructure();
+ $res = $this->_query("SELECT id FROM cache WHERE (expire=0 OR expire>" . time() . ")");
+ $result = array();
+ while ($id = @sqlite_fetch_single($res)) {
+ $result[] = $id;
+ }
+ return $result;
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ $this->_checkAndBuildStructure();
+ $res = $this->_query("SELECT DISTINCT(name) AS name FROM tag");
+ $result = array();
+ while ($id = @sqlite_fetch_single($res)) {
+ $result[] = $id;
+ }
+ return $result;
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ $first = true;
+ $ids = array();
+ foreach ($tags as $tag) {
+ $res = $this->_query("SELECT DISTINCT(id) AS id FROM tag WHERE name='$tag'");
+ if (!$res) {
+ return array();
+ }
+ $rows = @sqlite_fetch_all($res, SQLITE_ASSOC);
+ $ids2 = array();
+ foreach ($rows as $row) {
+ $ids2[] = $row['id'];
+ }
+ if ($first) {
+ $ids = $ids2;
+ $first = false;
+ } else {
+ $ids = array_intersect($ids, $ids2);
+ }
+ }
+ $result = array();
+ foreach ($ids as $id) {
+ $result[] = $id;
+ }
+ return $result;
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ $res = $this->_query("SELECT id FROM cache");
+ $rows = @sqlite_fetch_all($res, SQLITE_ASSOC);
+ $result = array();
+ foreach ($rows as $row) {
+ $id = $row['id'];
+ $matching = false;
+ foreach ($tags as $tag) {
+ $res = $this->_query("SELECT COUNT(*) AS nbr FROM tag WHERE name='$tag' AND id='$id'");
+ if (!$res) {
+ return array();
+ }
+ $nbr = (int) @sqlite_fetch_single($res);
+ if ($nbr > 0) {
+ $matching = true;
+ }
+ }
+ if (!$matching) {
+ $result[] = $id;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ $first = true;
+ $ids = array();
+ foreach ($tags as $tag) {
+ $res = $this->_query("SELECT DISTINCT(id) AS id FROM tag WHERE name='$tag'");
+ if (!$res) {
+ return array();
+ }
+ $rows = @sqlite_fetch_all($res, SQLITE_ASSOC);
+ $ids2 = array();
+ foreach ($rows as $row) {
+ $ids2[] = $row['id'];
+ }
+ if ($first) {
+ $ids = $ids2;
+ $first = false;
+ } else {
+ $ids = array_merge($ids, $ids2);
+ }
+ }
+ $result = array();
+ foreach ($ids as $id) {
+ $result[] = $id;
+ }
+ return $result;
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @throws Zend_Cache_Exception
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ $dir = dirname($this->_options['cache_db_complete_path']);
+ $free = disk_free_space($dir);
+ $total = disk_total_space($dir);
+ if ($total == 0) {
+ Zend_Cache::throwException('can\'t get disk_total_space');
+ } else {
+ if ($free >= $total) {
+ return 100;
+ }
+ return ((int) (100. * ($total - $free) / $total));
+ }
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ $tags = array();
+ $res = $this->_query("SELECT name FROM tag WHERE id='$id'");
+ if ($res) {
+ $rows = @sqlite_fetch_all($res, SQLITE_ASSOC);
+ foreach ($rows as $row) {
+ $tags[] = $row['name'];
+ }
+ }
+ $this->_query('CREATE TABLE cache (id TEXT PRIMARY KEY, content BLOB, lastModified INTEGER, expire INTEGER)');
+ $res = $this->_query("SELECT lastModified,expire FROM cache WHERE id='$id'");
+ if (!$res) {
+ return false;
+ }
+ $row = @sqlite_fetch_array($res, SQLITE_ASSOC);
+ return array(
+ 'tags' => $tags,
+ 'mtime' => $row['lastModified'],
+ 'expire' => $row['expire']
+ );
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ $sql = "SELECT expire FROM cache WHERE id='$id' AND (expire=0 OR expire>" . time() . ')';
+ $res = $this->_query($sql);
+ if (!$res) {
+ return false;
+ }
+ $expire = @sqlite_fetch_single($res);
+ $newExpire = $expire + $extraLifetime;
+ $res = $this->_query("UPDATE cache SET lastModified=" . time() . ", expire=$newExpire WHERE id='$id'");
+ if ($res) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => true,
+ 'tags' => true,
+ 'expired_read' => true,
+ 'priority' => false,
+ 'infinite_lifetime' => true,
+ 'get_list' => true
+ );
+ }
+
+ /**
+ * PUBLIC METHOD FOR UNIT TESTING ONLY !
+ *
+ * Force a cache record to expire
+ *
+ * @param string $id Cache id
+ */
+ public function ___expire($id)
+ {
+ $time = time() - 1;
+ $this->_query("UPDATE cache SET lastModified=$time, expire=$time WHERE id='$id'");
+ }
+
+ /**
+ * Return the connection resource
+ *
+ * If we are not connected, the connection is made
+ *
+ * @throws Zend_Cache_Exception
+ * @return resource Connection resource
+ */
+ private function _getConnection()
+ {
+ if (is_resource($this->_db)) {
+ return $this->_db;
+ } else {
+ $this->_db = @sqlite_open($this->_options['cache_db_complete_path']);
+ if (!(is_resource($this->_db))) {
+ Zend_Cache::throwException("Impossible to open " . $this->_options['cache_db_complete_path'] . " cache DB file");
+ }
+ return $this->_db;
+ }
+ }
+
+ /**
+ * Execute an SQL query silently
+ *
+ * @param string $query SQL query
+ * @return mixed|false query results
+ */
+ private function _query($query)
+ {
+ $db = $this->_getConnection();
+ if (is_resource($db)) {
+ $res = @sqlite_query($db, $query);
+ if ($res === false) {
+ return false;
+ } else {
+ return $res;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Deal with the automatic vacuum process
+ *
+ * @return void
+ */
+ private function _automaticVacuum()
+ {
+ if ($this->_options['automatic_vacuum_factor'] > 0) {
+ $rand = rand(1, $this->_options['automatic_vacuum_factor']);
+ if ($rand == 1) {
+ $this->_query('VACUUM');
+ @sqlite_close($this->_getConnection());
+ }
+ }
+ }
+
+ /**
+ * Register a cache id with the given tag
+ *
+ * @param string $id Cache id
+ * @param string $tag Tag
+ * @return boolean True if no problem
+ */
+ private function _registerTag($id, $tag) {
+ $res = $this->_query("DELETE FROM TAG WHERE name='$tag' AND id='$id'");
+ $res = $this->_query("INSERT INTO tag (name, id) VALUES ('$tag', '$id')");
+ if (!$res) {
+ $this->_log("Zend_Cache_Backend_Sqlite::_registerTag() : impossible to register tag=$tag on id=$id");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Build the database structure
+ *
+ * @return false
+ */
+ private function _buildStructure()
+ {
+ $this->_query('DROP INDEX tag_id_index');
+ $this->_query('DROP INDEX tag_name_index');
+ $this->_query('DROP INDEX cache_id_expire_index');
+ $this->_query('DROP TABLE version');
+ $this->_query('DROP TABLE cache');
+ $this->_query('DROP TABLE tag');
+ $this->_query('CREATE TABLE version (num INTEGER PRIMARY KEY)');
+ $this->_query('CREATE TABLE cache (id TEXT PRIMARY KEY, content BLOB, lastModified INTEGER, expire INTEGER)');
+ $this->_query('CREATE TABLE tag (name TEXT, id TEXT)');
+ $this->_query('CREATE INDEX tag_id_index ON tag(id)');
+ $this->_query('CREATE INDEX tag_name_index ON tag(name)');
+ $this->_query('CREATE INDEX cache_id_expire_index ON cache(id, expire)');
+ $this->_query('INSERT INTO version (num) VALUES (1)');
+ }
+
+ /**
+ * Check if the database structure is ok (with the good version)
+ *
+ * @return boolean True if ok
+ */
+ private function _checkStructureVersion()
+ {
+ $result = $this->_query("SELECT num FROM version");
+ if (!$result) return false;
+ $row = @sqlite_fetch_array($result);
+ if (!$row) {
+ return false;
+ }
+ if (((int) $row['num']) != 1) {
+ // old cache structure
+ $this->_log('Zend_Cache_Backend_Sqlite::_checkStructureVersion() : old cache structure version detected => the cache is going to be dropped');
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @return boolean True if no problem
+ */
+ private function _clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ $res1 = $this->_query('DELETE FROM cache');
+ $res2 = $this->_query('DELETE FROM tag');
+ return $res1 && $res2;
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $mktime = time();
+ $res1 = $this->_query("DELETE FROM tag WHERE id IN (SELECT id FROM cache WHERE expire>0 AND expire<=$mktime)");
+ $res2 = $this->_query("DELETE FROM cache WHERE expire>0 AND expire<=$mktime");
+ return $res1 && $res2;
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ $ids = $this->getIdsMatchingTags($tags);
+ $result = true;
+ foreach ($ids as $id) {
+ $result = $this->remove($id) && $result;
+ }
+ return $result;
+ break;
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ $ids = $this->getIdsNotMatchingTags($tags);
+ $result = true;
+ foreach ($ids as $id) {
+ $result = $this->remove($id) && $result;
+ }
+ return $result;
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $ids = $this->getIdsMatchingAnyTags($tags);
+ $result = true;
+ foreach ($ids as $id) {
+ $result = $this->remove($id) && $result;
+ }
+ return $result;
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Check if the database structure is ok (with the good version), if no : build it
+ *
+ * @throws Zend_Cache_Exception
+ * @return boolean True if ok
+ */
+ private function _checkAndBuildStructure()
+ {
+ if (!($this->_structureChecked)) {
+ if (!$this->_checkStructureVersion()) {
+ $this->_buildStructure();
+ if (!$this->_checkStructureVersion()) {
+ Zend_Cache::throwException("Impossible to build cache structure in " . $this->_options['cache_db_complete_path']);
+ }
+ }
+ $this->_structureChecked = true;
+ }
+ return true;
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/Static.php b/library/Zend/Cache/Backend/Static.php
new file mode 100644
index 0000000..6b80f6b
--- /dev/null
+++ b/library/Zend/Cache/Backend/Static.php
@@ -0,0 +1,564 @@
+ null,
+ 'sub_dir' => 'html',
+ 'file_extension' => '.html',
+ 'index_filename' => 'index',
+ 'file_locking' => true,
+ 'cache_file_umask' => 0600,
+ 'cache_directory_umask' => 0700,
+ 'debug_header' => false,
+ 'tag_cache' => null,
+ 'disable_caching' => false
+ );
+
+ /**
+ * Cache for handling tags
+ * @var Zend_Cache_Core
+ */
+ protected $_tagCache = null;
+
+ /**
+ * Tagged items
+ * @var array
+ */
+ protected $_tagged = null;
+
+ /**
+ * Interceptor child method to handle the case where an Inner
+ * Cache object is being set since it's not supported by the
+ * standard backend interface
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return Zend_Cache_Backend_Static
+ */
+ public function setOption($name, $value)
+ {
+ if ($name == 'tag_cache') {
+ $this->setInnerCache($value);
+ } else {
+ parent::setOption($name, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve any option via interception of the parent's statically held
+ * options including the local option for a tag cache.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function getOption($name)
+ {
+ if ($name == 'tag_cache') {
+ return $this->getInnerCache();
+ } else {
+ if (in_array($name, $this->_options)) {
+ return $this->_options[$name];
+ }
+ if ($name == 'lifetime') {
+ return parent::getLifetime();
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * Note : return value is always "string" (unserialization is done by the core not by the backend)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return string|false cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ if (($id = (string)$id) === '') {
+ $id = $this->_detectId();
+ } else {
+ $id = $this->_decodeId($id);
+ }
+ if (!$this->_verifyPath($id)) {
+ Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+ }
+ if ($doNotTestCacheValidity) {
+ $this->_log("Zend_Cache_Backend_Static::load() : \$doNotTestCacheValidity=true is unsupported by the Static backend");
+ }
+
+ $fileName = basename($id);
+ if ($fileName === '') {
+ $fileName = $this->_options['index_filename'];
+ }
+ $pathName = $this->_options['public_dir'] . dirname($id);
+ $file = rtrim($pathName, '/') . '/' . $fileName . $this->_options['file_extension'];
+ if (file_exists($file)) {
+ $content = file_get_contents($file);
+ return $content;
+ }
+
+ return false;
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id cache id
+ * @return bool
+ */
+ public function test($id)
+ {
+ $id = $this->_decodeId($id);
+ if (!$this->_verifyPath($id)) {
+ Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+ }
+
+ $fileName = basename($id);
+ if ($fileName === '') {
+ $fileName = $this->_options['index_filename'];
+ }
+ if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
+ $this->_tagged = $tagged;
+ } elseif (!$this->_tagged) {
+ return false;
+ }
+ $pathName = $this->_options['public_dir'] . dirname($id);
+
+ // Switch extension if needed
+ if (isset($this->_tagged[$id])) {
+ $extension = $this->_tagged[$id]['extension'];
+ } else {
+ $extension = $this->_options['file_extension'];
+ }
+ $file = $pathName . '/' . $fileName . $extension;
+ if (file_exists($file)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data Datas to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ if ($this->_options['disable_caching']) {
+ return true;
+ }
+ $extension = null;
+ if ($this->_isSerialized($data)) {
+ $data = unserialize($data);
+ $extension = '.' . ltrim($data[1], '.');
+ $data = $data[0];
+ }
+
+ clearstatcache();
+ if (($id = (string)$id) === '') {
+ $id = $this->_detectId();
+ } else {
+ $id = $this->_decodeId($id);
+ }
+
+ $fileName = basename($id);
+ if ($fileName === '') {
+ $fileName = $this->_options['index_filename'];
+ }
+
+ $pathName = realpath($this->_options['public_dir']) . dirname($id);
+ $this->_createDirectoriesFor($pathName);
+
+ if ($id === null || strlen($id) == 0) {
+ $dataUnserialized = unserialize($data);
+ $data = $dataUnserialized['data'];
+ }
+ $ext = $this->_options['file_extension'];
+ if ($extension) $ext = $extension;
+ $file = rtrim($pathName, '/') . '/' . $fileName . $ext;
+ if ($this->_options['file_locking']) {
+ $result = file_put_contents($file, $data, LOCK_EX);
+ } else {
+ $result = file_put_contents($file, $data);
+ }
+ @chmod($file, $this->_octdec($this->_options['cache_file_umask']));
+
+ if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
+ $this->_tagged = $tagged;
+ } elseif ($this->_tagged === null) {
+ $this->_tagged = array();
+ }
+ if (!isset($this->_tagged[$id])) {
+ $this->_tagged[$id] = array();
+ }
+ if (!isset($this->_tagged[$id]['tags'])) {
+ $this->_tagged[$id]['tags'] = array();
+ }
+ $this->_tagged[$id]['tags'] = array_unique(array_merge($this->_tagged[$id]['tags'], $tags));
+ $this->_tagged[$id]['extension'] = $ext;
+ $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+ return (bool) $result;
+ }
+
+ /**
+ * Recursively create the directories needed to write the static file
+ */
+ protected function _createDirectoriesFor($path)
+ {
+ if (!is_dir($path)) {
+ $oldUmask = umask(0);
+ if ( !@mkdir($path, $this->_octdec($this->_options['cache_directory_umask']), true)) {
+ $lastErr = error_get_last();
+ umask($oldUmask);
+ Zend_Cache::throwException("Can't create directory: {$lastErr['message']}");
+ }
+ umask($oldUmask);
+ }
+ }
+
+ /**
+ * Detect serialization of data (cannot predict since this is the only way
+ * to obey the interface yet pass in another parameter).
+ *
+ * In future, ZF 2.0, check if we can just avoid the interface restraints.
+ *
+ * This format is the only valid one possible for the class, so it's simple
+ * to just run a regular expression for the starting serialized format.
+ */
+ protected function _isSerialized($data)
+ {
+ return preg_match("/a:2:\{i:0;s:\d+:\"/", $data);
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id)
+ {
+ if (!$this->_verifyPath($id)) {
+ Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+ }
+ $fileName = basename($id);
+ if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
+ $this->_tagged = $tagged;
+ } elseif (!$this->_tagged) {
+ return false;
+ }
+ if (isset($this->_tagged[$id])) {
+ $extension = $this->_tagged[$id]['extension'];
+ } else {
+ $extension = $this->_options['file_extension'];
+ }
+ if ($fileName === '') {
+ $fileName = $this->_options['index_filename'];
+ }
+ $pathName = $this->_options['public_dir'] . dirname($id);
+ $file = realpath($pathName) . '/' . $fileName . $extension;
+ if (!file_exists($file)) {
+ return false;
+ }
+ return unlink($file);
+ }
+
+ /**
+ * Remove a cache record recursively for the given directory matching a
+ * REQUEST_URI based relative path (deletes the actual file matching this
+ * in addition to the matching directory)
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function removeRecursively($id)
+ {
+ if (!$this->_verifyPath($id)) {
+ Zend_Cache::throwException('Invalid cache id: does not match expected public_dir path');
+ }
+ $fileName = basename($id);
+ if ($fileName === '') {
+ $fileName = $this->_options['index_filename'];
+ }
+ $pathName = $this->_options['public_dir'] . dirname($id);
+ $file = $pathName . '/' . $fileName . $this->_options['file_extension'];
+ $directory = $pathName . '/' . $fileName;
+ if (file_exists($directory)) {
+ if (!is_writable($directory)) {
+ return false;
+ }
+ if (is_dir($directory)) {
+ foreach (new DirectoryIterator($directory) as $file) {
+ if (true === $file->isFile()) {
+ if (false === unlink($file->getPathName())) {
+ return false;
+ }
+ }
+ }
+ }
+ rmdir($directory);
+ }
+ if (file_exists($file)) {
+ if (!is_writable($file)) {
+ return false;
+ }
+ return unlink($file);
+ }
+ return true;
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ $result = false;
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ if (empty($tags)) {
+ throw new Zend_Exception('Cannot use tag matching modes as no tags were defined');
+ }
+ if ($this->_tagged === null && $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME)) {
+ $this->_tagged = $tagged;
+ } elseif (!$this->_tagged) {
+ return true;
+ }
+ foreach ($tags as $tag) {
+ $urls = array_keys($this->_tagged);
+ foreach ($urls as $url) {
+ if (isset($this->_tagged[$url]['tags']) && in_array($tag, $this->_tagged[$url]['tags'])) {
+ $this->remove($url);
+ unset($this->_tagged[$url]);
+ }
+ }
+ }
+ $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+ $result = true;
+ break;
+ case Zend_Cache::CLEANING_MODE_ALL:
+ if ($this->_tagged === null) {
+ $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME);
+ $this->_tagged = $tagged;
+ }
+ if ($this->_tagged === null || empty($this->_tagged)) {
+ return true;
+ }
+ $urls = array_keys($this->_tagged);
+ foreach ($urls as $url) {
+ $this->remove($url);
+ unset($this->_tagged[$url]);
+ }
+ $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+ $result = true;
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $this->_log("Zend_Cache_Backend_Static : Selected Cleaning Mode Currently Unsupported By This Backend");
+ break;
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ if (empty($tags)) {
+ throw new Zend_Exception('Cannot use tag matching modes as no tags were defined');
+ }
+ if ($this->_tagged === null) {
+ $tagged = $this->getInnerCache()->load(self::INNER_CACHE_NAME);
+ $this->_tagged = $tagged;
+ }
+ if ($this->_tagged === null || empty($this->_tagged)) {
+ return true;
+ }
+ $urls = array_keys($this->_tagged);
+ foreach ($urls as $url) {
+ $difference = array_diff($tags, $this->_tagged[$url]['tags']);
+ if (count($tags) == count($difference)) {
+ $this->remove($url);
+ unset($this->_tagged[$url]);
+ }
+ }
+ $this->getInnerCache()->save($this->_tagged, self::INNER_CACHE_NAME);
+ $result = true;
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ return $result;
+ }
+
+ /**
+ * Set an Inner Cache, used here primarily to store Tags associated
+ * with caches created by this backend. Note: If Tags are lost, the cache
+ * should be completely cleaned as the mapping of tags to caches will
+ * have been irrevocably lost.
+ *
+ * @param Zend_Cache_Core
+ * @return void
+ */
+ public function setInnerCache(Zend_Cache_Core $cache)
+ {
+ $this->_tagCache = $cache;
+ $this->_options['tag_cache'] = $cache;
+ }
+
+ /**
+ * Get the Inner Cache if set
+ *
+ * @return Zend_Cache_Core
+ */
+ public function getInnerCache()
+ {
+ if ($this->_tagCache === null) {
+ Zend_Cache::throwException('An Inner Cache has not been set; use setInnerCache()');
+ }
+ return $this->_tagCache;
+ }
+
+ /**
+ * Verify path exists and is non-empty
+ *
+ * @param string $path
+ * @return bool
+ */
+ protected function _verifyPath($path)
+ {
+ $path = realpath($path);
+ $base = realpath($this->_options['public_dir']);
+ return strncmp($path, $base, strlen($base)) !== 0;
+ }
+
+ /**
+ * Determine the page to save from the request
+ *
+ * @return string
+ */
+ protected function _detectId()
+ {
+ return $_SERVER['REQUEST_URI'];
+ }
+
+ /**
+ * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
+ *
+ * Throw an exception if a problem is found
+ *
+ * @param string $string Cache id or tag
+ * @throws Zend_Cache_Exception
+ * @return void
+ * @deprecated Not usable until perhaps ZF 2.0
+ */
+ protected static function _validateIdOrTag($string)
+ {
+ if (!is_string($string)) {
+ Zend_Cache::throwException('Invalid id or tag : must be a string');
+ }
+
+ // Internal only checked in Frontend - not here!
+ if (substr($string, 0, 9) == 'internal-') {
+ return;
+ }
+
+ // Validation assumes no query string, fragments or scheme included - only the path
+ if (!preg_match(
+ '/^(?:\/(?:(?:%[[:xdigit:]]{2}|[A-Za-z0-9-_.!~*\'()\[\]:@&=+$,;])*)?)+$/',
+ $string
+ )
+ ) {
+ Zend_Cache::throwException("Invalid id or tag '$string' : must be a valid URL path");
+ }
+ }
+
+ /**
+ * Detect an octal string and return its octal value for file permission ops
+ * otherwise return the non-string (assumed octal or decimal int already)
+ *
+ * @param string $val The potential octal in need of conversion
+ * @return int
+ */
+ protected function _octdec($val)
+ {
+ if (is_string($val) && decoct(octdec($val)) == $val) {
+ return octdec($val);
+ }
+ return $val;
+ }
+
+ /**
+ * Decode a request URI from the provided ID
+ *
+ * @param string $id
+ * @return string
+ */
+ protected function _decodeId($id)
+ {
+ return pack('H*', $id);
+ }
+}
diff --git a/library/Zend/Cache/Backend/Test.php b/library/Zend/Cache/Backend/Test.php
new file mode 100644
index 0000000..d009ea0
--- /dev/null
+++ b/library/Zend/Cache/Backend/Test.php
@@ -0,0 +1,413 @@
+_addLog('construct', array($options));
+ }
+
+ /**
+ * Set the frontend directives
+ *
+ * @param array $directives assoc of directives
+ * @return void
+ */
+ public function setDirectives($directives)
+ {
+ $this->_addLog('setDirectives', array($directives));
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * For this test backend only, if $id == 'false', then the method will return false
+ * if $id == 'serialized', the method will return a serialized array
+ * ('foo' else)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return string Cached datas (or false)
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ $this->_addLog('get', array($id, $doNotTestCacheValidity));
+
+ if ( $id == 'false'
+ || $id == 'd8523b3ee441006261eeffa5c3d3a0a7'
+ || $id == 'e83249ea22178277d5befc2c5e2e9ace'
+ || $id == '40f649b94977c0a6e76902e2a0b43587'
+ || $id == '88161989b73a4cbfd0b701c446115a99'
+ || $id == '205fc79cba24f0f0018eb92c7c8b3ba4'
+ || $id == '170720e35f38150b811f68a937fb042d')
+ {
+ return false;
+ }
+ if ($id=='serialized') {
+ return serialize(array('foo'));
+ }
+ if ($id=='serialized2') {
+ return serialize(array('headers' => array(), 'data' => 'foo'));
+ }
+ if ( $id == '71769f39054f75894288e397df04e445' || $id == '615d222619fb20b527168340cebd0578'
+ || $id == '8a02d218a5165c467e7a5747cc6bd4b6' || $id == '648aca1366211d17cbf48e65dc570bee'
+ || $id == '4a923ef02d7f997ca14d56dfeae25ea7') {
+ return serialize(array('foo', 'bar'));
+ }
+ return 'foo';
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * For this test backend only, if $id == 'false', then the method will return false
+ * (123456 else)
+ *
+ * @param string $id Cache id
+ * @return mixed|false false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ $this->_addLog('test', array($id));
+ if ($id=='false') {
+ return false;
+ }
+ if (($id=='3c439c922209e2cb0b54d6deffccd75a')) {
+ return false;
+ }
+ return 123456;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * For this test backend only, if $id == 'false', then the method will return false
+ * (true else)
+ *
+ * @param string $data Datas to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean True if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $this->_addLog('save', array($data, $id, $tags));
+ if (substr($id,-5)=='false') {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * For this test backend only, if $id == 'false', then the method will return false
+ * (true else)
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id)
+ {
+ $this->_addLog('remove', array($id));
+ if (substr($id,-5)=='false') {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * For this test backend only, if $mode == 'false', then the method will return false
+ * (true else)
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @return boolean True if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ $this->_addLog('clean', array($mode, $tags));
+ if ($mode=='false') {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the last log
+ *
+ * @return string The last log
+ */
+ public function getLastLog()
+ {
+ return $this->_log[$this->_index - 1];
+ }
+
+ /**
+ * Get the log index
+ *
+ * @return int Log index
+ */
+ public function getLogIndex()
+ {
+ return $this->_index;
+ }
+
+ /**
+ * Get the complete log array
+ *
+ * @return array Complete log array
+ */
+ public function getAllLogs()
+ {
+ return $this->_log;
+ }
+
+ /**
+ * Return true if the automatic cleaning is available for the backend
+ *
+ * @return boolean
+ */
+ public function isAutomaticCleaningAvailable()
+ {
+ return true;
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ return array(
+ 'prefix_id1', 'prefix_id2'
+ );
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ return array(
+ 'tag1', 'tag2'
+ );
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ if ($tags == array('tag1', 'tag2')) {
+ return array('prefix_id1', 'prefix_id2');
+ }
+
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ if ($tags == array('tag3', 'tag4')) {
+ return array('prefix_id3', 'prefix_id4');
+ }
+
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ if ($tags == array('tag5', 'tag6')) {
+ return array('prefix_id5', 'prefix_id6');
+ }
+
+ return array();
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ return 50;
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ return false;
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ return true;
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => true,
+ 'tags' => true,
+ 'expired_read' => false,
+ 'priority' => true,
+ 'infinite_lifetime' => true,
+ 'get_list' => true
+ );
+ }
+
+ /**
+ * Add an event to the log array
+ *
+ * @param string $methodName MethodName
+ * @param array $args Arguments
+ * @return void
+ */
+ private function _addLog($methodName, $args)
+ {
+ $this->_log[$this->_index] = array(
+ 'methodName' => $methodName,
+ 'args' => $args
+ );
+ $this->_index = $this->_index + 1;
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/TwoLevels.php b/library/Zend/Cache/Backend/TwoLevels.php
new file mode 100644
index 0000000..127f21d
--- /dev/null
+++ b/library/Zend/Cache/Backend/TwoLevels.php
@@ -0,0 +1,536 @@
+ (string) slow_backend :
+ * - Slow backend name
+ * - Must implement the Zend_Cache_Backend_ExtendedInterface
+ * - Should provide a big storage
+ *
+ * =====> (string) fast_backend :
+ * - Flow backend name
+ * - Must implement the Zend_Cache_Backend_ExtendedInterface
+ * - Must be much faster than slow_backend
+ *
+ * =====> (array) slow_backend_options :
+ * - Slow backend options (see corresponding backend)
+ *
+ * =====> (array) fast_backend_options :
+ * - Fast backend options (see corresponding backend)
+ *
+ * =====> (int) stats_update_factor :
+ * - Disable / Tune the computation of the fast backend filling percentage
+ * - When saving a record into cache :
+ * 1 => systematic computation of the fast backend filling percentage
+ * x (integer) > 1 => computation of the fast backend filling percentage randomly 1 times on x cache write
+ *
+ * =====> (boolean) slow_backend_custom_naming :
+ * =====> (boolean) fast_backend_custom_naming :
+ * =====> (boolean) slow_backend_autoload :
+ * =====> (boolean) fast_backend_autoload :
+ * - See Zend_Cache::factory() method
+ *
+ * =====> (boolean) auto_refresh_fast_cache
+ * - If true, auto refresh the fast cache when a cache record is hit
+ *
+ * @var array available options
+ */
+ protected $_options = array(
+ 'slow_backend' => 'File',
+ 'fast_backend' => 'Apc',
+ 'slow_backend_options' => array(),
+ 'fast_backend_options' => array(),
+ 'stats_update_factor' => 10,
+ 'slow_backend_custom_naming' => false,
+ 'fast_backend_custom_naming' => false,
+ 'slow_backend_autoload' => false,
+ 'fast_backend_autoload' => false,
+ 'auto_refresh_fast_cache' => true
+ );
+
+ /**
+ * Slow Backend
+ *
+ * @var Zend_Cache_Backend_ExtendedInterface
+ */
+ protected $_slowBackend;
+
+ /**
+ * Fast Backend
+ *
+ * @var Zend_Cache_Backend_ExtendedInterface
+ */
+ protected $_fastBackend;
+
+ /**
+ * Cache for the fast backend filling percentage
+ *
+ * @var int
+ */
+ protected $_fastBackendFillingPercentage = null;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ parent::__construct($options);
+
+ if ($this->_options['slow_backend'] === null) {
+ Zend_Cache::throwException('slow_backend option has to set');
+ } elseif ($this->_options['slow_backend'] instanceof Zend_Cache_Backend_ExtendedInterface) {
+ $this->_slowBackend = $this->_options['slow_backend'];
+ } else {
+ $this->_slowBackend = Zend_Cache::_makeBackend(
+ $this->_options['slow_backend'],
+ $this->_options['slow_backend_options'],
+ $this->_options['slow_backend_custom_naming'],
+ $this->_options['slow_backend_autoload']
+ );
+ if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_slowBackend))) {
+ Zend_Cache::throwException('slow_backend must implement the Zend_Cache_Backend_ExtendedInterface interface');
+ }
+ }
+
+ if ($this->_options['fast_backend'] === null) {
+ Zend_Cache::throwException('fast_backend option has to set');
+ } elseif ($this->_options['fast_backend'] instanceof Zend_Cache_Backend_ExtendedInterface) {
+ $this->_fastBackend = $this->_options['fast_backend'];
+ } else {
+ $this->_fastBackend = Zend_Cache::_makeBackend(
+ $this->_options['fast_backend'],
+ $this->_options['fast_backend_options'],
+ $this->_options['fast_backend_custom_naming'],
+ $this->_options['fast_backend_autoload']
+ );
+ if (!in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_fastBackend))) {
+ Zend_Cache::throwException('fast_backend must implement the Zend_Cache_Backend_ExtendedInterface interface');
+ }
+ }
+
+ $this->_slowBackend->setDirectives($this->_directives);
+ $this->_fastBackend->setDirectives($this->_directives);
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id cache id
+ * @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ $fastTest = $this->_fastBackend->test($id);
+ if ($fastTest) {
+ return $fastTest;
+ } else {
+ return $this->_slowBackend->test($id);
+ }
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data Datas to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false, $priority = 8)
+ {
+ $usage = $this->_getFastFillingPercentage('saving');
+ $boolFast = true;
+ $lifetime = $this->getLifetime($specificLifetime);
+ $preparedData = $this->_prepareData($data, $lifetime, $priority);
+ if (($priority > 0) && (10 * $priority >= $usage)) {
+ $fastLifetime = $this->_getFastLifetime($lifetime, $priority);
+ $boolFast = $this->_fastBackend->save($preparedData, $id, array(), $fastLifetime);
+ $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime);
+ } else {
+ $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime);
+ if ($boolSlow === true) {
+ $boolFast = $this->_fastBackend->remove($id);
+ if (!$boolFast && !$this->_fastBackend->test($id)) {
+ // some backends return false on remove() even if the key never existed. (and it won't if fast is full)
+ // all we care about is that the key doesn't exist now
+ $boolFast = true;
+ }
+ }
+ }
+
+ return ($boolFast && $boolSlow);
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * Note : return value is always "string" (unserialization is done by the core not by the backend)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @return string|false cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ $res = $this->_fastBackend->load($id, $doNotTestCacheValidity);
+ if ($res === false) {
+ $res = $this->_slowBackend->load($id, $doNotTestCacheValidity);
+ if ($res === false) {
+ // there is no cache at all for this id
+ return false;
+ }
+ }
+ $array = unserialize($res);
+ // maybe, we have to refresh the fast cache ?
+ if ($this->_options['auto_refresh_fast_cache']) {
+ if ($array['priority'] == 10) {
+ // no need to refresh the fast cache with priority = 10
+ return $array['data'];
+ }
+ $newFastLifetime = $this->_getFastLifetime($array['lifetime'], $array['priority'], time() - $array['expire']);
+ // we have the time to refresh the fast cache
+ $usage = $this->_getFastFillingPercentage('loading');
+ if (($array['priority'] > 0) && (10 * $array['priority'] >= $usage)) {
+ // we can refresh the fast cache
+ $preparedData = $this->_prepareData($array['data'], $array['lifetime'], $array['priority']);
+ $this->_fastBackend->save($preparedData, $id, array(), $newFastLifetime);
+ }
+ }
+ return $array['data'];
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id)
+ {
+ $boolFast = $this->_fastBackend->remove($id);
+ $boolSlow = $this->_slowBackend->remove($id);
+ return $boolFast && $boolSlow;
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ $boolFast = $this->_fastBackend->clean(Zend_Cache::CLEANING_MODE_ALL);
+ $boolSlow = $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_ALL);
+ return $boolFast && $boolSlow;
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ return $this->_slowBackend->clean(Zend_Cache::CLEANING_MODE_OLD);
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ $ids = $this->_slowBackend->getIdsMatchingTags($tags);
+ $res = true;
+ foreach ($ids as $id) {
+ $bool = $this->remove($id);
+ $res = $res && $bool;
+ }
+ return $res;
+ break;
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ $ids = $this->_slowBackend->getIdsNotMatchingTags($tags);
+ $res = true;
+ foreach ($ids as $id) {
+ $bool = $this->remove($id);
+ $res = $res && $bool;
+ }
+ return $res;
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $ids = $this->_slowBackend->getIdsMatchingAnyTags($tags);
+ $res = true;
+ foreach ($ids as $id) {
+ $bool = $this->remove($id);
+ $res = $res && $bool;
+ }
+ return $res;
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ return $this->_slowBackend->getIds();
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ return $this->_slowBackend->getTags();
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ return $this->_slowBackend->getIdsMatchingTags($tags);
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ return $this->_slowBackend->getIdsNotMatchingTags($tags);
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ return $this->_slowBackend->getIdsMatchingAnyTags($tags);
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ return $this->_slowBackend->getFillingPercentage();
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ return $this->_slowBackend->getMetadatas($id);
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ return $this->_slowBackend->touch($id, $extraLifetime);
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ $slowBackendCapabilities = $this->_slowBackend->getCapabilities();
+ return array(
+ 'automatic_cleaning' => $slowBackendCapabilities['automatic_cleaning'],
+ 'tags' => $slowBackendCapabilities['tags'],
+ 'expired_read' => $slowBackendCapabilities['expired_read'],
+ 'priority' => $slowBackendCapabilities['priority'],
+ 'infinite_lifetime' => $slowBackendCapabilities['infinite_lifetime'],
+ 'get_list' => $slowBackendCapabilities['get_list']
+ );
+ }
+
+ /**
+ * Prepare a serialized array to store datas and metadatas informations
+ *
+ * @param string $data data to store
+ * @param int $lifetime original lifetime
+ * @param int $priority priority
+ * @return string serialize array to store into cache
+ */
+ private function _prepareData($data, $lifetime, $priority)
+ {
+ $lt = $lifetime;
+ if ($lt === null) {
+ $lt = 9999999999;
+ }
+ return serialize(array(
+ 'data' => $data,
+ 'lifetime' => $lifetime,
+ 'expire' => time() + $lt,
+ 'priority' => $priority
+ ));
+ }
+
+ /**
+ * Compute and return the lifetime for the fast backend
+ *
+ * @param int $lifetime original lifetime
+ * @param int $priority priority
+ * @param int $maxLifetime maximum lifetime
+ * @return int lifetime for the fast backend
+ */
+ private function _getFastLifetime($lifetime, $priority, $maxLifetime = null)
+ {
+ if ($lifetime <= 0) {
+ // if no lifetime, we have an infinite lifetime
+ // we need to use arbitrary lifetimes
+ $fastLifetime = (int) (2592000 / (11 - $priority));
+ } else {
+ // prevent computed infinite lifetime (0) by ceil
+ $fastLifetime = (int) ceil($lifetime / (11 - $priority));
+ }
+
+ if ($maxLifetime >= 0 && $fastLifetime > $maxLifetime) {
+ return $maxLifetime;
+ }
+
+ return $fastLifetime;
+ }
+
+ /**
+ * PUBLIC METHOD FOR UNIT TESTING ONLY !
+ *
+ * Force a cache record to expire
+ *
+ * @param string $id cache id
+ */
+ public function ___expire($id)
+ {
+ $this->_fastBackend->remove($id);
+ $this->_slowBackend->___expire($id);
+ }
+
+ private function _getFastFillingPercentage($mode)
+ {
+
+ if ($mode == 'saving') {
+ // mode saving
+ if ($this->_fastBackendFillingPercentage === null) {
+ $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage();
+ } else {
+ $rand = rand(1, $this->_options['stats_update_factor']);
+ if ($rand == 1) {
+ // we force a refresh
+ $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage();
+ }
+ }
+ } else {
+ // mode loading
+ // we compute the percentage only if it's not available in cache
+ if ($this->_fastBackendFillingPercentage === null) {
+ $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage();
+ }
+ }
+ return $this->_fastBackendFillingPercentage;
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/WinCache.php b/library/Zend/Cache/Backend/WinCache.php
new file mode 100644
index 0000000..452bac9
--- /dev/null
+++ b/library/Zend/Cache/Backend/WinCache.php
@@ -0,0 +1,349 @@
+ infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $lifetime = $this->getLifetime($specificLifetime);
+ $result = wincache_ucache_set($id, array($data, time(), $lifetime), $lifetime);
+ if (count($tags) > 0) {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_WINCACHE_BACKEND);
+ }
+ return $result;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id cache id
+ * @return boolean true if no problem
+ */
+ public function remove($id)
+ {
+ return wincache_ucache_delete($id);
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => unsupported
+ * 'matchingTag' => unsupported
+ * 'notMatchingTag' => unsupported
+ * 'matchingAnyTag' => unsupported
+ *
+ * @param string $mode clean mode
+ * @param array $tags array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ return wincache_ucache_clear();
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $this->_log("Zend_Cache_Backend_WinCache::clean() : CLEANING_MODE_OLD is unsupported by the WinCache backend");
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_WINCACHE_BACKEND);
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+
+ /**
+ * Return true if the automatic cleaning is available for the backend
+ *
+ * DEPRECATED : use getCapabilities() instead
+ *
+ * @deprecated
+ * @return boolean
+ */
+ public function isAutomaticCleaningAvailable()
+ {
+ return false;
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @throws Zend_Cache_Exception
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ $mem = wincache_ucache_meminfo();
+ $memSize = $mem['memory_total'];
+ $memUsed = $mem['memory_free'];
+ if ($memSize == 0) {
+ Zend_Cache::throwException('can\'t get WinCache memory size');
+ }
+ if ($memUsed > $memSize) {
+ return 100;
+ }
+ return ((int) (100. * ($memUsed / $memSize)));
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_WINCACHE_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_WINCACHE_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_WINCACHE_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of any matching cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_WINCACHE_BACKEND);
+ return array();
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ $res = array();
+ $array = wincache_ucache_info();
+ $records = $array['ucache_entries'];
+ foreach ($records as $record) {
+ $res[] = $record['key_name'];
+ }
+ return $res;
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array must include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ $tmp = wincache_ucache_get($id);
+ if (is_array($tmp)) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ if (!isset($tmp[2])) {
+ return false;
+ }
+ $lifetime = $tmp[2];
+ return array(
+ 'expire' => $mtime + $lifetime,
+ 'tags' => array(),
+ 'mtime' => $mtime
+ );
+ }
+ return false;
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ $tmp = wincache_ucache_get($id);
+ if (is_array($tmp)) {
+ $data = $tmp[0];
+ $mtime = $tmp[1];
+ if (!isset($tmp[2])) {
+ return false;
+ }
+ $lifetime = $tmp[2];
+ $newLifetime = $lifetime - (time() - $mtime) + $extraLifetime;
+ if ($newLifetime <=0) {
+ return false;
+ }
+ return wincache_ucache_set($id, array($data, time(), $newLifetime), $newLifetime);
+ }
+ return false;
+ }
+
+ /**
+ * Return an associative array of capabilities (booleans) of the backend
+ *
+ * The array must include these keys :
+ * - automatic_cleaning (is automating cleaning necessary)
+ * - tags (are tags supported)
+ * - expired_read (is it possible to read expired cache records
+ * (for doNotTestCacheValidity option for example))
+ * - priority does the backend deal with priority when saving
+ * - infinite_lifetime (is infinite lifetime can work with this backend)
+ * - get_list (is it possible to get the list of cache ids and the complete list of tags)
+ *
+ * @return array associative of with capabilities
+ */
+ public function getCapabilities()
+ {
+ return array(
+ 'automatic_cleaning' => false,
+ 'tags' => false,
+ 'expired_read' => false,
+ 'priority' => false,
+ 'infinite_lifetime' => false,
+ 'get_list' => true
+ );
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/Xcache.php b/library/Zend/Cache/Backend/Xcache.php
new file mode 100644
index 0000000..18c0d7d
--- /dev/null
+++ b/library/Zend/Cache/Backend/Xcache.php
@@ -0,0 +1,221 @@
+ (string) user :
+ * xcache.admin.user (necessary for the clean() method)
+ *
+ * =====> (string) password :
+ * xcache.admin.pass (clear, not MD5) (necessary for the clean() method)
+ *
+ * @var array available options
+ */
+ protected $_options = array(
+ 'user' => null,
+ 'password' => null
+ );
+
+ /**
+ * Constructor
+ *
+ * @param array $options associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ if (!extension_loaded('xcache')) {
+ Zend_Cache::throwException('The xcache extension must be loaded for using this backend !');
+ }
+ parent::__construct($options);
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * WARNING $doNotTestCacheValidity=true is unsupported by the Xcache backend
+ *
+ * @param string $id cache id
+ * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
+ * @return string cached datas (or false)
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ if ($doNotTestCacheValidity) {
+ $this->_log("Zend_Cache_Backend_Xcache::load() : \$doNotTestCacheValidity=true is unsupported by the Xcache backend");
+ }
+ $tmp = xcache_get($id);
+ if (is_array($tmp)) {
+ return $tmp[0];
+ }
+ return false;
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id cache id
+ * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ if (xcache_isset($id)) {
+ $tmp = xcache_get($id);
+ if (is_array($tmp)) {
+ return $tmp[1];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data datas to cache
+ * @param string $id cache id
+ * @param array $tags array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime if != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $lifetime = $this->getLifetime($specificLifetime);
+ $result = xcache_set($id, array($data, time()), $lifetime);
+ if (count($tags) > 0) {
+ $this->_log(self::TAGS_UNSUPPORTED_BY_SAVE_OF_XCACHE_BACKEND);
+ }
+ return $result;
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id cache id
+ * @return boolean true if no problem
+ */
+ public function remove($id)
+ {
+ return xcache_unset($id);
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => unsupported
+ * 'matchingTag' => unsupported
+ * 'notMatchingTag' => unsupported
+ * 'matchingAnyTag' => unsupported
+ *
+ * @param string $mode clean mode
+ * @param array $tags array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ // Necessary because xcache_clear_cache() need basic authentification
+ $backup = array();
+ if (isset($_SERVER['PHP_AUTH_USER'])) {
+ $backup['PHP_AUTH_USER'] = $_SERVER['PHP_AUTH_USER'];
+ }
+ if (isset($_SERVER['PHP_AUTH_PW'])) {
+ $backup['PHP_AUTH_PW'] = $_SERVER['PHP_AUTH_PW'];
+ }
+ if ($this->_options['user']) {
+ $_SERVER['PHP_AUTH_USER'] = $this->_options['user'];
+ }
+ if ($this->_options['password']) {
+ $_SERVER['PHP_AUTH_PW'] = $this->_options['password'];
+ }
+
+ $cnt = xcache_count(XC_TYPE_VAR);
+ for ($i=0; $i < $cnt; $i++) {
+ xcache_clear_cache(XC_TYPE_VAR, $i);
+ }
+
+ if (isset($backup['PHP_AUTH_USER'])) {
+ $_SERVER['PHP_AUTH_USER'] = $backup['PHP_AUTH_USER'];
+ $_SERVER['PHP_AUTH_PW'] = $backup['PHP_AUTH_PW'];
+ }
+ return true;
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $this->_log("Zend_Cache_Backend_Xcache::clean() : CLEANING_MODE_OLD is unsupported by the Xcache backend");
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $this->_log(self::TAGS_UNSUPPORTED_BY_CLEAN_OF_XCACHE_BACKEND);
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+
+ /**
+ * Return true if the automatic cleaning is available for the backend
+ *
+ * @return boolean
+ */
+ public function isAutomaticCleaningAvailable()
+ {
+ return false;
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/ZendPlatform.php b/library/Zend/Cache/Backend/ZendPlatform.php
new file mode 100644
index 0000000..fb9d36d
--- /dev/null
+++ b/library/Zend/Cache/Backend/ZendPlatform.php
@@ -0,0 +1,317 @@
+_directives['lifetime'];
+ }
+ $res = output_cache_get($id, $lifetime);
+ if($res) {
+ return $res[0];
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id Cache id
+ * @return mixed|false false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ */
+ public function test($id)
+ {
+ $result = output_cache_get($id, $this->_directives['lifetime']);
+ if ($result) {
+ return $result[1];
+ }
+ return false;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data Data to cache
+ * @param string $id Cache id
+ * @param array $tags Array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ if (!($specificLifetime === false)) {
+ $this->_log("Zend_Cache_Backend_ZendPlatform::save() : non false specifc lifetime is unsuported for this backend");
+ }
+
+ $lifetime = $this->_directives['lifetime'];
+ $result1 = output_cache_put($id, array($data, time()));
+ $result2 = (count($tags) == 0);
+
+ foreach ($tags as $tag) {
+ $tagid = self::TAGS_PREFIX.$tag;
+ $old_tags = output_cache_get($tagid, $lifetime);
+ if ($old_tags === false) {
+ $old_tags = array();
+ }
+ $old_tags[$id] = $id;
+ output_cache_remove_key($tagid);
+ $result2 = output_cache_put($tagid, $old_tags);
+ }
+
+ return $result1 && $result2;
+ }
+
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id Cache id
+ * @return boolean True if no problem
+ */
+ public function remove($id)
+ {
+ return output_cache_remove_key($id);
+ }
+
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
+ * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
+ * This mode is not supported in this backend
+ * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => unsupported
+ * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode Clean mode
+ * @param array $tags Array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean True if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $cache_dir = ini_get('zend_accelerator.output_cache_dir');
+ if (!$cache_dir) {
+ return false;
+ }
+ $cache_dir .= '/.php_cache_api/';
+ return $this->_clean($cache_dir, $mode);
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ $idlist = null;
+ foreach ($tags as $tag) {
+ $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']);
+ if ($idlist) {
+ $idlist = array_intersect_assoc($idlist, $next_idlist);
+ } else {
+ $idlist = $next_idlist;
+ }
+ if (count($idlist) == 0) {
+ // if ID list is already empty - we may skip checking other IDs
+ $idlist = null;
+ break;
+ }
+ }
+ if ($idlist) {
+ foreach ($idlist as $id) {
+ output_cache_remove_key($id);
+ }
+ }
+ return true;
+ break;
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ $this->_log("Zend_Cache_Backend_ZendPlatform::clean() : CLEANING_MODE_NOT_MATCHING_TAG is not supported by the Zend Platform backend");
+ return false;
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $idlist = null;
+ foreach ($tags as $tag) {
+ $next_idlist = output_cache_get(self::TAGS_PREFIX.$tag, $this->_directives['lifetime']);
+ if ($idlist) {
+ $idlist = array_merge_recursive($idlist, $next_idlist);
+ } else {
+ $idlist = $next_idlist;
+ }
+ if (count($idlist) == 0) {
+ // if ID list is already empty - we may skip checking other IDs
+ $idlist = null;
+ break;
+ }
+ }
+ if ($idlist) {
+ foreach ($idlist as $id) {
+ output_cache_remove_key($id);
+ }
+ }
+ return true;
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+
+ /**
+ * Clean a directory and recursivly go over it's subdirectories
+ *
+ * Remove all the cached files that need to be cleaned (according to mode and files mtime)
+ *
+ * @param string $dir Path of directory ot clean
+ * @param string $mode The same parameter as in Zend_Cache_Backend_ZendPlatform::clean()
+ * @return boolean True if ok
+ */
+ private function _clean($dir, $mode)
+ {
+ $d = @dir($dir);
+ if (!$d) {
+ return false;
+ }
+ $result = true;
+ while (false !== ($file = $d->read())) {
+ if ($file == '.' || $file == '..') {
+ continue;
+ }
+ $file = $d->path . $file;
+ if (is_dir($file)) {
+ $result = ($this->_clean($file .'/', $mode)) && ($result);
+ } else {
+ if ($mode == Zend_Cache::CLEANING_MODE_ALL) {
+ $result = ($this->_remove($file)) && ($result);
+ } else if ($mode == Zend_Cache::CLEANING_MODE_OLD) {
+ // Files older than lifetime get deleted from cache
+ if ($this->_directives['lifetime'] !== null) {
+ if ((time() - @filemtime($file)) > $this->_directives['lifetime']) {
+ $result = ($this->_remove($file)) && ($result);
+ }
+ }
+ }
+ }
+ }
+ $d->close();
+ return $result;
+ }
+
+ /**
+ * Remove a file
+ *
+ * If we can't remove the file (because of locks or any problem), we will touch
+ * the file to invalidate it
+ *
+ * @param string $file Complete file path
+ * @return boolean True if ok
+ */
+ private function _remove($file)
+ {
+ if (!@unlink($file)) {
+ # If we can't remove the file (because of locks or any problem), we will touch
+ # the file to invalidate it
+ $this->_log("Zend_Cache_Backend_ZendPlatform::_remove() : we can't remove $file => we are going to try to invalidate it");
+ if ($this->_directives['lifetime'] === null) {
+ return false;
+ }
+ if (!file_exists($file)) {
+ return false;
+ }
+ return @touch($file, time() - 2*abs($this->_directives['lifetime']));
+ }
+ return true;
+ }
+
+}
diff --git a/library/Zend/Cache/Backend/ZendServer.php b/library/Zend/Cache/Backend/ZendServer.php
new file mode 100644
index 0000000..3a4a1da
--- /dev/null
+++ b/library/Zend/Cache/Backend/ZendServer.php
@@ -0,0 +1,207 @@
+ (string) namespace :
+ * Namespace to be used for chaching operations
+ *
+ * @var array available options
+ */
+ protected $_options = array(
+ 'namespace' => 'zendframework'
+ );
+
+ /**
+ * Store data
+ *
+ * @param mixed $data Object to store
+ * @param string $id Cache id
+ * @param int $timeToLive Time to live in seconds
+ * @throws Zend_Cache_Exception
+ */
+ abstract protected function _store($data, $id, $timeToLive);
+
+ /**
+ * Fetch data
+ *
+ * @param string $id Cache id
+ * @throws Zend_Cache_Exception
+ */
+ abstract protected function _fetch($id);
+
+ /**
+ * Unset data
+ *
+ * @param string $id Cache id
+ */
+ abstract protected function _unset($id);
+
+ /**
+ * Clear cache
+ */
+ abstract protected function _clear();
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * @param string $id cache id
+ * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
+ * @return string cached datas (or false)
+ */
+ public function load($id, $doNotTestCacheValidity = false)
+ {
+ $tmp = $this->_fetch($id);
+ if ($tmp !== null) {
+ return $tmp;
+ }
+ return false;
+ }
+
+ /**
+ * Test if a cache is available or not (for the given id)
+ *
+ * @param string $id cache id
+ * @return mixed false (a cache is not available) or "last modified" timestamp (int) of the available cache record
+ * @throws Zend_Cache_Exception
+ */
+ public function test($id)
+ {
+ $tmp = $this->_fetch('internal-metadatas---' . $id);
+ if ($tmp !== false) {
+ if (!is_array($tmp) || !isset($tmp['mtime'])) {
+ Zend_Cache::throwException('Cache metadata for \'' . $id . '\' id is corrupted' );
+ }
+ return $tmp['mtime'];
+ }
+ return false;
+ }
+
+ /**
+ * Compute & return the expire time
+ *
+ * @return int expire time (unix timestamp)
+ */
+ private function _expireTime($lifetime)
+ {
+ if ($lifetime === null) {
+ return 9999999999;
+ }
+ return time() + $lifetime;
+ }
+
+ /**
+ * Save some string datas into a cache record
+ *
+ * Note : $data is always "string" (serialization is done by the
+ * core not by the backend)
+ *
+ * @param string $data datas to cache
+ * @param string $id cache id
+ * @param array $tags array of strings, the cache record will be tagged by each string entry
+ * @param int $specificLifetime if != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @return boolean true if no problem
+ */
+ public function save($data, $id, $tags = array(), $specificLifetime = false)
+ {
+ $lifetime = $this->getLifetime($specificLifetime);
+ $metadatas = array(
+ 'mtime' => time(),
+ 'expire' => $this->_expireTime($lifetime),
+ );
+
+ if (count($tags) > 0) {
+ $this->_log('Zend_Cache_Backend_ZendServer::save() : tags are unsupported by the ZendServer backends');
+ }
+
+ return $this->_store($data, $id, $lifetime) &&
+ $this->_store($metadatas, 'internal-metadatas---' . $id, $lifetime);
+ }
+
+ /**
+ * Remove a cache record
+ *
+ * @param string $id cache id
+ * @return boolean true if no problem
+ */
+ public function remove($id)
+ {
+ $result1 = $this->_unset($id);
+ $result2 = $this->_unset('internal-metadatas---' . $id);
+
+ return $result1 && $result2;
+ }
+
+ /**
+ * Clean some cache records
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => unsupported
+ * 'matchingTag' => unsupported
+ * 'notMatchingTag' => unsupported
+ * 'matchingAnyTag' => unsupported
+ *
+ * @param string $mode clean mode
+ * @param array $tags array of tags
+ * @throws Zend_Cache_Exception
+ * @return boolean true if no problem
+ */
+ public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array())
+ {
+ switch ($mode) {
+ case Zend_Cache::CLEANING_MODE_ALL:
+ $this->_clear();
+ return true;
+ break;
+ case Zend_Cache::CLEANING_MODE_OLD:
+ $this->_log("Zend_Cache_Backend_ZendServer::clean() : CLEANING_MODE_OLD is unsupported by the Zend Server backends.");
+ break;
+ case Zend_Cache::CLEANING_MODE_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG:
+ case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG:
+ $this->_clear();
+ $this->_log('Zend_Cache_Backend_ZendServer::clean() : tags are unsupported by the Zend Server backends.');
+ break;
+ default:
+ Zend_Cache::throwException('Invalid mode for clean() method');
+ break;
+ }
+ }
+}
diff --git a/library/Zend/Cache/Backend/ZendServer/Disk.php b/library/Zend/Cache/Backend/ZendServer/Disk.php
new file mode 100644
index 0000000..de806b1
--- /dev/null
+++ b/library/Zend/Cache/Backend/ZendServer/Disk.php
@@ -0,0 +1,100 @@
+_options['namespace'] . '::' . $id,
+ $data,
+ $timeToLive) === false) {
+ $this->_log('Store operation failed.');
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Fetch data
+ *
+ * @param string $id Cache id
+ */
+ protected function _fetch($id)
+ {
+ return zend_disk_cache_fetch($this->_options['namespace'] . '::' . $id);
+ }
+
+ /**
+ * Unset data
+ *
+ * @param string $id Cache id
+ * @return boolean true if no problem
+ */
+ protected function _unset($id)
+ {
+ return zend_disk_cache_delete($this->_options['namespace'] . '::' . $id);
+ }
+
+ /**
+ * Clear cache
+ */
+ protected function _clear()
+ {
+ zend_disk_cache_clear($this->_options['namespace']);
+ }
+}
diff --git a/library/Zend/Cache/Backend/ZendServer/ShMem.php b/library/Zend/Cache/Backend/ZendServer/ShMem.php
new file mode 100644
index 0000000..d6f7bf0
--- /dev/null
+++ b/library/Zend/Cache/Backend/ZendServer/ShMem.php
@@ -0,0 +1,100 @@
+_options['namespace'] . '::' . $id,
+ $data,
+ $timeToLive) === false) {
+ $this->_log('Store operation failed.');
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Fetch data
+ *
+ * @param string $id Cache id
+ */
+ protected function _fetch($id)
+ {
+ return zend_shm_cache_fetch($this->_options['namespace'] . '::' . $id);
+ }
+
+ /**
+ * Unset data
+ *
+ * @param string $id Cache id
+ * @return boolean true if no problem
+ */
+ protected function _unset($id)
+ {
+ return zend_shm_cache_delete($this->_options['namespace'] . '::' . $id);
+ }
+
+ /**
+ * Clear cache
+ */
+ protected function _clear()
+ {
+ zend_shm_cache_clear($this->_options['namespace']);
+ }
+}
diff --git a/library/Zend/Cache/Core.php b/library/Zend/Cache/Core.php
new file mode 100644
index 0000000..47fa687
--- /dev/null
+++ b/library/Zend/Cache/Core.php
@@ -0,0 +1,764 @@
+ (boolean) write_control :
+ * - Enable / disable write control (the cache is read just after writing to detect corrupt entries)
+ * - Enable write control will lightly slow the cache writing but not the cache reading
+ * Write control can detect some corrupt cache files but maybe it's not a perfect control
+ *
+ * ====> (boolean) caching :
+ * - Enable / disable caching
+ * (can be very useful for the debug of cached scripts)
+ *
+ * =====> (string) cache_id_prefix :
+ * - prefix for cache ids (namespace)
+ *
+ * ====> (boolean) automatic_serialization :
+ * - Enable / disable automatic serialization
+ * - It can be used to save directly datas which aren't strings (but it's slower)
+ *
+ * ====> (int) automatic_cleaning_factor :
+ * - Disable / Tune the automatic cleaning process
+ * - The automatic cleaning process destroy too old (for the given life time)
+ * cache files when a new cache file is written :
+ * 0 => no automatic cache cleaning
+ * 1 => systematic cache cleaning
+ * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
+ *
+ * ====> (int) lifetime :
+ * - Cache lifetime (in seconds)
+ * - If null, the cache is valid forever.
+ *
+ * ====> (boolean) logging :
+ * - If set to true, logging is activated (but the system is slower)
+ *
+ * ====> (boolean) ignore_user_abort
+ * - If set to true, the core will set the ignore_user_abort PHP flag inside the
+ * save() method to avoid cache corruptions in some cases (default false)
+ *
+ * @var array $_options available options
+ */
+ protected $_options = array(
+ 'write_control' => true,
+ 'caching' => true,
+ 'cache_id_prefix' => null,
+ 'automatic_serialization' => false,
+ 'automatic_cleaning_factor' => 10,
+ 'lifetime' => 3600,
+ 'logging' => false,
+ 'logger' => null,
+ 'ignore_user_abort' => false
+ );
+
+ /**
+ * Array of options which have to be transfered to backend
+ *
+ * @var array $_directivesList
+ */
+ protected static $_directivesList = array('lifetime', 'logging', 'logger');
+
+ /**
+ * Not used for the core, just a sort a hint to get a common setOption() method (for the core and for frontends)
+ *
+ * @var array $_specificOptions
+ */
+ protected $_specificOptions = array();
+
+ /**
+ * Last used cache id
+ *
+ * @var string $_lastId
+ */
+ private $_lastId = null;
+
+ /**
+ * True if the backend implements Zend_Cache_Backend_ExtendedInterface
+ *
+ * @var boolean $_extendedBackend
+ */
+ protected $_extendedBackend = false;
+
+ /**
+ * Array of capabilities of the backend (only if it implements Zend_Cache_Backend_ExtendedInterface)
+ *
+ * @var array
+ */
+ protected $_backendCapabilities = array();
+
+ /**
+ * Constructor
+ *
+ * @param array|Zend_Config $options Associative array of options or Zend_Config instance
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct($options = array())
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ }
+ if (!is_array($options)) {
+ Zend_Cache::throwException("Options passed were not an array"
+ . " or Zend_Config instance.");
+ }
+ while (list($name, $value) = each($options)) {
+ $this->setOption($name, $value);
+ }
+ $this->_loggerSanity();
+ }
+
+ /**
+ * Set options using an instance of type Zend_Config
+ *
+ * @param Zend_Config $config
+ * @return Zend_Cache_Core
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ $options = $config->toArray();
+ while (list($name, $value) = each($options)) {
+ $this->setOption($name, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Set the backend
+ *
+ * @param Zend_Cache_Backend $backendObject
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setBackend(Zend_Cache_Backend $backendObject)
+ {
+ $this->_backend= $backendObject;
+ // some options (listed in $_directivesList) have to be given
+ // to the backend too (even if they are not "backend specific")
+ $directives = array();
+ foreach (Zend_Cache_Core::$_directivesList as $directive) {
+ $directives[$directive] = $this->_options[$directive];
+ }
+ $this->_backend->setDirectives($directives);
+ if (in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_backend))) {
+ $this->_extendedBackend = true;
+ $this->_backendCapabilities = $this->_backend->getCapabilities();
+ }
+
+ }
+
+ /**
+ * Returns the backend
+ *
+ * @return Zend_Cache_Backend backend object
+ */
+ public function getBackend()
+ {
+ return $this->_backend;
+ }
+
+ /**
+ * Public frontend to set an option
+ *
+ * There is an additional validation (relatively to the protected _setOption method)
+ *
+ * @param string $name Name of the option
+ * @param mixed $value Value of the option
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setOption($name, $value)
+ {
+ if (!is_string($name)) {
+ Zend_Cache::throwException("Incorrect option name : $name");
+ }
+ $name = strtolower($name);
+ if (array_key_exists($name, $this->_options)) {
+ // This is a Core option
+ $this->_setOption($name, $value);
+ return;
+ }
+ if (array_key_exists($name, $this->_specificOptions)) {
+ // This a specic option of this frontend
+ $this->_specificOptions[$name] = $value;
+ return;
+ }
+ }
+
+ /**
+ * Public frontend to get an option value
+ *
+ * @param string $name Name of the option
+ * @throws Zend_Cache_Exception
+ * @return mixed option value
+ */
+ public function getOption($name)
+ {
+ if (is_string($name)) {
+ $name = strtolower($name);
+ if (array_key_exists($name, $this->_options)) {
+ // This is a Core option
+ return $this->_options[$name];
+ }
+ if (array_key_exists($name, $this->_specificOptions)) {
+ // This a specic option of this frontend
+ return $this->_specificOptions[$name];
+ }
+ }
+ Zend_Cache::throwException("Incorrect option name : $name");
+ }
+
+ /**
+ * Set an option
+ *
+ * @param string $name Name of the option
+ * @param mixed $value Value of the option
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ private function _setOption($name, $value)
+ {
+ if (!is_string($name) || !array_key_exists($name, $this->_options)) {
+ Zend_Cache::throwException("Incorrect option name : $name");
+ }
+ if ($name == 'lifetime' && empty($value)) {
+ $value = null;
+ }
+ $this->_options[$name] = $value;
+ }
+
+ /**
+ * Force a new lifetime
+ *
+ * The new value is set for the core/frontend but for the backend too (directive)
+ *
+ * @param int $newLifetime New lifetime (in seconds)
+ * @return void
+ */
+ public function setLifetime($newLifetime)
+ {
+ $this->_options['lifetime'] = $newLifetime;
+ $this->_backend->setDirectives(array(
+ 'lifetime' => $newLifetime
+ ));
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use
+ * @return mixed|false Cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
+ {
+ if (!$this->_options['caching']) {
+ return false;
+ }
+ $id = $this->_id($id); // cache id may need prefix
+ $this->_lastId = $id;
+ self::_validateIdOrTag($id);
+
+ $this->_log("Zend_Cache_Core: load item '{$id}'", 7);
+ $data = $this->_backend->load($id, $doNotTestCacheValidity);
+ if ($data===false) {
+ // no cache available
+ return false;
+ }
+ if ((!$doNotUnserialize) && $this->_options['automatic_serialization']) {
+ // we need to unserialize before sending the result
+ return unserialize($data);
+ }
+ return $data;
+ }
+
+ /**
+ * Test if a cache is available for the given id
+ *
+ * @param string $id Cache id
+ * @return int|false Last modified time of cache entry if it is available, false otherwise
+ */
+ public function test($id)
+ {
+ if (!$this->_options['caching']) {
+ return false;
+ }
+ $id = $this->_id($id); // cache id may need prefix
+ self::_validateIdOrTag($id);
+ $this->_lastId = $id;
+
+ $this->_log("Zend_Cache_Core: test item '{$id}'", 7);
+ return $this->_backend->test($id);
+ }
+
+ /**
+ * Save some data in a cache
+ *
+ * @param mixed $data Data to put in cache (can be another type than string if automatic_serialization is on)
+ * @param string $id Cache id (if not set, the last cache id will be used)
+ * @param array $tags Cache tags
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends
+ * @throws Zend_Cache_Exception
+ * @return boolean True if no problem
+ */
+ public function save($data, $id = null, $tags = array(), $specificLifetime = false, $priority = 8)
+ {
+ if (!$this->_options['caching']) {
+ return true;
+ }
+ if ($id === null) {
+ $id = $this->_lastId;
+ } else {
+ $id = $this->_id($id);
+ }
+ self::_validateIdOrTag($id);
+ self::_validateTagsArray($tags);
+ if ($this->_options['automatic_serialization']) {
+ // we need to serialize datas before storing them
+ $data = serialize($data);
+ } else {
+ if (!is_string($data)) {
+ Zend_Cache::throwException("Datas must be string or set automatic_serialization = true");
+ }
+ }
+
+ // automatic cleaning
+ if ($this->_options['automatic_cleaning_factor'] > 0) {
+ $rand = rand(1, $this->_options['automatic_cleaning_factor']);
+ if ($rand==1) {
+ // new way || deprecated way
+ if ($this->_extendedBackend || method_exists($this->_backend, 'isAutomaticCleaningAvailable')) {
+ $this->_log("Zend_Cache_Core::save(): automatic cleaning running", 7);
+ $this->clean(Zend_Cache::CLEANING_MODE_OLD);
+ } else {
+ $this->_log("Zend_Cache_Core::save(): automatic cleaning is not available/necessary with current backend", 4);
+ }
+ }
+ }
+
+ $this->_log("Zend_Cache_Core: save item '{$id}'", 7);
+ if ($this->_options['ignore_user_abort']) {
+ $abort = ignore_user_abort(true);
+ }
+ if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) {
+ $result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority);
+ } else {
+ $result = $this->_backend->save($data, $id, $tags, $specificLifetime);
+ }
+ if ($this->_options['ignore_user_abort']) {
+ ignore_user_abort($abort);
+ }
+
+ if (!$result) {
+ // maybe the cache is corrupted, so we remove it !
+ $this->_log("Zend_Cache_Core::save(): failed to save item '{$id}' -> removing it", 4);
+ $this->_backend->remove($id);
+ return false;
+ }
+
+ if ($this->_options['write_control']) {
+ $data2 = $this->_backend->load($id, true);
+ if ($data!=$data2) {
+ $this->_log("Zend_Cache_Core::save(): write control of item '{$id}' failed -> removing it", 4);
+ $this->_backend->remove($id);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove a cache
+ *
+ * @param string $id Cache id to remove
+ * @return boolean True if ok
+ */
+ public function remove($id)
+ {
+ if (!$this->_options['caching']) {
+ return true;
+ }
+ $id = $this->_id($id); // cache id may need prefix
+ self::_validateIdOrTag($id);
+
+ $this->_log("Zend_Cache_Core: remove item '{$id}'", 7);
+ return $this->_backend->remove($id);
+ }
+
+ /**
+ * Clean cache entries
+ *
+ * Available modes are :
+ * 'all' (default) => remove all cache entries ($tags is not used)
+ * 'old' => remove too old cache entries ($tags is not used)
+ * 'matchingTag' => remove cache entries matching all given tags
+ * ($tags can be an array of strings or a single string)
+ * 'notMatchingTag' => remove cache entries not matching one of the given tags
+ * ($tags can be an array of strings or a single string)
+ * 'matchingAnyTag' => remove cache entries matching any given tags
+ * ($tags can be an array of strings or a single string)
+ *
+ * @param string $mode
+ * @param array|string $tags
+ * @throws Zend_Cache_Exception
+ * @return boolean True if ok
+ */
+ public function clean($mode = 'all', $tags = array())
+ {
+ if (!$this->_options['caching']) {
+ return true;
+ }
+ if (!in_array($mode, array(Zend_Cache::CLEANING_MODE_ALL,
+ Zend_Cache::CLEANING_MODE_OLD,
+ Zend_Cache::CLEANING_MODE_MATCHING_TAG,
+ Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG,
+ Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG))) {
+ Zend_Cache::throwException('Invalid cleaning mode');
+ }
+ self::_validateTagsArray($tags);
+
+ return $this->_backend->clean($mode, $tags);
+ }
+
+ /**
+ * Return an array of stored cache ids which match given tags
+ *
+ * In case of multiple tags, a logical AND is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching cache ids (string)
+ */
+ public function getIdsMatchingTags($tags = array())
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+ if (!($this->_backendCapabilities['tags'])) {
+ Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
+ }
+
+ $ids = $this->_backend->getIdsMatchingTags($tags);
+
+ // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
+ if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
+ $prefix = & $this->_options['cache_id_prefix'];
+ $prefixLen = strlen($prefix);
+ foreach ($ids as &$id) {
+ if (strpos($id, $prefix) === 0) {
+ $id = substr($id, $prefixLen);
+ }
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * Return an array of stored cache ids which don't match given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of not matching cache ids (string)
+ */
+ public function getIdsNotMatchingTags($tags = array())
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+ if (!($this->_backendCapabilities['tags'])) {
+ Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
+ }
+
+ $ids = $this->_backend->getIdsNotMatchingTags($tags);
+
+ // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
+ if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
+ $prefix = & $this->_options['cache_id_prefix'];
+ $prefixLen = strlen($prefix);
+ foreach ($ids as &$id) {
+ if (strpos($id, $prefix) === 0) {
+ $id = substr($id, $prefixLen);
+ }
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * Return an array of stored cache ids which match any given tags
+ *
+ * In case of multiple tags, a logical OR is made between tags
+ *
+ * @param array $tags array of tags
+ * @return array array of matching any cache ids (string)
+ */
+ public function getIdsMatchingAnyTags($tags = array())
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+ if (!($this->_backendCapabilities['tags'])) {
+ Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
+ }
+
+ $ids = $this->_backend->getIdsMatchingAnyTags($tags);
+
+ // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
+ if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
+ $prefix = & $this->_options['cache_id_prefix'];
+ $prefixLen = strlen($prefix);
+ foreach ($ids as &$id) {
+ if (strpos($id, $prefix) === 0) {
+ $id = substr($id, $prefixLen);
+ }
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * Return an array of stored cache ids
+ *
+ * @return array array of stored cache ids (string)
+ */
+ public function getIds()
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+
+ $ids = $this->_backend->getIds();
+
+ // we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
+ if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
+ $prefix = & $this->_options['cache_id_prefix'];
+ $prefixLen = strlen($prefix);
+ foreach ($ids as &$id) {
+ if (strpos($id, $prefix) === 0) {
+ $id = substr($id, $prefixLen);
+ }
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * Return an array of stored tags
+ *
+ * @return array array of stored tags (string)
+ */
+ public function getTags()
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+ if (!($this->_backendCapabilities['tags'])) {
+ Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
+ }
+ return $this->_backend->getTags();
+ }
+
+ /**
+ * Return the filling percentage of the backend storage
+ *
+ * @return int integer between 0 and 100
+ */
+ public function getFillingPercentage()
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+ return $this->_backend->getFillingPercentage();
+ }
+
+ /**
+ * Return an array of metadatas for the given cache id
+ *
+ * The array will include these keys :
+ * - expire : the expire timestamp
+ * - tags : a string array of tags
+ * - mtime : timestamp of last modification time
+ *
+ * @param string $id cache id
+ * @return array array of metadatas (false if the cache id is not found)
+ */
+ public function getMetadatas($id)
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+ $id = $this->_id($id); // cache id may need prefix
+ return $this->_backend->getMetadatas($id);
+ }
+
+ /**
+ * Give (if possible) an extra lifetime to the given cache id
+ *
+ * @param string $id cache id
+ * @param int $extraLifetime
+ * @return boolean true if ok
+ */
+ public function touch($id, $extraLifetime)
+ {
+ if (!$this->_extendedBackend) {
+ Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
+ }
+ $id = $this->_id($id); // cache id may need prefix
+
+ $this->_log("Zend_Cache_Core: touch item '{$id}'", 7);
+ return $this->_backend->touch($id, $extraLifetime);
+ }
+
+ /**
+ * Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
+ *
+ * Throw an exception if a problem is found
+ *
+ * @param string $string Cache id or tag
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected static function _validateIdOrTag($string)
+ {
+ if (!is_string($string)) {
+ Zend_Cache::throwException('Invalid id or tag : must be a string');
+ }
+ if (substr($string, 0, 9) == 'internal-') {
+ Zend_Cache::throwException('"internal-*" ids or tags are reserved');
+ }
+ if (!preg_match('~^[a-zA-Z0-9_]+$~D', $string)) {
+ Zend_Cache::throwException("Invalid id or tag '$string' : must use only [a-zA-Z0-9_]");
+ }
+ }
+
+ /**
+ * Validate a tags array (security, reliable filenames, reserved prefixes...)
+ *
+ * Throw an exception if a problem is found
+ *
+ * @param array $tags Array of tags
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected static function _validateTagsArray($tags)
+ {
+ if (!is_array($tags)) {
+ Zend_Cache::throwException('Invalid tags array : must be an array');
+ }
+ foreach($tags as $tag) {
+ self::_validateIdOrTag($tag);
+ }
+ reset($tags);
+ }
+
+ /**
+ * Make sure if we enable logging that the Zend_Log class
+ * is available.
+ * Create a default log object if none is set.
+ *
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected function _loggerSanity()
+ {
+ if (!isset($this->_options['logging']) || !$this->_options['logging']) {
+ return;
+ }
+
+ if (isset($this->_options['logger']) && $this->_options['logger'] instanceof Zend_Log) {
+ return;
+ }
+
+ // Create a default logger to the standard output stream
+ require_once 'Zend/Log.php';
+ require_once 'Zend/Log/Writer/Stream.php';
+ require_once 'Zend/Log/Filter/Priority.php';
+ $logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
+ $logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<='));
+ $this->_options['logger'] = $logger;
+ }
+
+ /**
+ * Log a message at the WARN (4) priority.
+ *
+ * @param string $message
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected function _log($message, $priority = 4)
+ {
+ if (!$this->_options['logging']) {
+ return;
+ }
+ if (!(isset($this->_options['logger']) || $this->_options['logger'] instanceof Zend_Log)) {
+ Zend_Cache::throwException('Logging is enabled but logger is not set');
+ }
+ $logger = $this->_options['logger'];
+ $logger->log($message, $priority);
+ }
+
+ /**
+ * Make and return a cache id
+ *
+ * Checks 'cache_id_prefix' and returns new id with prefix or simply the id if null
+ *
+ * @param string $id Cache id
+ * @return string Cache id (with or without prefix)
+ */
+ protected function _id($id)
+ {
+ if (($id !== null) && isset($this->_options['cache_id_prefix'])) {
+ return $this->_options['cache_id_prefix'] . $id; // return with prefix
+ }
+ return $id; // no prefix, just return the $id passed
+ }
+
+}
diff --git a/library/Zend/Cache/Exception.php b/library/Zend/Cache/Exception.php
new file mode 100644
index 0000000..a3b519c
--- /dev/null
+++ b/library/Zend/Cache/Exception.php
@@ -0,0 +1,32 @@
+_tags = $tags;
+ $this->_extension = $extension;
+ ob_start(array($this, '_flush'));
+ ob_implicit_flush(false);
+ $this->_idStack[] = $id;
+ return false;
+ }
+
+ /**
+ * callback for output buffering
+ * (shouldn't really be called manually)
+ *
+ * @param string $data Buffered output
+ * @return string Data to send to browser
+ */
+ public function _flush($data)
+ {
+ $id = array_pop($this->_idStack);
+ if ($id === null) {
+ Zend_Cache::throwException('use of _flush() without a start()');
+ }
+ if ($this->_extension) {
+ $this->save(serialize(array($data, $this->_extension)), $id, $this->_tags);
+ } else {
+ $this->save($data, $id, $this->_tags);
+ }
+ return $data;
+ }
+}
diff --git a/library/Zend/Cache/Frontend/Class.php b/library/Zend/Cache/Frontend/Class.php
new file mode 100644
index 0000000..91f26ab
--- /dev/null
+++ b/library/Zend/Cache/Frontend/Class.php
@@ -0,0 +1,265 @@
+ (mixed) cached_entity :
+ * - if set to a class name, we will cache an abstract class and will use only static calls
+ * - if set to an object, we will cache this object methods
+ *
+ * ====> (boolean) cache_by_default :
+ * - if true, method calls will be cached by default
+ *
+ * ====> (array) cached_methods :
+ * - an array of method names which will be cached (even if cache_by_default = false)
+ *
+ * ====> (array) non_cached_methods :
+ * - an array of method names which won't be cached (even if cache_by_default = true)
+ *
+ * @var array available options
+ */
+ protected $_specificOptions = array(
+ 'cached_entity' => null,
+ 'cache_by_default' => true,
+ 'cached_methods' => array(),
+ 'non_cached_methods' => array()
+ );
+
+ /**
+ * Tags array
+ *
+ * @var array
+ */
+ private $_tags = array();
+
+ /**
+ * SpecificLifetime value
+ *
+ * false => no specific life time
+ *
+ * @var int
+ */
+ private $_specificLifetime = false;
+
+ /**
+ * The cached object or the name of the cached abstract class
+ *
+ * @var mixed
+ */
+ private $_cachedEntity = null;
+
+ /**
+ * The class name of the cached object or cached abstract class
+ *
+ * Used to differentiate between different classes with the same method calls.
+ *
+ * @var string
+ */
+ private $_cachedEntityLabel = '';
+
+ /**
+ * Priority (used by some particular backends)
+ *
+ * @var int
+ */
+ private $_priority = 8;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ while (list($name, $value) = each($options)) {
+ $this->setOption($name, $value);
+ }
+ if ($this->_specificOptions['cached_entity'] === null) {
+ Zend_Cache::throwException('cached_entity must be set !');
+ }
+ $this->setCachedEntity($this->_specificOptions['cached_entity']);
+ $this->setOption('automatic_serialization', true);
+ }
+
+ /**
+ * Set a specific life time
+ *
+ * @param int $specificLifetime
+ * @return void
+ */
+ public function setSpecificLifetime($specificLifetime = false)
+ {
+ $this->_specificLifetime = $specificLifetime;
+ }
+
+ /**
+ * Set the priority (used by some particular backends)
+ *
+ * @param int $priority integer between 0 (very low priority) and 10 (maximum priority)
+ */
+ public function setPriority($priority)
+ {
+ $this->_priority = $priority;
+ }
+
+ /**
+ * Public frontend to set an option
+ *
+ * Just a wrapper to get a specific behaviour for cached_entity
+ *
+ * @param string $name Name of the option
+ * @param mixed $value Value of the option
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setOption($name, $value)
+ {
+ if ($name == 'cached_entity') {
+ $this->setCachedEntity($value);
+ } else {
+ parent::setOption($name, $value);
+ }
+ }
+
+ /**
+ * Specific method to set the cachedEntity
+ *
+ * if set to a class name, we will cache an abstract class and will use only static calls
+ * if set to an object, we will cache this object methods
+ *
+ * @param mixed $cachedEntity
+ */
+ public function setCachedEntity($cachedEntity)
+ {
+ if (!is_string($cachedEntity) && !is_object($cachedEntity)) {
+ Zend_Cache::throwException('cached_entity must be an object or a class name');
+ }
+ $this->_cachedEntity = $cachedEntity;
+ $this->_specificOptions['cached_entity'] = $cachedEntity;
+ if (is_string($this->_cachedEntity)){
+ $this->_cachedEntityLabel = $this->_cachedEntity;
+ } else {
+ $ro = new ReflectionObject($this->_cachedEntity);
+ $this->_cachedEntityLabel = $ro->getName();
+ }
+ }
+
+ /**
+ * Set the cache array
+ *
+ * @param array $tags
+ * @return void
+ */
+ public function setTagsArray($tags = array())
+ {
+ $this->_tags = $tags;
+ }
+
+ /**
+ * Main method : call the specified method or get the result from cache
+ *
+ * @param string $name Method name
+ * @param array $parameters Method parameters
+ * @return mixed Result
+ */
+ public function __call($name, $parameters)
+ {
+ $callback = array($this->_cachedEntity, $name);
+
+ if (!is_callable($callback, false)) {
+ Zend_Cache::throwException('Invalid callback');
+ }
+
+ $cacheBool1 = $this->_specificOptions['cache_by_default'];
+ $cacheBool2 = in_array($name, $this->_specificOptions['cached_methods']);
+ $cacheBool3 = in_array($name, $this->_specificOptions['non_cached_methods']);
+ $cache = (($cacheBool1 || $cacheBool2) && (!$cacheBool3));
+ if (!$cache) {
+ // We do not have not cache
+ return call_user_func_array($callback, $parameters);
+ }
+
+ $id = $this->_makeId($name, $parameters);
+ if ( ($rs = $this->load($id)) && isset($rs[0], $rs[1]) ) {
+ // A cache is available
+ $output = $rs[0];
+ $return = $rs[1];
+ } else {
+ // A cache is not available (or not valid for this frontend)
+ ob_start();
+ ob_implicit_flush(false);
+
+ try {
+ $return = call_user_func_array($callback, $parameters);
+ $output = ob_get_clean();
+ $data = array($output, $return);
+ $this->save($data, $id, $this->_tags, $this->_specificLifetime, $this->_priority);
+ } catch (Exception $e) {
+ ob_end_clean();
+ throw $e;
+ }
+ }
+
+ echo $output;
+ return $return;
+ }
+
+ /**
+ * ZF-9970
+ *
+ * @deprecated
+ */
+ private function _makeId($name, $args)
+ {
+ return $this->makeId($name, $args);
+ }
+
+ /**
+ * Make a cache id from the method name and parameters
+ *
+ * @param string $name Method name
+ * @param array $args Method parameters
+ * @return string Cache id
+ */
+ public function makeId($name, array $args = array())
+ {
+ return md5($this->_cachedEntityLabel . '__' . $name . '__' . serialize($args));
+ }
+
+}
diff --git a/library/Zend/Cache/Frontend/File.php b/library/Zend/Cache/Frontend/File.php
new file mode 100644
index 0000000..5bc1f35
--- /dev/null
+++ b/library/Zend/Cache/Frontend/File.php
@@ -0,0 +1,222 @@
+ (string) master_file :
+ * - a complete path of the master file
+ * - deprecated (see master_files)
+ *
+ * ====> (array) master_files :
+ * - an array of complete path of master files
+ * - this option has to be set !
+ *
+ * ====> (string) master_files_mode :
+ * - Zend_Cache_Frontend_File::MODE_AND or Zend_Cache_Frontend_File::MODE_OR
+ * - if MODE_AND, then all master files have to be touched to get a cache invalidation
+ * - if MODE_OR (default), then a single touched master file is enough to get a cache invalidation
+ *
+ * ====> (boolean) ignore_missing_master_files
+ * - if set to true, missing master files are ignored silently
+ * - if set to false (default), an exception is thrown if there is a missing master file
+ * @var array available options
+ */
+ protected $_specificOptions = array(
+ 'master_file' => null,
+ 'master_files' => null,
+ 'master_files_mode' => 'OR',
+ 'ignore_missing_master_files' => false
+ );
+
+ /**
+ * Master file mtimes
+ *
+ * Array of int
+ *
+ * @var array
+ */
+ private $_masterFile_mtimes = null;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Associative array of options
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ while (list($name, $value) = each($options)) {
+ $this->setOption($name, $value);
+ }
+ if (!isset($this->_specificOptions['master_files'])) {
+ Zend_Cache::throwException('master_files option must be set');
+ }
+ }
+
+ /**
+ * Change the master_files option
+ *
+ * @param array $masterFiles the complete paths and name of the master files
+ */
+ public function setMasterFiles(array $masterFiles)
+ {
+ $this->_specificOptions['master_file'] = null; // to keep a compatibility
+ $this->_specificOptions['master_files'] = null;
+ $this->_masterFile_mtimes = array();
+
+ clearstatcache();
+ $i = 0;
+ foreach ($masterFiles as $masterFile) {
+ if (file_exists($masterFile)) {
+ $mtime = filemtime($masterFile);
+ } else {
+ $mtime = false;
+ }
+
+ if (!$this->_specificOptions['ignore_missing_master_files'] && !$mtime) {
+ Zend_Cache::throwException('Unable to read master_file : ' . $masterFile);
+ }
+
+ $this->_masterFile_mtimes[$i] = $mtime;
+ $this->_specificOptions['master_files'][$i] = $masterFile;
+ if ($i === 0) { // to keep a compatibility
+ $this->_specificOptions['master_file'] = $masterFile;
+ }
+
+ $i++;
+ }
+ }
+
+ /**
+ * Change the master_file option
+ *
+ * To keep the compatibility
+ *
+ * @deprecated
+ * @param string $masterFile the complete path and name of the master file
+ */
+ public function setMasterFile($masterFile)
+ {
+ $this->setMasterFiles(array($masterFile));
+ }
+
+ /**
+ * Public frontend to set an option
+ *
+ * Just a wrapper to get a specific behaviour for master_file
+ *
+ * @param string $name Name of the option
+ * @param mixed $value Value of the option
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function setOption($name, $value)
+ {
+ if ($name == 'master_file') {
+ $this->setMasterFile($value);
+ } else if ($name == 'master_files') {
+ $this->setMasterFiles($value);
+ } else {
+ parent::setOption($name, $value);
+ }
+ }
+
+ /**
+ * Test if a cache is available for the given id and (if yes) return it (false else)
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use
+ * @return mixed|false Cached datas
+ */
+ public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
+ {
+ if (!$doNotTestCacheValidity) {
+ if ($this->test($id)) {
+ return parent::load($id, true, $doNotUnserialize);
+ }
+ return false;
+ }
+ return parent::load($id, true, $doNotUnserialize);
+ }
+
+ /**
+ * Test if a cache is available for the given id
+ *
+ * @param string $id Cache id
+ * @return int|false Last modified time of cache entry if it is available, false otherwise
+ */
+ public function test($id)
+ {
+ $lastModified = parent::test($id);
+ if ($lastModified) {
+ if ($this->_specificOptions['master_files_mode'] == self::MODE_AND) {
+ // MODE_AND
+ foreach($this->_masterFile_mtimes as $masterFileMTime) {
+ if ($masterFileMTime) {
+ if ($lastModified > $masterFileMTime) {
+ return $lastModified;
+ }
+ }
+ }
+ } else {
+ // MODE_OR
+ $res = true;
+ foreach($this->_masterFile_mtimes as $masterFileMTime) {
+ if ($masterFileMTime) {
+ if ($lastModified <= $masterFileMTime) {
+ return false;
+ }
+ }
+ }
+ return $lastModified;
+ }
+ }
+ return false;
+ }
+
+}
+
diff --git a/library/Zend/Cache/Frontend/Function.php b/library/Zend/Cache/Frontend/Function.php
new file mode 100644
index 0000000..dfbcf71
--- /dev/null
+++ b/library/Zend/Cache/Frontend/Function.php
@@ -0,0 +1,179 @@
+ (boolean) cache_by_default :
+ * - if true, function calls will be cached by default
+ *
+ * ====> (array) cached_functions :
+ * - an array of function names which will be cached (even if cache_by_default = false)
+ *
+ * ====> (array) non_cached_functions :
+ * - an array of function names which won't be cached (even if cache_by_default = true)
+ *
+ * @var array options
+ */
+ protected $_specificOptions = array(
+ 'cache_by_default' => true,
+ 'cached_functions' => array(),
+ 'non_cached_functions' => array()
+ );
+
+ /**
+ * Constructor
+ *
+ * @param array $options Associative array of options
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ while (list($name, $value) = each($options)) {
+ $this->setOption($name, $value);
+ }
+ $this->setOption('automatic_serialization', true);
+ }
+
+ /**
+ * Main method : call the specified function or get the result from cache
+ *
+ * @param callback $callback A valid callback
+ * @param array $parameters Function parameters
+ * @param array $tags Cache tags
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends
+ * @return mixed Result
+ */
+ public function call($callback, array $parameters = array(), $tags = array(), $specificLifetime = false, $priority = 8)
+ {
+ if (!is_callable($callback, true, $name)) {
+ Zend_Cache::throwException('Invalid callback');
+ }
+
+ $cacheBool1 = $this->_specificOptions['cache_by_default'];
+ $cacheBool2 = in_array($name, $this->_specificOptions['cached_functions']);
+ $cacheBool3 = in_array($name, $this->_specificOptions['non_cached_functions']);
+ $cache = (($cacheBool1 || $cacheBool2) && (!$cacheBool3));
+ if (!$cache) {
+ // Caching of this callback is disabled
+ return call_user_func_array($callback, $parameters);
+ }
+
+ $id = $this->_makeId($callback, $parameters);
+ if ( ($rs = $this->load($id)) && isset($rs[0], $rs[1])) {
+ // A cache is available
+ $output = $rs[0];
+ $return = $rs[1];
+ } else {
+ // A cache is not available (or not valid for this frontend)
+ ob_start();
+ ob_implicit_flush(false);
+ $return = call_user_func_array($callback, $parameters);
+ $output = ob_get_clean();
+ $data = array($output, $return);
+ $this->save($data, $id, $tags, $specificLifetime, $priority);
+ }
+
+ echo $output;
+ return $return;
+ }
+
+ /**
+ * ZF-9970
+ *
+ * @deprecated
+ */
+ private function _makeId($callback, array $args)
+ {
+ return $this->makeId($callback, $args);
+ }
+
+ /**
+ * Make a cache id from the function name and parameters
+ *
+ * @param callback $callback A valid callback
+ * @param array $args Function parameters
+ * @throws Zend_Cache_Exception
+ * @return string Cache id
+ */
+ public function makeId($callback, array $args = array())
+ {
+ if (!is_callable($callback, true, $name)) {
+ Zend_Cache::throwException('Invalid callback');
+ }
+
+ // functions, methods and classnames are case-insensitive
+ $name = strtolower($name);
+
+ // generate a unique id for object callbacks
+ if (is_object($callback)) { // Closures & __invoke
+ $object = $callback;
+ } elseif (isset($callback[0])) { // array($object, 'method')
+ $object = $callback[0];
+ }
+ if (isset($object)) {
+ try {
+ $tmp = @serialize($callback);
+ } catch (Exception $e) {
+ Zend_Cache::throwException($e->getMessage());
+ }
+ if (!$tmp) {
+ $lastErr = error_get_last();
+ Zend_Cache::throwException("Can't serialize callback object to generate id: {$lastErr['message']}");
+ }
+ $name.= '__' . $tmp;
+ }
+
+ // generate a unique id for arguments
+ $argsStr = '';
+ if ($args) {
+ try {
+ $argsStr = @serialize(array_values($args));
+ } catch (Exception $e) {
+ Zend_Cache::throwException($e->getMessage());
+ }
+ if (!$argsStr) {
+ $lastErr = error_get_last();
+ throw Zend_Cache::throwException("Can't serialize arguments to generate id: {$lastErr['message']}");
+ }
+ }
+
+ return md5($name . $argsStr);
+ }
+
+}
diff --git a/library/Zend/Cache/Frontend/Output.php b/library/Zend/Cache/Frontend/Output.php
new file mode 100644
index 0000000..290731b
--- /dev/null
+++ b/library/Zend/Cache/Frontend/Output.php
@@ -0,0 +1,105 @@
+_idStack = array();
+ }
+
+ /**
+ * Start the cache
+ *
+ * @param string $id Cache id
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @param boolean $echoData If set to true, datas are sent to the browser if the cache is hit (simpy returned else)
+ * @return mixed True if the cache is hit (false else) with $echoData=true (default) ; string else (datas)
+ */
+ public function start($id, $doNotTestCacheValidity = false, $echoData = true)
+ {
+ $data = $this->load($id, $doNotTestCacheValidity);
+ if ($data !== false) {
+ if ( $echoData ) {
+ echo($data);
+ return true;
+ } else {
+ return $data;
+ }
+ }
+ ob_start();
+ ob_implicit_flush(false);
+ $this->_idStack[] = $id;
+ return false;
+ }
+
+ /**
+ * Stop the cache
+ *
+ * @param array $tags Tags array
+ * @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
+ * @param string $forcedDatas If not null, force written datas with this
+ * @param boolean $echoData If set to true, datas are sent to the browser
+ * @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends
+ * @return void
+ */
+ public function end($tags = array(), $specificLifetime = false, $forcedDatas = null, $echoData = true, $priority = 8)
+ {
+ if ($forcedDatas === null) {
+ $data = ob_get_clean();
+ } else {
+ $data =& $forcedDatas;
+ }
+ $id = array_pop($this->_idStack);
+ if ($id === null) {
+ Zend_Cache::throwException('use of end() without a start()');
+ }
+ $this->save($data, $id, $tags, $specificLifetime, $priority);
+ if ($echoData) {
+ echo($data);
+ }
+ }
+
+}
diff --git a/library/Zend/Cache/Frontend/Page.php b/library/Zend/Cache/Frontend/Page.php
new file mode 100644
index 0000000..b9bd29f
--- /dev/null
+++ b/library/Zend/Cache/Frontend/Page.php
@@ -0,0 +1,404 @@
+ (boolean) http_conditional :
+ * - if true, http conditional mode is on
+ * WARNING : http_conditional OPTION IS NOT IMPLEMENTED FOR THE MOMENT (TODO)
+ *
+ * ====> (boolean) debug_header :
+ * - if true, a debug text is added before each cached pages
+ *
+ * ====> (boolean) content_type_memorization :
+ * - deprecated => use memorize_headers instead
+ * - if the Content-Type header is sent after the cache was started, the
+ * corresponding value can be memorized and replayed when the cache is hit
+ * (if false (default), the frontend doesn't take care of Content-Type header)
+ *
+ * ====> (array) memorize_headers :
+ * - an array of strings corresponding to some HTTP headers name. Listed headers
+ * will be stored with cache datas and "replayed" when the cache is hit
+ *
+ * ====> (array) default_options :
+ * - an associative array of default options :
+ * - (boolean) cache : cache is on by default if true
+ * - (boolean) cacheWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') :
+ * if true, cache is still on even if there are some variables in this superglobal array
+ * if false, cache is off if there are some variables in this superglobal array
+ * - (boolean) makeIdWithXXXVariables (XXXX = 'Get', 'Post', 'Session', 'Files' or 'Cookie') :
+ * if true, we have to use the content of this superglobal array to make a cache id
+ * if false, the cache id won't be dependent of the content of this superglobal array
+ * - (int) specific_lifetime : cache specific lifetime
+ * (false => global lifetime is used, null => infinite lifetime,
+ * integer => this lifetime is used), this "lifetime" is probably only
+ * usefull when used with "regexps" array
+ * - (array) tags : array of tags (strings)
+ * - (int) priority : integer between 0 (very low priority) and 10 (maximum priority) used by
+ * some particular backends
+ *
+ * ====> (array) regexps :
+ * - an associative array to set options only for some REQUEST_URI
+ * - keys are (pcre) regexps
+ * - values are associative array with specific options to set if the regexp matchs on $_SERVER['REQUEST_URI']
+ * (see default_options for the list of available options)
+ * - if several regexps match the $_SERVER['REQUEST_URI'], only the last one will be used
+ *
+ * @var array options
+ */
+ protected $_specificOptions = array(
+ 'http_conditional' => false,
+ 'debug_header' => false,
+ 'content_type_memorization' => false,
+ 'memorize_headers' => array(),
+ 'default_options' => array(
+ 'cache_with_get_variables' => false,
+ 'cache_with_post_variables' => false,
+ 'cache_with_session_variables' => false,
+ 'cache_with_files_variables' => false,
+ 'cache_with_cookie_variables' => false,
+ 'make_id_with_get_variables' => true,
+ 'make_id_with_post_variables' => true,
+ 'make_id_with_session_variables' => true,
+ 'make_id_with_files_variables' => true,
+ 'make_id_with_cookie_variables' => true,
+ 'cache' => true,
+ 'specific_lifetime' => false,
+ 'tags' => array(),
+ 'priority' => null
+ ),
+ 'regexps' => array()
+ );
+
+ /**
+ * Internal array to store some options
+ *
+ * @var array associative array of options
+ */
+ protected $_activeOptions = array();
+
+ /**
+ * If true, the page won't be cached
+ *
+ * @var boolean
+ */
+ protected $_cancel = false;
+
+ /**
+ * Constructor
+ *
+ * @param array $options Associative array of options
+ * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ public function __construct(array $options = array())
+ {
+ while (list($name, $value) = each($options)) {
+ $name = strtolower($name);
+ switch ($name) {
+ case 'regexps':
+ $this->_setRegexps($value);
+ break;
+ case 'default_options':
+ $this->_setDefaultOptions($value);
+ break;
+ case 'content_type_memorization':
+ $this->_setContentTypeMemorization($value);
+ break;
+ default:
+ $this->setOption($name, $value);
+ }
+ }
+ if (isset($this->_specificOptions['http_conditional'])) {
+ if ($this->_specificOptions['http_conditional']) {
+ Zend_Cache::throwException('http_conditional is not implemented for the moment !');
+ }
+ }
+ $this->setOption('automatic_serialization', true);
+ }
+
+ /**
+ * Specific setter for the 'default_options' option (with some additional tests)
+ *
+ * @param array $options Associative array
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected function _setDefaultOptions($options)
+ {
+ if (!is_array($options)) {
+ Zend_Cache::throwException('default_options must be an array !');
+ }
+ foreach ($options as $key=>$value) {
+ if (!is_string($key)) {
+ Zend_Cache::throwException("invalid option [$key] !");
+ }
+ $key = strtolower($key);
+ if (isset($this->_specificOptions['default_options'][$key])) {
+ $this->_specificOptions['default_options'][$key] = $value;
+ }
+ }
+ }
+
+ /**
+ * Set the deprecated contentTypeMemorization option
+ *
+ * @param boolean $value value
+ * @return void
+ * @deprecated
+ */
+ protected function _setContentTypeMemorization($value)
+ {
+ $found = null;
+ foreach ($this->_specificOptions['memorize_headers'] as $key => $value) {
+ if (strtolower($value) == 'content-type') {
+ $found = $key;
+ }
+ }
+ if ($value) {
+ if (!$found) {
+ $this->_specificOptions['memorize_headers'][] = 'Content-Type';
+ }
+ } else {
+ if ($found) {
+ unset($this->_specificOptions['memorize_headers'][$found]);
+ }
+ }
+ }
+
+ /**
+ * Specific setter for the 'regexps' option (with some additional tests)
+ *
+ * @param array $options Associative array
+ * @throws Zend_Cache_Exception
+ * @return void
+ */
+ protected function _setRegexps($regexps)
+ {
+ if (!is_array($regexps)) {
+ Zend_Cache::throwException('regexps option must be an array !');
+ }
+ foreach ($regexps as $regexp=>$conf) {
+ if (!is_array($conf)) {
+ Zend_Cache::throwException('regexps option must be an array of arrays !');
+ }
+ $validKeys = array_keys($this->_specificOptions['default_options']);
+ foreach ($conf as $key=>$value) {
+ if (!is_string($key)) {
+ Zend_Cache::throwException("unknown option [$key] !");
+ }
+ $key = strtolower($key);
+ if (!in_array($key, $validKeys)) {
+ unset($regexps[$regexp][$key]);
+ }
+ }
+ }
+ $this->setOption('regexps', $regexps);
+ }
+
+ /**
+ * Start the cache
+ *
+ * @param string $id (optional) A cache id (if you set a value here, maybe you have to use Output frontend instead)
+ * @param boolean $doNotDie For unit testing only !
+ * @return boolean True if the cache is hit (false else)
+ */
+ public function start($id = false, $doNotDie = false)
+ {
+ $this->_cancel = false;
+ $lastMatchingRegexp = null;
+ if (isset($_SERVER['REQUEST_URI'])) {
+ foreach ($this->_specificOptions['regexps'] as $regexp => $conf) {
+ if (preg_match("`$regexp`", $_SERVER['REQUEST_URI'])) {
+ $lastMatchingRegexp = $regexp;
+ }
+ }
+ }
+ $this->_activeOptions = $this->_specificOptions['default_options'];
+ if ($lastMatchingRegexp !== null) {
+ $conf = $this->_specificOptions['regexps'][$lastMatchingRegexp];
+ foreach ($conf as $key=>$value) {
+ $this->_activeOptions[$key] = $value;
+ }
+ }
+ if (!($this->_activeOptions['cache'])) {
+ return false;
+ }
+ if (!$id) {
+ $id = $this->_makeId();
+ if (!$id) {
+ return false;
+ }
+ }
+ $array = $this->load($id);
+ if ($array !== false) {
+ $data = $array['data'];
+ $headers = $array['headers'];
+ if (!headers_sent()) {
+ foreach ($headers as $key=>$headerCouple) {
+ $name = $headerCouple[0];
+ $value = $headerCouple[1];
+ header("$name: $value");
+ }
+ }
+ if ($this->_specificOptions['debug_header']) {
+ echo 'DEBUG HEADER : This is a cached page !';
+ }
+ echo $data;
+ if ($doNotDie) {
+ return true;
+ }
+ die();
+ }
+ ob_start(array($this, '_flush'));
+ ob_implicit_flush(false);
+ return false;
+ }
+
+ /**
+ * Cancel the current caching process
+ */
+ public function cancel()
+ {
+ $this->_cancel = true;
+ }
+
+ /**
+ * callback for output buffering
+ * (shouldn't really be called manually)
+ *
+ * @param string $data Buffered output
+ * @return string Data to send to browser
+ */
+ public function _flush($data)
+ {
+ if ($this->_cancel) {
+ return $data;
+ }
+ $contentType = null;
+ $storedHeaders = array();
+ $headersList = headers_list();
+ foreach($this->_specificOptions['memorize_headers'] as $key=>$headerName) {
+ foreach ($headersList as $headerSent) {
+ $tmp = explode(':', $headerSent);
+ $headerSentName = trim(array_shift($tmp));
+ if (strtolower($headerName) == strtolower($headerSentName)) {
+ $headerSentValue = trim(implode(':', $tmp));
+ $storedHeaders[] = array($headerSentName, $headerSentValue);
+ }
+ }
+ }
+ $array = array(
+ 'data' => $data,
+ 'headers' => $storedHeaders
+ );
+ $this->save($array, null, $this->_activeOptions['tags'], $this->_activeOptions['specific_lifetime'], $this->_activeOptions['priority']);
+ return $data;
+ }
+
+ /**
+ * Make an id depending on REQUEST_URI and superglobal arrays (depending on options)
+ *
+ * @return mixed|false a cache id (string), false if the cache should have not to be used
+ */
+ protected function _makeId()
+ {
+ $tmp = $_SERVER['REQUEST_URI'];
+ $array = explode('?', $tmp, 2);
+ $tmp = $array[0];
+ foreach (array('Get', 'Post', 'Session', 'Files', 'Cookie') as $arrayName) {
+ $tmp2 = $this->_makePartialId($arrayName, $this->_activeOptions['cache_with_' . strtolower($arrayName) . '_variables'], $this->_activeOptions['make_id_with_' . strtolower($arrayName) . '_variables']);
+ if ($tmp2===false) {
+ return false;
+ }
+ $tmp = $tmp . $tmp2;
+ }
+ return md5($tmp);
+ }
+
+ /**
+ * Make a partial id depending on options
+ *
+ * @param string $arrayName Superglobal array name
+ * @param bool $bool1 If true, cache is still on even if there are some variables in the superglobal array
+ * @param bool $bool2 If true, we have to use the content of the superglobal array to make a partial id
+ * @return mixed|false Partial id (string) or false if the cache should have not to be used
+ */
+ protected function _makePartialId($arrayName, $bool1, $bool2)
+ {
+ switch ($arrayName) {
+ case 'Get':
+ $var = $_GET;
+ break;
+ case 'Post':
+ $var = $_POST;
+ break;
+ case 'Session':
+ if (isset($_SESSION)) {
+ $var = $_SESSION;
+ } else {
+ $var = null;
+ }
+ break;
+ case 'Cookie':
+ if (isset($_COOKIE)) {
+ $var = $_COOKIE;
+ } else {
+ $var = null;
+ }
+ break;
+ case 'Files':
+ $var = $_FILES;
+ break;
+ default:
+ return false;
+ }
+ if ($bool1) {
+ if ($bool2) {
+ return serialize($var);
+ }
+ return '';
+ }
+ if (count($var) > 0) {
+ return false;
+ }
+ return '';
+ }
+
+}
diff --git a/library/Zend/Cache/Manager.php b/library/Zend/Cache/Manager.php
new file mode 100644
index 0000000..5d73f7e
--- /dev/null
+++ b/library/Zend/Cache/Manager.php
@@ -0,0 +1,298 @@
+ array(
+ 'frontend' => array(
+ 'name' => 'Core',
+ 'options' => array(
+ 'automatic_serialization' => true,
+ ),
+ ),
+ 'backend' => array(
+ 'name' => 'File',
+ 'options' => array(
+ // use system temp dir by default of file backend
+ // 'cache_dir' => '../cache',
+ ),
+ ),
+ ),
+
+ // Static Page HTML Cache
+ 'page' => array(
+ 'frontend' => array(
+ 'name' => 'Capture',
+ 'options' => array(
+ 'ignore_user_abort' => true,
+ ),
+ ),
+ 'backend' => array(
+ 'name' => 'Static',
+ 'options' => array(
+ 'public_dir' => '../public',
+ ),
+ ),
+ ),
+
+ // Tag Cache
+ 'pagetag' => array(
+ 'frontend' => array(
+ 'name' => 'Core',
+ 'options' => array(
+ 'automatic_serialization' => true,
+ 'lifetime' => null
+ ),
+ ),
+ 'backend' => array(
+ 'name' => 'File',
+ 'options' => array(
+ // use system temp dir by default of file backend
+ // 'cache_dir' => '../cache',
+ // use default umask of file backend
+ // 'cache_file_umask' => 0644
+ ),
+ ),
+ ),
+ );
+
+ /**
+ * Set a new cache for the Cache Manager to contain
+ *
+ * @param string $name
+ * @param Zend_Cache_Core $cache
+ * @return Zend_Cache_Manager
+ */
+ public function setCache($name, Zend_Cache_Core $cache)
+ {
+ $this->_caches[$name] = $cache;
+ return $this;
+ }
+
+ /**
+ * Check if the Cache Manager contains the named cache object, or a named
+ * configuration template to lazy load the cache object
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function hasCache($name)
+ {
+ if (isset($this->_caches[$name])
+ || $this->hasCacheTemplate($name)
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Fetch the named cache object, or instantiate and return a cache object
+ * using a named configuration template
+ *
+ * @param string $name
+ * @return Zend_Cache_Core
+ */
+ public function getCache($name)
+ {
+ if (isset($this->_caches[$name])) {
+ return $this->_caches[$name];
+ }
+ if (isset($this->_optionTemplates[$name])) {
+ if ($name == self::PAGECACHE
+ && (!isset($this->_optionTemplates[$name]['backend']['options']['tag_cache'])
+ || !$this->_optionTemplates[$name]['backend']['options']['tag_cache'] instanceof Zend_Cache_Core)
+ ) {
+ $this->_optionTemplates[$name]['backend']['options']['tag_cache']
+ = $this->getCache(self::PAGETAGCACHE);
+ }
+
+ $this->_caches[$name] = Zend_Cache::factory(
+ $this->_optionTemplates[$name]['frontend']['name'],
+ $this->_optionTemplates[$name]['backend']['name'],
+ isset($this->_optionTemplates[$name]['frontend']['options']) ? $this->_optionTemplates[$name]['frontend']['options'] : array(),
+ isset($this->_optionTemplates[$name]['backend']['options']) ? $this->_optionTemplates[$name]['backend']['options'] : array(),
+ isset($this->_optionTemplates[$name]['frontend']['customFrontendNaming']) ? $this->_optionTemplates[$name]['frontend']['customFrontendNaming'] : false,
+ isset($this->_optionTemplates[$name]['backend']['customBackendNaming']) ? $this->_optionTemplates[$name]['backend']['customBackendNaming'] : false,
+ isset($this->_optionTemplates[$name]['frontendBackendAutoload']) ? $this->_optionTemplates[$name]['frontendBackendAutoload'] : false
+ );
+
+ return $this->_caches[$name];
+ }
+ }
+
+ /**
+ * Fetch all available caches
+ *
+ * @return array An array of all available caches with it's names as key
+ */
+ public function getCaches()
+ {
+ $caches = $this->_caches;
+ foreach ($this->_optionTemplates as $name => $tmp) {
+ if (!isset($caches[$name])) {
+ $caches[$name] = $this->getCache($name);
+ }
+ }
+ return $caches;
+ }
+
+ /**
+ * Set a named configuration template from which a cache object can later
+ * be lazy loaded
+ *
+ * @param string $name
+ * @param array $options
+ * @return Zend_Cache_Manager
+ */
+ public function setCacheTemplate($name, $options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Cache/Exception.php';
+ throw new Zend_Cache_Exception('Options passed must be in'
+ . ' an associative array or instance of Zend_Config');
+ }
+ $this->_optionTemplates[$name] = $options;
+ return $this;
+ }
+
+ /**
+ * Check if the named configuration template
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function hasCacheTemplate($name)
+ {
+ if (isset($this->_optionTemplates[$name])) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the named configuration template
+ *
+ * @param string $name
+ * @return array
+ */
+ public function getCacheTemplate($name)
+ {
+ if (isset($this->_optionTemplates[$name])) {
+ return $this->_optionTemplates[$name];
+ }
+ }
+
+ /**
+ * Pass an array containing changes to be applied to a named
+ * configuration
+ * template
+ *
+ * @param string $name
+ * @param array $options
+ * @return Zend_Cache_Manager
+ * @throws Zend_Cache_Exception for invalid options format or if option templates do not have $name
+ */
+ public function setTemplateOptions($name, $options)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Cache/Exception.php';
+ throw new Zend_Cache_Exception('Options passed must be in'
+ . ' an associative array or instance of Zend_Config');
+ }
+ if (!isset($this->_optionTemplates[$name])) {
+ throw new Zend_Cache_Exception('A cache configuration template'
+ . 'does not exist with the name "' . $name . '"');
+ }
+ $this->_optionTemplates[$name]
+ = $this->_mergeOptions($this->_optionTemplates[$name], $options);
+ return $this;
+ }
+
+ /**
+ * Simple method to merge two configuration arrays
+ *
+ * @param array $current
+ * @param array $options
+ * @return array
+ */
+ protected function _mergeOptions(array $current, array $options)
+ {
+ if (isset($options['frontend']['name'])) {
+ $current['frontend']['name'] = $options['frontend']['name'];
+ }
+ if (isset($options['backend']['name'])) {
+ $current['backend']['name'] = $options['backend']['name'];
+ }
+ if (isset($options['frontend']['options'])) {
+ foreach ($options['frontend']['options'] as $key=>$value) {
+ $current['frontend']['options'][$key] = $value;
+ }
+ }
+ if (isset($options['backend']['options'])) {
+ foreach ($options['backend']['options'] as $key=>$value) {
+ $current['backend']['options'][$key] = $value;
+ }
+ }
+ return $current;
+ }
+}
diff --git a/library/Zend/Captcha/Adapter.php b/library/Zend/Captcha/Adapter.php
new file mode 100644
index 0000000..c0e7d97
--- /dev/null
+++ b/library/Zend/Captcha/Adapter.php
@@ -0,0 +1,76 @@
+_name;
+ }
+
+ /**
+ * Set name
+ *
+ * @param string $name
+ */
+ public function setName($name)
+ {
+ $this->_name = $name;
+ return $this;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param array|Zend_Config $options
+ * @return void
+ */
+ public function __construct($options = null)
+ {
+ // Set options
+ if (is_array($options)) {
+ $this->setOptions($options);
+ } else if ($options instanceof Zend_Config) {
+ $this->setConfig($options);
+ }
+ }
+
+ /**
+ * Set single option for the object
+ *
+ * @param string $key
+ * @param string $value
+ * @return Zend_Form_Element
+ */
+ public function setOption($key, $value)
+ {
+ if (in_array(strtolower($key), $this->_skipOptions)) {
+ return $this;
+ }
+
+ $method = 'set' . ucfirst ($key);
+ if (method_exists ($this, $method)) {
+ // Setter exists; use it
+ $this->$method ($value);
+ $this->_options[$key] = $value;
+ } elseif (property_exists($this, $key)) {
+ // Assume it's metadata
+ $this->$key = $value;
+ $this->_options[$key] = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Set object state from options array
+ *
+ * @param array $options
+ * @return Zend_Form_Element
+ */
+ public function setOptions($options = null)
+ {
+ foreach ($options as $key => $value) {
+ $this->setOption($key, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve options representing object state
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
+ /**
+ * Set object state from config object
+ *
+ * @param Zend_Config $config
+ * @return Zend_Captcha_Base
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ return $this->setOptions($config->toArray());
+ }
+
+ /**
+ * Get optional decorator
+ *
+ * By default, return null, indicating no extra decorator needed.
+ *
+ * @return null
+ */
+ public function getDecorator()
+ {
+ return null;
+ }
+}
diff --git a/library/Zend/Captcha/Dumb.php b/library/Zend/Captcha/Dumb.php
new file mode 100644
index 0000000..46b8d05
--- /dev/null
+++ b/library/Zend/Captcha/Dumb.php
@@ -0,0 +1,52 @@
+'
+ . strrev($this->getWord())
+ . '';
+ }
+}
diff --git a/library/Zend/Captcha/Exception.php b/library/Zend/Captcha/Exception.php
new file mode 100644
index 0000000..1b4f27f
--- /dev/null
+++ b/library/Zend/Captcha/Exception.php
@@ -0,0 +1,37 @@
+_figlet = new Zend_Text_Figlet($options);
+ }
+
+ /**
+ * Generate new captcha
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $this->_useNumbers = false;
+ return parent::generate();
+ }
+
+ /**
+ * Display the captcha
+ *
+ * @param Zend_View_Interface $view
+ * @param mixed $element
+ * @return string
+ */
+ public function render(Zend_View_Interface $view = null, $element = null)
+ {
+ return ''
+ . $this->_figlet->render($this->getWord())
+ . "
\n";
+ }
+}
diff --git a/library/Zend/Captcha/Image.php b/library/Zend/Captcha/Image.php
new file mode 100644
index 0000000..de644ea
--- /dev/null
+++ b/library/Zend/Captcha/Image.php
@@ -0,0 +1,609 @@
+_imgAlt;
+ }
+ /**
+ * @return string
+ */
+ public function getStartImage ()
+ {
+ return $this->_startImage;
+ }
+ /**
+ * @return int
+ */
+ public function getDotNoiseLevel ()
+ {
+ return $this->_dotNoiseLevel;
+ }
+ /**
+ * @return int
+ */
+ public function getLineNoiseLevel ()
+ {
+ return $this->_lineNoiseLevel;
+ }
+ /**
+ * Get captcha expiration
+ *
+ * @return int
+ */
+ public function getExpiration()
+ {
+ return $this->_expiration;
+ }
+
+ /**
+ * Get garbage collection frequency
+ *
+ * @return int
+ */
+ public function getGcFreq()
+ {
+ return $this->_gcFreq;
+ }
+ /**
+ * Get font to use when generating captcha
+ *
+ * @return string
+ */
+ public function getFont()
+ {
+ return $this->_font;
+ }
+
+ /**
+ * Get font size
+ *
+ * @return int
+ */
+ public function getFontSize()
+ {
+ return $this->_fsize;
+ }
+
+ /**
+ * Get captcha image height
+ *
+ * @return int
+ */
+ public function getHeight()
+ {
+ return $this->_height;
+ }
+
+ /**
+ * Get captcha image directory
+ *
+ * @return string
+ */
+ public function getImgDir()
+ {
+ return $this->_imgDir;
+ }
+ /**
+ * Get captcha image base URL
+ *
+ * @return string
+ */
+ public function getImgUrl()
+ {
+ return $this->_imgUrl;
+ }
+ /**
+ * Get captcha image file suffix
+ *
+ * @return string
+ */
+ public function getSuffix()
+ {
+ return $this->_suffix;
+ }
+ /**
+ * Get captcha image width
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ return $this->_width;
+ }
+ /**
+ * @param string $startImage
+ */
+ public function setStartImage ($startImage)
+ {
+ $this->_startImage = $startImage;
+ return $this;
+ }
+ /**
+ * @param int $dotNoiseLevel
+ */
+ public function setDotNoiseLevel ($dotNoiseLevel)
+ {
+ $this->_dotNoiseLevel = $dotNoiseLevel;
+ return $this;
+ }
+ /**
+ * @param int $lineNoiseLevel
+ */
+ public function setLineNoiseLevel ($lineNoiseLevel)
+ {
+ $this->_lineNoiseLevel = $lineNoiseLevel;
+ return $this;
+ }
+
+ /**
+ * Set captcha expiration
+ *
+ * @param int $expiration
+ * @return Zend_Captcha_Image
+ */
+ public function setExpiration($expiration)
+ {
+ $this->_expiration = $expiration;
+ return $this;
+ }
+
+ /**
+ * Set garbage collection frequency
+ *
+ * @param int $gcFreq
+ * @return Zend_Captcha_Image
+ */
+ public function setGcFreq($gcFreq)
+ {
+ $this->_gcFreq = $gcFreq;
+ return $this;
+ }
+
+ /**
+ * Set captcha font
+ *
+ * @param string $font
+ * @return Zend_Captcha_Image
+ */
+ public function setFont($font)
+ {
+ $this->_font = $font;
+ return $this;
+ }
+
+ /**
+ * Set captcha font size
+ *
+ * @param int $fsize
+ * @return Zend_Captcha_Image
+ */
+ public function setFontSize($fsize)
+ {
+ $this->_fsize = $fsize;
+ return $this;
+ }
+
+ /**
+ * Set captcha image height
+ *
+ * @param int $height
+ * @return Zend_Captcha_Image
+ */
+ public function setHeight($height)
+ {
+ $this->_height = $height;
+ return $this;
+ }
+
+ /**
+ * Set captcha image storage directory
+ *
+ * @param string $imgDir
+ * @return Zend_Captcha_Image
+ */
+ public function setImgDir($imgDir)
+ {
+ $this->_imgDir = rtrim($imgDir, "/\\") . '/';
+ return $this;
+ }
+
+ /**
+ * Set captcha image base URL
+ *
+ * @param string $imgUrl
+ * @return Zend_Captcha_Image
+ */
+ public function setImgUrl($imgUrl)
+ {
+ $this->_imgUrl = rtrim($imgUrl, "/\\") . '/';
+ return $this;
+ }
+ /**
+ * @param string $imgAlt
+ */
+ public function setImgAlt ($imgAlt)
+ {
+ $this->_imgAlt = $imgAlt;
+ return $this;
+ }
+
+ /**
+ * Set captch image filename suffix
+ *
+ * @param string $suffix
+ * @return Zend_Captcha_Image
+ */
+ public function setSuffix($suffix)
+ {
+ $this->_suffix = $suffix;
+ return $this;
+ }
+
+ /**
+ * Set captcha image width
+ *
+ * @param int $width
+ * @return Zend_Captcha_Image
+ */
+ public function setWidth($width)
+ {
+ $this->_width = $width;
+ return $this;
+ }
+
+ /**
+ * Generate random frequency
+ *
+ * @return float
+ */
+ protected function _randomFreq()
+ {
+ return mt_rand(700000, 1000000) / 15000000;
+ }
+
+ /**
+ * Generate random phase
+ *
+ * @return float
+ */
+ protected function _randomPhase()
+ {
+ // random phase from 0 to pi
+ return mt_rand(0, 3141592) / 1000000;
+ }
+
+ /**
+ * Generate random character size
+ *
+ * @return int
+ */
+ protected function _randomSize()
+ {
+ return mt_rand(300, 700) / 100;
+ }
+
+ /**
+ * Generate captcha
+ *
+ * @return string captcha ID
+ */
+ public function generate()
+ {
+ $id = parent::generate();
+ $tries = 5;
+ // If there's already such file, try creating a new ID
+ while($tries-- && file_exists($this->getImgDir() . $id . $this->getSuffix())) {
+ $id = $this->_generateRandomId();
+ $this->_setId($id);
+ }
+ $this->_generateImage($id, $this->getWord());
+
+ if (mt_rand(1, $this->getGcFreq()) == 1) {
+ $this->_gc();
+ }
+ return $id;
+ }
+
+ /**
+ * Generate image captcha
+ *
+ * Override this function if you want different image generator
+ * Wave transform from http://www.captcha.ru/captchas/multiwave/
+ *
+ * @param string $id Captcha ID
+ * @param string $word Captcha word
+ */
+ protected function _generateImage($id, $word)
+ {
+ if (!extension_loaded("gd")) {
+ require_once 'Zend/Captcha/Exception.php';
+ throw new Zend_Captcha_Exception("Image CAPTCHA requires GD extension");
+ }
+
+ if (!function_exists("imagepng")) {
+ require_once 'Zend/Captcha/Exception.php';
+ throw new Zend_Captcha_Exception("Image CAPTCHA requires PNG support");
+ }
+
+ if (!function_exists("imageftbbox")) {
+ require_once 'Zend/Captcha/Exception.php';
+ throw new Zend_Captcha_Exception("Image CAPTCHA requires FT fonts support");
+ }
+
+ $font = $this->getFont();
+
+ if (empty($font)) {
+ require_once 'Zend/Captcha/Exception.php';
+ throw new Zend_Captcha_Exception("Image CAPTCHA requires font");
+ }
+
+ $w = $this->getWidth();
+ $h = $this->getHeight();
+ $fsize = $this->getFontSize();
+
+ $img_file = $this->getImgDir() . $id . $this->getSuffix();
+ if(empty($this->_startImage)) {
+ $img = imagecreatetruecolor($w, $h);
+ } else {
+ $img = imagecreatefrompng($this->_startImage);
+ if(!$img) {
+ require_once 'Zend/Captcha/Exception.php';
+ throw new Zend_Captcha_Exception("Can not load start image");
+ }
+ $w = imagesx($img);
+ $h = imagesy($img);
+ }
+ $text_color = imagecolorallocate($img, 0, 0, 0);
+ $bg_color = imagecolorallocate($img, 255, 255, 255);
+ imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bg_color);
+ $textbox = imageftbbox($fsize, 0, $font, $word);
+ $x = ($w - ($textbox[2] - $textbox[0])) / 2;
+ $y = ($h - ($textbox[7] - $textbox[1])) / 2;
+ imagefttext($img, $fsize, 0, $x, $y, $text_color, $font, $word);
+
+ // generate noise
+ for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
+ imagefilledellipse($img, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
+ }
+ for($i=0; $i<$this->_lineNoiseLevel; $i++) {
+ imageline($img, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
+ }
+
+ // transformed image
+ $img2 = imagecreatetruecolor($w, $h);
+ $bg_color = imagecolorallocate($img2, 255, 255, 255);
+ imagefilledrectangle($img2, 0, 0, $w-1, $h-1, $bg_color);
+ // apply wave transforms
+ $freq1 = $this->_randomFreq();
+ $freq2 = $this->_randomFreq();
+ $freq3 = $this->_randomFreq();
+ $freq4 = $this->_randomFreq();
+
+ $ph1 = $this->_randomPhase();
+ $ph2 = $this->_randomPhase();
+ $ph3 = $this->_randomPhase();
+ $ph4 = $this->_randomPhase();
+
+ $szx = $this->_randomSize();
+ $szy = $this->_randomSize();
+
+ for ($x = 0; $x < $w; $x++) {
+ for ($y = 0; $y < $h; $y++) {
+ $sx = $x + (sin($x*$freq1 + $ph1) + sin($y*$freq3 + $ph3)) * $szx;
+ $sy = $y + (sin($x*$freq2 + $ph2) + sin($y*$freq4 + $ph4)) * $szy;
+
+ if ($sx < 0 || $sy < 0 || $sx >= $w - 1 || $sy >= $h - 1) {
+ continue;
+ } else {
+ $color = (imagecolorat($img, $sx, $sy) >> 16) & 0xFF;
+ $color_x = (imagecolorat($img, $sx + 1, $sy) >> 16) & 0xFF;
+ $color_y = (imagecolorat($img, $sx, $sy + 1) >> 16) & 0xFF;
+ $color_xy = (imagecolorat($img, $sx + 1, $sy + 1) >> 16) & 0xFF;
+ }
+ if ($color == 255 && $color_x == 255 && $color_y == 255 && $color_xy == 255) {
+ // ignore background
+ continue;
+ } elseif ($color == 0 && $color_x == 0 && $color_y == 0 && $color_xy == 0) {
+ // transfer inside of the image as-is
+ $newcolor = 0;
+ } else {
+ // do antialiasing for border items
+ $frac_x = $sx-floor($sx);
+ $frac_y = $sy-floor($sy);
+ $frac_x1 = 1-$frac_x;
+ $frac_y1 = 1-$frac_y;
+
+ $newcolor = $color * $frac_x1 * $frac_y1
+ + $color_x * $frac_x * $frac_y1
+ + $color_y * $frac_x1 * $frac_y
+ + $color_xy * $frac_x * $frac_y;
+ }
+ imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newcolor, $newcolor, $newcolor));
+ }
+ }
+
+ // generate noise
+ for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
+ imagefilledellipse($img2, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
+ }
+ for ($i=0; $i<$this->_lineNoiseLevel; $i++) {
+ imageline($img2, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
+ }
+
+ imagepng($img2, $img_file);
+ imagedestroy($img);
+ imagedestroy($img2);
+ }
+
+ /**
+ * Remove old files from image directory
+ *
+ */
+ protected function _gc()
+ {
+ $expire = time() - $this->getExpiration();
+ $imgdir = $this->getImgDir();
+ if(!$imgdir || strlen($imgdir) < 2) {
+ // safety guard
+ return;
+ }
+ $suffixLength = strlen($this->_suffix);
+ foreach (new DirectoryIterator($imgdir) as $file) {
+ if (!$file->isDot() && !$file->isDir()) {
+ if ($file->getMTime() < $expire) {
+ // only deletes files ending with $this->_suffix
+ if (substr($file->getFilename(), -($suffixLength)) == $this->_suffix) {
+ unlink($file->getPathname());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Display the captcha
+ *
+ * @param Zend_View_Interface $view
+ * @param mixed $element
+ * @return string
+ */
+ public function render(Zend_View_Interface $view = null, $element = null)
+ {
+ $endTag = ' />';
+ if (($view instanceof Zend_View_Abstract) && !$view->doctype()->isXhtml()) {
+ $endTag = '>';
+ }
+ return ' 'Missing captcha fields',
+ self::ERR_CAPTCHA => 'Failed to validate captcha',
+ self::BAD_CAPTCHA => 'Captcha value is wrong: %value%',
+ );
+
+ /**
+ * Retrieve ReCaptcha Private key
+ *
+ * @return string
+ */
+ public function getPrivkey()
+ {
+ return $this->getService()->getPrivateKey();
+ }
+
+ /**
+ * Retrieve ReCaptcha Public key
+ *
+ * @return string
+ */
+ public function getPubkey()
+ {
+ return $this->getService()->getPublicKey();
+ }
+
+ /**
+ * Set ReCaptcha Private key
+ *
+ * @param string $privkey
+ * @return Zend_Captcha_ReCaptcha
+ */
+ public function setPrivkey($privkey)
+ {
+ $this->getService()->setPrivateKey($privkey);
+ return $this;
+ }
+
+ /**
+ * Set ReCaptcha public key
+ *
+ * @param string $pubkey
+ * @return Zend_Captcha_ReCaptcha
+ */
+ public function setPubkey($pubkey)
+ {
+ $this->getService()->setPublicKey($pubkey);
+ return $this;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param array|Zend_Config $options
+ * @return void
+ */
+ public function __construct($options = null)
+ {
+ $this->setService(new Zend_Service_ReCaptcha());
+ $this->_serviceParams = $this->getService()->getParams();
+ $this->_serviceOptions = $this->getService()->getOptions();
+
+ parent::__construct($options);
+
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ }
+ if (!empty($options)) {
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Set service object
+ *
+ * @param Zend_Service_ReCaptcha $service
+ * @return Zend_Captcha_ReCaptcha
+ */
+ public function setService(Zend_Service_ReCaptcha $service)
+ {
+ $this->_service = $service;
+ return $this;
+ }
+
+ /**
+ * Retrieve ReCaptcha service object
+ *
+ * @return Zend_Service_ReCaptcha
+ */
+ public function getService()
+ {
+ return $this->_service;
+ }
+
+ /**
+ * Set option
+ *
+ * If option is a service parameter, proxies to the service. The same
+ * goes for any service options (distinct from service params)
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return Zend_Captcha_ReCaptcha
+ */
+ public function setOption($key, $value)
+ {
+ $service = $this->getService();
+ if (isset($this->_serviceParams[$key])) {
+ $service->setParam($key, $value);
+ return $this;
+ }
+ if (isset($this->_serviceOptions[$key])) {
+ $service->setOption($key, $value);
+ return $this;
+ }
+ return parent::setOption($key, $value);
+ }
+
+ /**
+ * Generate captcha
+ *
+ * @see Zend_Form_Captcha_Adapter::generate()
+ * @return string
+ */
+ public function generate()
+ {
+ return "";
+ }
+
+ /**
+ * Validate captcha
+ *
+ * @see Zend_Validate_Interface::isValid()
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value, $context = null)
+ {
+ if (!is_array($value) && !is_array($context)) {
+ $this->_error(self::MISSING_VALUE);
+ return false;
+ }
+
+ if (!is_array($value) && is_array($context)) {
+ $value = $context;
+ }
+
+ if (empty($value[$this->_CHALLENGE]) || empty($value[$this->_RESPONSE])) {
+ $this->_error(self::MISSING_VALUE);
+ return false;
+ }
+
+ $service = $this->getService();
+
+ $res = $service->verify($value[$this->_CHALLENGE], $value[$this->_RESPONSE]);
+
+ if (!$res) {
+ $this->_error(self::ERR_CAPTCHA);
+ return false;
+ }
+
+ if (!$res->isValid()) {
+ $this->_error(self::BAD_CAPTCHA, $res->getErrorCode());
+ $service->setParam('error', $res->getErrorCode());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Render captcha
+ *
+ * @param Zend_View_Interface $view
+ * @param mixed $element
+ * @return string
+ */
+ public function render(Zend_View_Interface $view = null, $element = null)
+ {
+ $name = null;
+ if ($element instanceof Zend_Form_Element) {
+ $name = $element->getBelongsTo();
+ }
+ return $this->getService()->getHTML($name);
+ }
+
+ /**
+ * Get captcha decorator
+ *
+ * @return string
+ */
+ public function getDecorator()
+ {
+ return "Captcha_ReCaptcha";
+ }
+}
diff --git a/library/Zend/Captcha/Word.php b/library/Zend/Captcha/Word.php
new file mode 100644
index 0000000..b5ab2f9
--- /dev/null
+++ b/library/Zend/Captcha/Word.php
@@ -0,0 +1,418 @@
+ 'Empty captcha value',
+ self::MISSING_ID => 'Captcha ID field is missing',
+ self::BAD_CAPTCHA => 'Captcha value is wrong',
+ );
+
+ /**
+ * Length of the word to generate
+ *
+ * @var integer
+ */
+ protected $_wordlen = 8;
+
+ /**
+ * Retrieve session class to utilize
+ *
+ * @return string
+ */
+ public function getSessionClass()
+ {
+ return $this->_sessionClass;
+ }
+
+ /**
+ * Set session class for persistence
+ *
+ * @param string $_sessionClass
+ * @return Zend_Captcha_Word
+ */
+ public function setSessionClass($_sessionClass)
+ {
+ $this->_sessionClass = $_sessionClass;
+ return $this;
+ }
+
+ /**
+ * Retrieve word length to use when genrating captcha
+ *
+ * @return integer
+ */
+ public function getWordlen()
+ {
+ return $this->_wordlen;
+ }
+
+ /**
+ * Set word length of captcha
+ *
+ * @param integer $wordlen
+ * @return Zend_Captcha_Word
+ */
+ public function setWordlen($wordlen)
+ {
+ $this->_wordlen = $wordlen;
+ return $this;
+ }
+
+ /**
+ * Retrieve captcha ID
+ *
+ * @return string
+ */
+ public function getId ()
+ {
+ if (null === $this->_id) {
+ $this->_setId($this->_generateRandomId());
+ }
+ return $this->_id;
+ }
+
+ /**
+ * Set captcha identifier
+ *
+ * @param string $id
+ * return Zend_Captcha_Word
+ */
+ protected function _setId ($id)
+ {
+ $this->_id = $id;
+ return $this;
+ }
+
+ /**
+ * Set timeout for session token
+ *
+ * @param int $ttl
+ * @return Zend_Captcha_Word
+ */
+ public function setTimeout($ttl)
+ {
+ $this->_timeout = (int) $ttl;
+ return $this;
+ }
+
+ /**
+ * Get session token timeout
+ *
+ * @return int
+ */
+ public function getTimeout()
+ {
+ return $this->_timeout;
+ }
+
+ /**
+ * Sets if session should be preserved on generate()
+ *
+ * @param bool $keepSession Should session be kept on generate()?
+ * @return Zend_Captcha_Word
+ */
+ public function setKeepSession($keepSession)
+ {
+ $this->_keepSession = $keepSession;
+ return $this;
+ }
+
+ /**
+ * Numbers should be included in the pattern?
+ *
+ * @return bool
+ */
+ public function getUseNumbers()
+ {
+ return $this->_useNumbers;
+ }
+
+ /**
+ * Set if numbers should be included in the pattern
+ *
+ * @param bool $_useNumbers numbers should be included in the pattern?
+ * @return Zend_Captcha_Word
+ */
+ public function setUseNumbers($_useNumbers)
+ {
+ $this->_useNumbers = $_useNumbers;
+ return $this;
+ }
+
+ /**
+ * Get session object
+ *
+ * @return Zend_Session_Namespace
+ */
+ public function getSession()
+ {
+ if (!isset($this->_session) || (null === $this->_session)) {
+ $id = $this->getId();
+ if (!class_exists($this->_sessionClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($this->_sessionClass);
+ }
+ $this->_session = new $this->_sessionClass('Zend_Form_Captcha_' . $id);
+ $this->_session->setExpirationHops(1, null, true);
+ $this->_session->setExpirationSeconds($this->getTimeout());
+ }
+ return $this->_session;
+ }
+
+ /**
+ * Set session namespace object
+ *
+ * @param Zend_Session_Namespace $session
+ * @return Zend_Captcha_Word
+ */
+ public function setSession(Zend_Session_Namespace $session)
+ {
+ $this->_session = $session;
+ if($session) {
+ $this->_keepSession = true;
+ }
+ return $this;
+ }
+
+ /**
+ * Get captcha word
+ *
+ * @return string
+ */
+ public function getWord()
+ {
+ if (empty($this->_word)) {
+ $session = $this->getSession();
+ $this->_word = $session->word;
+ }
+ return $this->_word;
+ }
+
+ /**
+ * Set captcha word
+ *
+ * @param string $word
+ * @return Zend_Captcha_Word
+ */
+ protected function _setWord($word)
+ {
+ $session = $this->getSession();
+ $session->word = $word;
+ $this->_word = $word;
+ return $this;
+ }
+
+ /**
+ * Generate new random word
+ *
+ * @return string
+ */
+ protected function _generateWord()
+ {
+ $word = '';
+ $wordLen = $this->getWordLen();
+ $vowels = $this->_useNumbers ? self::$VN : self::$V;
+ $consonants = $this->_useNumbers ? self::$CN : self::$C;
+
+ for ($i=0; $i < $wordLen; $i = $i + 2) {
+ // generate word with mix of vowels and consonants
+ $consonant = $consonants[array_rand($consonants)];
+ $vowel = $vowels[array_rand($vowels)];
+ $word .= $consonant . $vowel;
+ }
+
+ if (strlen($word) > $wordLen) {
+ $word = substr($word, 0, $wordLen);
+ }
+
+ return $word;
+ }
+
+ /**
+ * Generate new session ID and new word
+ *
+ * @return string session ID
+ */
+ public function generate()
+ {
+ if(!$this->_keepSession) {
+ $this->_session = null;
+ }
+ $id = $this->_generateRandomId();
+ $this->_setId($id);
+ $word = $this->_generateWord();
+ $this->_setWord($word);
+ return $id;
+ }
+
+ protected function _generateRandomId()
+ {
+ return md5(mt_rand(0, 1000) . microtime(true));
+ }
+
+ /**
+ * Validate the word
+ *
+ * @see Zend_Validate_Interface::isValid()
+ * @param mixed $value
+ * @return boolean
+ */
+ public function isValid($value, $context = null)
+ {
+ if (!is_array($value) && !is_array($context)) {
+ $this->_error(self::MISSING_VALUE);
+ return false;
+ }
+ if (!is_array($value) && is_array($context)) {
+ $value = $context;
+ }
+
+ $name = $this->getName();
+
+ if (isset($value[$name])) {
+ $value = $value[$name];
+ }
+
+ if (!isset($value['input'])) {
+ $this->_error(self::MISSING_VALUE);
+ return false;
+ }
+ $input = strtolower($value['input']);
+ $this->_setValue($input);
+
+ if (!isset($value['id'])) {
+ $this->_error(self::MISSING_ID);
+ return false;
+ }
+
+ $this->_id = $value['id'];
+ if ($input !== $this->getWord()) {
+ $this->_error(self::BAD_CAPTCHA);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get captcha decorator
+ *
+ * @return string
+ */
+ public function getDecorator()
+ {
+ return "Captcha_Word";
+ }
+}
diff --git a/library/Zend/Cloud/AbstractFactory.php b/library/Zend/Cloud/AbstractFactory.php
new file mode 100644
index 0000000..f8ea6a6
--- /dev/null
+++ b/library/Zend/Cloud/AbstractFactory.php
@@ -0,0 +1,67 @@
+toArray();
+ }
+
+ if (!isset($options[$adapterOption])) {
+ return null;
+ }
+
+ $classname = $options[$adapterOption];
+ unset($options[$adapterOption]);
+ if (!class_exists($classname)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($classname);
+ }
+
+ return new $classname($options);
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/Adapter.php b/library/Zend/Cloud/DocumentService/Adapter.php
new file mode 100644
index 0000000..b40db7d
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/Adapter.php
@@ -0,0 +1,155 @@
+_documentClass = (string) $class;
+ return $this;
+ }
+
+ /**
+ * Get the class for document objects
+ *
+ * @return string
+ */
+ public function getDocumentClass()
+ {
+ return $this->_documentClass;
+ }
+
+ /**
+ * Set the class for document set objects
+ *
+ * @param string $class
+ * @return Zend_Cloud_DocumentService_Adapter_AbstractAdapter
+ */
+ public function setDocumentSetClass($class)
+ {
+ $this->_documentSetClass = (string) $class;
+ return $this;
+ }
+
+ /**
+ * Get the class for document set objects
+ *
+ * @return string
+ */
+ public function getDocumentSetClass()
+ {
+ return $this->_documentSetClass;
+ }
+
+ /**
+ * Set the query class for query objects
+ *
+ * @param string $class
+ * @return Zend_Cloud_DocumentService_Adapter_AbstractAdapter
+ */
+ public function setQueryClass($class)
+ {
+ $this->_queryClass = (string) $class;
+ return $this;
+ }
+
+ /**
+ * Get the class for query objects
+ *
+ * @return string
+ */
+ public function getQueryClass()
+ {
+ return $this->_queryClass;
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/Adapter/SimpleDb.php b/library/Zend/Cloud/DocumentService/Adapter/SimpleDb.php
new file mode 100644
index 0000000..6d5c34b
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/Adapter/SimpleDb.php
@@ -0,0 +1,468 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid options provided to constructor');
+ }
+
+ $this->_simpleDb = new Zend_Service_Amazon_SimpleDb(
+ $options[self::AWS_ACCESS_KEY], $options[self::AWS_SECRET_KEY]
+ );
+
+ if (isset($options[self::HTTP_ADAPTER])) {
+ $this->_simpleDb->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
+ }
+
+ if (isset($options[self::DOCUMENT_CLASS])) {
+ $this->setDocumentClass($options[self::DOCUMENT_CLASS]);
+ }
+
+ if (isset($options[self::DOCUMENTSET_CLASS])) {
+ $this->setDocumentSetClass($options[self::DOCUMENTSET_CLASS]);
+ }
+
+ if (isset($options[self::QUERY_CLASS])) {
+ $this->setQueryClass($options[self::QUERY_CLASS]);
+ }
+ }
+
+ /**
+ * Create collection.
+ *
+ * @param string $name
+ * @param array $options
+ * @return void
+ */
+ public function createCollection($name, $options = null)
+ {
+ try {
+ $this->_simpleDb->createDomain($name);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on domain creation: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Delete collection.
+ *
+ * @param string $name
+ * @param array $options
+ * @return void
+ */
+ public function deleteCollection($name, $options = null)
+ {
+ try {
+ $this->_simpleDb->deleteDomain($name);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on collection deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * List collections.
+ *
+ * @param array $options
+ * @return array
+ */
+ public function listCollections($options = null)
+ {
+ try {
+ // TODO package this in Pages
+ $domains = $this->_simpleDb->listDomains()->getData();
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on collection deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ return $domains;
+ }
+
+ /**
+ * List documents
+ *
+ * Returns a key/value array of document names to document objects.
+ *
+ * @param string $collectionName Name of collection for which to list documents
+ * @param array|null $options
+ * @return Zend_Cloud_DocumentService_DocumentSet
+ */
+ public function listDocuments($collectionName, array $options = null)
+ {
+ $query = $this->select('*')->from($collectionName);
+ $items = $this->query($collectionName, $query, $options);
+ return $items;
+ }
+
+ /**
+ * Insert document
+ *
+ * @param string $collectionName Collection into which to insert document
+ * @param array|Zend_Cloud_DocumentService_Document $document
+ * @param array $options
+ * @return void
+ */
+ public function insertDocument($collectionName, $document, $options = null)
+ {
+ if (is_array($document)) {
+ $document = $this->_getDocumentFromArray($document);
+ }
+
+ if (!$document instanceof Zend_Cloud_DocumentService_Document) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
+ }
+
+ try {
+ $this->_simpleDb->putAttributes(
+ $collectionName,
+ $document->getID(),
+ $this->_makeAttributes($document->getID(), $document->getFields())
+ );
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document insertion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Replace an existing document with a new version
+ *
+ * @param string $collectionName
+ * @param array|Zend_Cloud_DocumentService_Document $document
+ * @param array $options
+ * @return void
+ */
+ public function replaceDocument($collectionName, $document, $options = null)
+ {
+ if (is_array($document)) {
+ $document = $this->_getDocumentFromArray($document);
+ }
+
+ if (!$document instanceof Zend_Cloud_DocumentService_Document) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
+ }
+
+ // Delete document first, then insert. PutAttributes always keeps any
+ // fields not referenced in the payload, but present in the document
+ $documentId = $document->getId();
+ $fields = $document->getFields();
+ $docClass = get_class($document);
+ $this->deleteDocument($collectionName, $document, $options);
+
+ $document = new $docClass($fields, $documentId);
+ $this->insertDocument($collectionName, $document);
+ }
+
+ /**
+ * Update document. The new document replaces the existing document.
+ *
+ * Option 'merge' specifies to add all attributes (if true) or
+ * specific attributes ("attr" => true) instead of replacing them.
+ * By default, attributes are replaced.
+ *
+ * @param string $collectionName
+ * @param mixed|Zend_Cloud_DocumentService_Document $documentId Document ID, adapter-dependent
+ * @param array|Zend_Cloud_DocumentService_Document $fieldset Set of fields to update
+ * @param array $options
+ * @return boolean
+ */
+ public function updateDocument($collectionName, $documentId, $fieldset = null, $options = null)
+ {
+ if (null === $fieldset && $documentId instanceof Zend_Cloud_DocumentService_Document) {
+ $fieldset = $documentId->getFields();
+ if (empty($documentId)) {
+ $documentId = $documentId->getId();
+ }
+ } elseif ($fieldset instanceof Zend_Cloud_DocumentService_Document) {
+ if (empty($documentId)) {
+ $documentId = $fieldset->getId();
+ }
+ $fieldset = $fieldset->getFields();
+ }
+
+ $replace = array();
+ if (empty($options[self::MERGE_OPTION])) {
+ // no merge option - we replace all
+ foreach ($fieldset as $key => $value) {
+ $replace[$key] = true;
+ }
+ } elseif (is_array($options[self::MERGE_OPTION])) {
+ foreach ($fieldset as $key => $value) {
+ if (empty($options[self::MERGE_OPTION][$key])) {
+ // if there's merge key, we add it, otherwise we replace it
+ $replace[$key] = true;
+ }
+ }
+ } // otherwise $replace is empty - all is merged
+
+ try {
+ $this->_simpleDb->putAttributes(
+ $collectionName,
+ $documentId,
+ $this->_makeAttributes($documentId, $fieldset),
+ $replace
+ );
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document update: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ return true;
+ }
+
+ /**
+ * Delete document.
+ *
+ * @param string $collectionName Collection from which to delete document
+ * @param mixed $document Document ID or Document object.
+ * @param array $options
+ * @return boolean
+ */
+ public function deleteDocument($collectionName, $document, $options = null)
+ {
+ if ($document instanceof Zend_Cloud_DocumentService_Document) {
+ $document = $document->getId();
+ }
+ try {
+ $this->_simpleDb->deleteAttributes($collectionName, $document);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ return true;
+ }
+
+ /**
+ * Fetch single document by ID
+ *
+ * @param string $collectionName Collection name
+ * @param mixed $documentId Document ID, adapter-dependent
+ * @param array $options
+ * @return Zend_Cloud_DocumentService_Document
+ */
+ public function fetchDocument($collectionName, $documentId, $options = null)
+ {
+ try {
+ $attributes = $this->_simpleDb->getAttributes($collectionName, $documentId);
+ if ($attributes == false || count($attributes) == 0) {
+ return false;
+ }
+ return $this->_resolveAttributes($attributes, true);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on fetching document: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Query for documents stored in the document service. If a string is passed in
+ * $query, the query string will be passed directly to the service.
+ *
+ * @param string $collectionName Collection name
+ * @param string $query
+ * @param array $options
+ * @return array Zend_Cloud_DocumentService_DocumentSet
+ */
+ public function query($collectionName, $query, $options = null)
+ {
+ $returnDocs = isset($options[self::RETURN_DOCUMENTS])
+ ? (bool) $options[self::RETURN_DOCUMENTS]
+ : true;
+
+ try {
+ if ($query instanceof Zend_Cloud_DocumentService_Adapter_SimpleDb_Query) {
+ $query = $query->assemble($collectionName);
+ }
+ $result = $this->_simpleDb->select($query);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document query: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ return $this->_getDocumentSetFromResultSet($result, $returnDocs);
+ }
+
+ /**
+ * Create query statement
+ *
+ * @param string $fields
+ * @return Zend_Cloud_DocumentService_Adapter_SimpleDb_Query
+ */
+ public function select($fields = null)
+ {
+ $queryClass = $this->getQueryClass();
+ if (!class_exists($queryClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($queryClass);
+ }
+
+ $query = new $queryClass($this);
+ $defaultClass = self::DEFAULT_QUERY_CLASS;
+ if (!$query instanceof $defaultClass) {
+ throw new Zend_Cloud_DocumentService_Exception('Query class must extend ' . self::DEFAULT_QUERY_CLASS);
+ }
+
+ $query->select($fields);
+ return $query;
+ }
+
+ /**
+ * Get the concrete service client
+ *
+ * @return Zend_Service_Amazon_SimpleDb
+ */
+ public function getClient()
+ {
+ return $this->_simpleDb;
+ }
+
+ /**
+ * Convert array of key-value pairs to array of Amazon attributes
+ *
+ * @param string $name
+ * @param array $attributes
+ * @return array
+ */
+ protected function _makeAttributes($name, $attributes)
+ {
+ $result = array();
+ foreach ($attributes as $key => $attr) {
+ $result[] = new Zend_Service_Amazon_SimpleDb_Attribute($name, $key, $attr);
+ }
+ return $result;
+ }
+
+ /**
+ * Convert array of Amazon attributes to array of key-value pairs
+ *
+ * @param array $attributes
+ * @return array
+ */
+ protected function _resolveAttributes($attributes, $returnDocument = false)
+ {
+ $result = array();
+ foreach ($attributes as $attr) {
+ $value = $attr->getValues();
+ if (count($value) == 0) {
+ $value = null;
+ } elseif (count($value) == 1) {
+ $value = $value[0];
+ }
+ $result[$attr->getName()] = $value;
+ }
+
+ // Return as document object?
+ if ($returnDocument) {
+ $documentClass = $this->getDocumentClass();
+ return new $documentClass($result, $attr->getItemName());
+ }
+
+ return $result;
+ }
+
+ /**
+ * Create suitable document from array of fields
+ *
+ * @param array $document
+ * @return Zend_Cloud_DocumentService_Document
+ */
+ protected function _getDocumentFromArray($document)
+ {
+ if (!isset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD])) {
+ if (isset($document[self::ITEM_NAME])) {
+ $key = $document[self::ITEM_NAME];
+ unset($document[self::ITEM_NAME]);
+ } else {
+ throw new Zend_Cloud_DocumentService_Exception('Fields array should contain the key field '.Zend_Cloud_DocumentService_Document::KEY_FIELD);
+ }
+ } else {
+ $key = $document[Zend_Cloud_DocumentService_Document::KEY_FIELD];
+ unset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD]);
+ }
+
+ $documentClass = $this->getDocumentClass();
+ return new $documentClass($document, $key);
+ }
+
+ /**
+ * Create a DocumentSet from a SimpleDb resultset
+ *
+ * @param Zend_Service_Amazon_SimpleDb_Page $resultSet
+ * @param bool $returnDocs
+ * @return Zend_Cloud_DocumentService_DocumentSet
+ */
+ protected function _getDocumentSetFromResultSet(Zend_Service_Amazon_SimpleDb_Page $resultSet, $returnDocs = true)
+ {
+ $docs = array();
+ foreach ($resultSet->getData() as $item) {
+ $docs[] = $this->_resolveAttributes($item, $returnDocs);
+ }
+
+ $setClass = $this->getDocumentSetClass();
+ return new $setClass($docs);
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/Adapter/SimpleDb/Query.php b/library/Zend/Cloud/DocumentService/Adapter/SimpleDb/Query.php
new file mode 100644
index 0000000..f1683a9
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/Adapter/SimpleDb/Query.php
@@ -0,0 +1,175 @@
+_adapter = $adapter;
+ if (null !== $collectionName) {
+ $this->from($collectionName);
+ }
+ }
+
+ /**
+ * Get adapter
+ *
+ * @return Zend_Cloud_DocumentService_Adapter_SimpleDb
+ */
+ public function getAdapter()
+ {
+ return $this->_adapter;
+ }
+
+ /**
+ * Assemble the query into a format the adapter can utilize
+ *
+ * @var string $collectionName Name of collection from which to select
+ * @return string
+ */
+ public function assemble($collectionName = null)
+ {
+ $adapter = $this->getAdapter()->getClient();
+ $select = null;
+ $from = null;
+ $where = null;
+ $order = null;
+ $limit = null;
+ foreach ($this->getClauses() as $clause) {
+ list($name, $args) = $clause;
+
+ switch ($name) {
+ case self::QUERY_SELECT:
+ $select = $args[0];
+ break;
+ case self::QUERY_FROM:
+ if (null === $from) {
+ // Only allow setting FROM clause once
+ $from = $adapter->quoteName($args);
+ }
+ break;
+ case self::QUERY_WHERE:
+ $statement = $this->_parseWhere($args[0], $args[1]);
+ if (null === $where) {
+ $where = $statement;
+ } else {
+ $operator = empty($args[2]) ? 'AND' : $args[2];
+ $where .= ' ' . $operator . ' ' . $statement;
+ }
+ break;
+ case self::QUERY_WHEREID:
+ $statement = $this->_parseWhere('ItemName() = ?', array($args));
+ if (null === $where) {
+ $where = $statement;
+ } else {
+ $operator = empty($args[2]) ? 'AND' : $args[2];
+ $where .= ' ' . $operator . ' ' . $statement;
+ }
+ break;
+ case self::QUERY_ORDER:
+ $order = $adapter->quoteName($args[0]);
+ if (isset($args[1])) {
+ $order .= ' ' . $args[1];
+ }
+ break;
+ case self::QUERY_LIMIT:
+ $limit = $args;
+ break;
+ default:
+ // Ignore unknown clauses
+ break;
+ }
+ }
+
+ if (empty($select)) {
+ $select = "*";
+ }
+ if (empty($from)) {
+ if (null === $collectionName) {
+ require_once 'Zend/Cloud/DocumentService/Exception.php';
+ throw new Zend_Cloud_DocumentService_Exception("Query requires a FROM clause");
+ }
+ $from = $adapter->quoteName($collectionName);
+ }
+ $query = "select $select from $from";
+ if (!empty($where)) {
+ $query .= " where $where";
+ }
+ if (!empty($order)) {
+ $query .= " order by $order";
+ }
+ if (!empty($limit)) {
+ $query .= " limit $limit";
+ }
+ return $query;
+ }
+
+ /**
+ * Parse a where statement into service-specific language
+ *
+ * @todo Ensure this fulfills the entire SimpleDB query specification for WHERE
+ * @param string $where
+ * @param array $args
+ * @return string
+ */
+ protected function _parseWhere($where, $args)
+ {
+ if (!is_array($args)) {
+ $args = (array) $args;
+ }
+ $adapter = $this->getAdapter()->getClient();
+ $i = 0;
+ while (false !== ($pos = strpos($where, '?'))) {
+ $where = substr_replace($where, $adapter->quote($args[$i]), $pos);
+ ++$i;
+ }
+ if (('(' != $where[0]) || (')' != $where[strlen($where) - 1])) {
+ $where = '(' . $where . ')';
+ }
+ return $where;
+ }
+ }
diff --git a/library/Zend/Cloud/DocumentService/Adapter/WindowsAzure.php b/library/Zend/Cloud/DocumentService/Adapter/WindowsAzure.php
new file mode 100644
index 0000000..ee27809
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/Adapter/WindowsAzure.php
@@ -0,0 +1,628 @@
+toArray();
+ }
+
+ if (empty($options)) {
+ $options = array();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid options provided');
+ }
+
+ if (isset($options[self::DOCUMENT_CLASS])) {
+ $this->setDocumentClass($options[self::DOCUMENT_CLASS]);
+ }
+
+ if (isset($options[self::DOCUMENTSET_CLASS])) {
+ $this->setDocumentSetClass($options[self::DOCUMENTSET_CLASS]);
+ }
+
+ if (isset($options[self::QUERY_CLASS])) {
+ $this->setQueryClass($options[self::QUERY_CLASS]);
+ }
+
+ // Build Zend_Service_WindowsAzure_Storage_Blob instance
+ if (!isset($options[self::HOST])) {
+ $host = self::DEFAULT_HOST;
+ } else {
+ $host = $options[self::HOST];
+ }
+
+ if (! isset($options[self::ACCOUNT_NAME])) {
+ throw new Zend_Cloud_DocumentService_Exception('No Windows Azure account name provided.');
+ }
+
+ if (! isset($options[self::ACCOUNT_KEY])) {
+ throw new Zend_Cloud_DocumentService_Exception('No Windows Azure account key provided.');
+ }
+
+ // TODO: support $usePathStyleUri and $retryPolicy
+ try {
+ $this->_storageClient = new Zend_Service_WindowsAzure_Storage_Table(
+ $host, $options[self::ACCOUNT_NAME], $options[self::ACCOUNT_KEY]);
+ // Parse other options
+ if (! empty($options[self::PROXY_HOST])) {
+ $proxyHost = $options[self::PROXY_HOST];
+ $proxyPort = isset($options[self::PROXY_PORT]) ? $options[self::PROXY_PORT] : 8080;
+ $proxyCredentials = isset($options[self::PROXY_CREDENTIALS]) ? $options[self::PROXY_CREDENTIALS] : '';
+ $this->_storageClient->setProxy(true, $proxyHost, $proxyPort, $proxyCredentials);
+ }
+ if (isset($options[self::HTTP_ADAPTER])) {
+ $this->_storageClient->setHttpClientChannel($options[self::HTTP_ADAPTER]);
+ }
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document service creation: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Set the default partition key
+ *
+ * @param string $key
+ * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure
+ */
+ public function setDefaultPartitionKey($key)
+ {
+ $this->_validateKey($key);
+ $this->_defaultPartitionKey = $key;
+ return $this;
+ }
+
+ /**
+ * Retrieve default partition key
+ *
+ * @return null|string
+ */
+ public function getDefaultPartitionKey()
+ {
+ return $this->_defaultPartitionKey;
+ }
+
+ /**
+ * Create collection.
+ *
+ * @param string $name
+ * @param array $options
+ * @return boolean
+ */
+ public function createCollection($name, $options = null)
+ {
+ if (!preg_match('/^[A-Za-z][A-Za-z0-9]{2,}$/', $name)) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid collection name; Windows Azure collection names must consist of alphanumeric characters only, and be at least 3 characters long');
+ }
+ try {
+ $this->_storageClient->createTable($name);
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "table specified already exists") === false) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on collection creation: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Delete collection.
+ *
+ * @param string $name
+ * @param array $options
+ * @return boolean
+ */
+ public function deleteCollection($name, $options = null)
+ {
+ try {
+ $this->_storageClient->deleteTable($name);
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "does not exist") === false) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on collection deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * List collections.
+ *
+ * @param array $options
+ * @return array
+ */
+ public function listCollections($options = null)
+ {
+ try {
+ $tables = $this->_storageClient->listTables();
+ $restables = array();
+ foreach ($tables as $table) {
+ $restables[] = $table->name;
+ }
+ return $restables;
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on collection list: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ return $tables;
+ }
+
+ /**
+ * Create suitable document from array of fields
+ *
+ * @param array $document
+ * @param null|string $collectionName Collection to which this document belongs
+ * @return Zend_Cloud_DocumentService_Document
+ */
+ protected function _getDocumentFromArray($document, $collectionName = null)
+ {
+ $key = null;
+ if (!isset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD])) {
+ if (isset($document[self::ROW_KEY])) {
+ $rowKey = $document[self::ROW_KEY];
+ unset($document[self::ROW_KEY]);
+ if (isset($document[self::PARTITION_KEY])) {
+ $key = array($document[self::PARTITION_KEY], $rowKey);
+ unset($document[self::PARTITION_KEY]);
+ } elseif (null !== ($partitionKey = $this->getDefaultPartitionKey())) {
+ $key = array($partitionKey, $rowKey);
+ } elseif (null !== $collectionName) {
+ $key = array($collectionName, $rowKey);
+ }
+ }
+ } else {
+ $key = $document[Zend_Cloud_DocumentService_Document::KEY_FIELD];
+ unset($document[Zend_Cloud_DocumentService_Document::KEY_FIELD]);
+ }
+
+ $documentClass = $this->getDocumentClass();
+ return new $documentClass($document, $key);
+ }
+
+ /**
+ * List all documents in a collection
+ *
+ * @param string $collectionName
+ * @param null|array $options
+ * @return Zend_Cloud_DocumentService_DocumentSet
+ */
+ public function listDocuments($collectionName, array $options = null)
+ {
+ $select = $this->select()->from($collectionName);
+ return $this->query($collectionName, $select);
+ }
+
+ /**
+ * Insert document
+ *
+ * @param array|Zend_Cloud_DocumentService_Document $document
+ * @param array $options
+ * @return boolean
+ */
+ public function insertDocument($collectionName, $document, $options = null)
+ {
+ if (is_array($document)) {
+ $document = $this->_getDocumentFromArray($document, $collectionName);
+ }
+
+ if (!$document instanceof Zend_Cloud_DocumentService_Document) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
+ }
+
+ $key = $this->_validateDocumentId($document->getId(), $collectionName);
+ $document->setId($key);
+
+ $this->_validateCompositeKey($key);
+ $this->_validateFields($document);
+ try {
+
+ $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($key[0], $key[1]);
+ $entity->setAzureValues($document->getFields(), true);
+ $this->_storageClient->insertEntity($collectionName, $entity);
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document insertion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Replace document.
+ *
+ * The new document replaces the existing document.
+ *
+ * @param Zend_Cloud_DocumentService_Document $document
+ * @param array $options
+ * @return boolean
+ */
+ public function replaceDocument($collectionName, $document, $options = null)
+ {
+ if (is_array($document)) {
+ $document = $this->_getDocumentFromArray($document, $collectionName);
+ }
+
+ if (!$document instanceof Zend_Cloud_DocumentService_Document) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid document supplied');
+ }
+
+ $key = $this->_validateDocumentId($document->getId(), $collectionName);
+ $this->_validateFields($document);
+ try {
+ $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($key[0], $key[1]);
+ $entity->setAzureValues($document->getFields(), true);
+ if (isset($options[self::VERIFY_ETAG])) {
+ $entity->setEtag($options[self::VERIFY_ETAG]);
+ }
+
+ $this->_storageClient->updateEntity($collectionName, $entity, isset($options[self::VERIFY_ETAG]));
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document replace: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Update document.
+ *
+ * The new document is merged the existing document.
+ *
+ * @param string $collectionName
+ * @param mixed|Zend_Cloud_DocumentService_Document $documentId Document identifier or document contaiing updates
+ * @param null|array|Zend_Cloud_DocumentService_Document Fields to update (or new fields))
+ * @param array $options
+ * @return boolean
+ */
+ public function updateDocument($collectionName, $documentId, $fieldset = null, $options = null)
+ {
+ if (null === $fieldset && $documentId instanceof Zend_Cloud_DocumentService_Document) {
+ $fieldset = $documentId->getFields();
+ $documentId = $documentId->getId();
+ } elseif ($fieldset instanceof Zend_Cloud_DocumentService_Document) {
+ if ($documentId == null) {
+ $documentId = $fieldset->getId();
+ }
+ $fieldset = $fieldset->getFields();
+ }
+
+ $this->_validateCompositeKey($documentId, $collectionName);
+ $this->_validateFields($fieldset);
+ try {
+ $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($documentId[0], $documentId[1]);
+
+ // Ensure timestamp is set correctly
+ if (isset($fieldset[self::TIMESTAMP_KEY])) {
+ $entity->setTimestamp($fieldset[self::TIMESTAMP_KEY]);
+ unset($fieldset[self::TIMESTAMP_KEY]);
+ }
+
+ $entity->setAzureValues($fieldset, true);
+ if (isset($options[self::VERIFY_ETAG])) {
+ $entity->setEtag($options[self::VERIFY_ETAG]);
+ }
+
+ $this->_storageClient->mergeEntity($collectionName, $entity, isset($options[self::VERIFY_ETAG]));
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document update: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Delete document.
+ *
+ * @param mixed $document Document ID or Document object.
+ * @param array $options
+ * @return void
+ */
+ public function deleteDocument($collectionName, $documentId, $options = null)
+ {
+ if ($documentId instanceof Zend_Cloud_DocumentService_Document) {
+ $documentId = $documentId->getId();
+ }
+
+ $documentId = $this->_validateDocumentId($documentId, $collectionName);
+
+ try {
+ $entity = new Zend_Service_WindowsAzure_Storage_DynamicTableEntity($documentId[0], $documentId[1]);
+ if (isset($options[self::VERIFY_ETAG])) {
+ $entity->setEtag($options[self::VERIFY_ETAG]);
+ }
+ $this->_storageClient->deleteEntity($collectionName, $entity, isset($options[self::VERIFY_ETAG]));
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "does not exist") === false) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ }
+
+ /**
+ * Fetch single document by ID
+ *
+ * @param string $collectionName Collection name
+ * @param mixed $documentId Document ID, adapter-dependent
+ * @param array $options
+ * @return Zend_Cloud_DocumentService_Document
+ */
+ public function fetchDocument($collectionName, $documentId, $options = null)
+ {
+ $documentId = $this->_validateDocumentId($documentId, $collectionName);
+ try {
+ $entity = $this->_storageClient->retrieveEntityById($collectionName, $documentId[0], $documentId[1]);
+ $documentClass = $this->getDocumentClass();
+ return new $documentClass($this->_resolveAttributes($entity), array($entity->getPartitionKey(), $entity->getRowKey()));
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "does not exist") !== false) {
+ return false;
+ }
+ throw new Zend_Cloud_DocumentService_Exception('Error on document fetch: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Query for documents stored in the document service. If a string is passed in
+ * $query, the query string will be passed directly to the service.
+ *
+ * @param string $collectionName Collection name
+ * @param string|Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query $query
+ * @param array $options
+ * @return array Zend_Cloud_DocumentService_DocumentSet
+ */
+ public function query($collectionName, $query, $options = null)
+ {
+ try {
+ if ($query instanceof Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query) {
+ $entities = $this->_storageClient->retrieveEntities($query->assemble());
+ } else {
+ $entities = $this->_storageClient->retrieveEntities($collectionName, $query);
+ }
+
+ $documentClass = $this->getDocumentClass();
+ $resultSet = array();
+ foreach ($entities as $entity) {
+ $resultSet[] = new $documentClass(
+ $this->_resolveAttributes($entity),
+ array($entity->getPartitionKey(), $entity->getRowKey())
+ );
+ }
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_DocumentService_Exception('Error on document query: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ $setClass = $this->getDocumentSetClass();
+ return new $setClass($resultSet);
+ }
+
+ /**
+ * Create query statement
+ *
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function select($fields = null)
+ {
+ $queryClass = $this->getQueryClass();
+ if (!class_exists($queryClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($queryClass);
+ }
+
+ $query = new $queryClass();
+ $defaultClass = self::DEFAULT_QUERY_CLASS;
+ if (!$query instanceof $defaultClass) {
+ throw new Zend_Cloud_DocumentService_Exception('Query class must extend ' . self::DEFAULT_QUERY_CLASS);
+ }
+
+ $query->select($fields);
+ return $query;
+ }
+
+ /**
+ * Get the concrete service client
+ *
+ * @return Zend_Service_WindowsAzure_Storage_Table
+ */
+ public function getClient()
+ {
+ return $this->_storageClient;
+ }
+
+ /**
+ * Resolve table values to attributes
+ *
+ * @param Zend_Service_WindowsAzure_Storage_TableEntity $entity
+ * @return array
+ */
+ protected function _resolveAttributes(Zend_Service_WindowsAzure_Storage_TableEntity $entity)
+ {
+ $result = array();
+ foreach ($entity->getAzureValues() as $attr) {
+ $result[$attr->Name] = $attr->Value;
+ }
+ return $result;
+ }
+
+
+ /**
+ * Validate a partition or row key
+ *
+ * @param string $key
+ * @return void
+ * @throws Zend_Cloud_DocumentService_Exception
+ */
+ protected function _validateKey($key)
+ {
+ if (preg_match('@[/#?' . preg_quote('\\') . ']@', $key)) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid partition or row key provided; must not contain /, \\, #, or ? characters');
+ }
+ }
+
+ /**
+ * Validate a composite key
+ *
+ * @param array $key
+ * @return throws Zend_Cloud_DocumentService_Exception
+ */
+ protected function _validateCompositeKey(array $key)
+ {
+ if (2 != count($key)) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid document key provided; must contain exactly two elements: a PartitionKey and a RowKey');
+ }
+ foreach ($key as $k) {
+ $this->_validateKey($k);
+ }
+ }
+
+ /**
+ * Validate a document identifier
+ *
+ * If the identifier is an array containing a valid partition and row key,
+ * returns it. If the identifier is a string:
+ * - if a default partition key is present, it creates an identifier using
+ * that and the provided document ID
+ * - if a collection name is provided, it will use that for the partition key
+ * - otherwise, it's invalid
+ *
+ * @param array|string $documentId
+ * @param null|string $collectionName
+ * @return array
+ * @throws Zend_Cloud_DocumentService_Exception
+ */
+ protected function _validateDocumentId($documentId, $collectionName = false)
+ {
+ if (is_array($documentId)) {
+ $this->_validateCompositeKey($documentId);
+ return $documentId;
+ }
+ if (!is_string($documentId)) {
+ throw new Zend_Cloud_DocumentService_Exception('Invalid document identifier; must be a string or an array');
+ }
+
+ $this->_validateKey($documentId);
+
+ if (null !== ($partitionKey = $this->getDefaultPartitionKey())) {
+ return array($partitionKey, $documentId);
+ }
+ if (null !== $collectionName) {
+ return array($collectionName, $documentId);
+ }
+ throw new Zend_Cloud_DocumentService_Exception('Cannot determine partition name; invalid document identifier');
+ }
+
+ /**
+ * Validate a document's fields for well-formedness
+ *
+ * Since Azure uses Atom, and fieldnames are included as part of XML
+ * element tag names, the field names must be valid XML names.
+ *
+ * @param Zend_Cloud_DocumentService_Document|array $document
+ * @return void
+ * @throws Zend_Cloud_DocumentService_Exception
+ */
+ public function _validateFields($document)
+ {
+ if ($document instanceof Zend_Cloud_DocumentService_Document) {
+ $document = $document->getFields();
+ } elseif (!is_array($document)) {
+ throw new Zend_Cloud_DocumentService_Exception('Cannot inspect fields; invalid type provided');
+ }
+
+ foreach (array_keys($document) as $key) {
+ $this->_validateFieldKey($key);
+ }
+ }
+
+ /**
+ * Validate an individual field name for well-formedness
+ *
+ * Since Azure uses Atom, and fieldnames are included as part of XML
+ * element tag names, the field names must be valid XML names.
+ *
+ * While we could potentially normalize names, this could also lead to
+ * conflict with other field names -- which we should avoid. As such,
+ * invalid field names will raise an exception.
+ *
+ * @param string $key
+ * @return void
+ * @throws Zend_Cloud_DocumentService_Exception
+ */
+ public function _validateFieldKey($key)
+ {
+ if (!preg_match('/^[_A-Za-z][-._A-Za-z0-9]*$/', $key)) {
+ throw new Zend_Cloud_DocumentService_Exception('Field keys must conform to XML names (^[_A-Za-z][-._A-Za-z0-9]*$); key "' . $key . '" does not match');
+ }
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/Adapter/WindowsAzure/Query.php b/library/Zend/Cloud/DocumentService/Adapter/WindowsAzure/Query.php
new file mode 100644
index 0000000..1d2c358
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/Adapter/WindowsAzure/Query.php
@@ -0,0 +1,171 @@
+_azureSelect = $select;
+ }
+
+ /**
+ * SELECT clause (fields to be selected)
+ *
+ * Does nothing for Azure.
+ *
+ * @param string $select
+ * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query
+ */
+ public function select($select)
+ {
+ return $this;
+ }
+
+ /**
+ * FROM clause (table name)
+ *
+ * @param string $from
+ * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query
+ */
+ public function from($from)
+ {
+ $this->_azureSelect->from($from);
+ return $this;
+ }
+
+ /**
+ * WHERE clause (conditions to be used)
+ *
+ * @param string $where
+ * @param mixed $value Value or array of values to be inserted instead of ?
+ * @param string $op Operation to use to join where clauses (AND/OR)
+ * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query
+ */
+ public function where($where, $value = null, $op = 'and')
+ {
+ if (!empty($value) && !is_array($value)) {
+ // fix buglet in Azure - numeric values are quoted unless passed as an array
+ $value = array($value);
+ }
+ $this->_azureSelect->where($where, $value, $op);
+ return $this;
+ }
+
+ /**
+ * WHERE clause for item ID
+ *
+ * This one should be used when fetching specific rows since some adapters
+ * have special syntax for primary keys
+ *
+ * @param array $value Row ID for the document (PartitionKey, RowKey)
+ * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query
+ */
+ public function whereId($value)
+ {
+ if (!is_array($value)) {
+ require_once 'Zend/Cloud/DocumentService/Exception.php';
+ throw new Zend_Cloud_DocumentService_Exception('Invalid document key');
+ }
+ $this->_azureSelect->wherePartitionKey($value[0])->whereRowKey($value[1]);
+ return $this;
+ }
+
+ /**
+ * LIMIT clause (how many rows to return)
+ *
+ * @param int $limit
+ * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query
+ */
+ public function limit($limit)
+ {
+ $this->_azureSelect->top($limit);
+ return $this;
+ }
+
+ /**
+ * ORDER BY clause (sorting)
+ *
+ * @todo Azure service doesn't seem to support this yet; emulate?
+ * @param string $sort Column to sort by
+ * @param string $direction Direction - asc/desc
+ * @return Zend_Cloud_DocumentService_Adapter_WindowsAzure_Query
+ * @throws Zend_Cloud_OperationNotAvailableException
+ */
+ public function order($sort, $direction = 'asc')
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('No support for sorting for Azure yet');
+ }
+
+ /**
+ * Get Azure select query
+ *
+ * @return Zend_Service_WindowsAzure_Storage_TableEntityQuery
+ */
+ public function getAzureSelect()
+ {
+ return $this->_azureSelect;
+ }
+
+ /**
+ * Assemble query
+ *
+ * Simply return the WindowsAzure table entity query object
+ *
+ * @return Zend_Service_WindowsAzure_Storage_TableEntityQuery
+ */
+ public function assemble()
+ {
+ return $this->getAzureSelect();
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/Document.php b/library/Zend/Cloud/DocumentService/Document.php
new file mode 100644
index 0000000..7749360
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/Document.php
@@ -0,0 +1,248 @@
+_fields = $fields;
+ $this->setId($id);
+ }
+
+ /**
+ * Set document identifier
+ *
+ * @param mixed $id
+ * @return Zend_Cloud_DocumentService_Document
+ */
+ public function setId($id)
+ {
+ $this->_id = $id;
+ return $this;
+ }
+
+ /**
+ * Get ID name.
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ return $this->_id;
+ }
+
+ /**
+ * Get fields as array.
+ *
+ * @return array
+ */
+ public function getFields()
+ {
+ return $this->_fields;
+ }
+
+ /**
+ * Get field by name.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function getField($name)
+ {
+ if (isset($this->_fields[$name])) {
+ return $this->_fields[$name];
+ }
+ return null;
+ }
+
+ /**
+ * Set field by name.
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return Zend_Cloud_DocumentService_Document
+ */
+ public function setField($name, $value)
+ {
+ $this->_fields[$name] = $value;
+ return $this;
+ }
+
+ /**
+ * Overloading: get value
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return $this->getField($name);
+ }
+
+ /**
+ * Overloading: set field
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return void
+ */
+ public function __set($name, $value)
+ {
+ $this->setField($name, $value);
+ }
+
+ /**
+ * ArrayAccess: does field exist?
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function offsetExists($name)
+ {
+ return isset($this->_fields[$name]);
+ }
+
+ /**
+ * ArrayAccess: get field by name
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function offsetGet($name)
+ {
+ return $this->getField($name);
+ }
+
+ /**
+ * ArrayAccess: set field to value
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return void
+ */
+ public function offsetSet($name, $value)
+ {
+ $this->setField($name, $value);
+ }
+
+ /**
+ * ArrayAccess: remove field from document
+ *
+ * @param string $name
+ * @return void
+ */
+ public function offsetUnset($name)
+ {
+ if ($this->offsetExists($name)) {
+ unset($this->_fields[$name]);
+ }
+ }
+
+ /**
+ * Overloading: retrieve and set fields by name
+ *
+ * @param string $name
+ * @param mixed $args
+ * @return mixed
+ */
+ public function __call($name, $args)
+ {
+ $prefix = substr($name, 0, 3);
+ if ($prefix == 'get') {
+ // Get value
+ $option = substr($name, 3);
+ return $this->getField($option);
+ } elseif ($prefix == 'set') {
+ // set value
+ $option = substr($name, 3);
+ return $this->setField($option, $args[0]);
+ }
+
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException("Unknown operation $name");
+ }
+
+ /**
+ * Countable: return count of fields in document
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->_fields);
+ }
+
+ /**
+ * IteratorAggregate: return iterator for iterating over fields
+ *
+ * @return Iterator
+ */
+ public function getIterator()
+ {
+ return new ArrayIterator($this->_fields);
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/DocumentSet.php b/library/Zend/Cloud/DocumentService/DocumentSet.php
new file mode 100644
index 0000000..3942c58
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/DocumentSet.php
@@ -0,0 +1,68 @@
+_documentCount = count($documents);
+ $this->_documents = new ArrayIterator($documents);
+ }
+
+ /**
+ * Countable: number of documents in set
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->_documentCount;
+ }
+
+ /**
+ * IteratorAggregate: retrieve iterator
+ *
+ * @return Traversable
+ */
+ public function getIterator()
+ {
+ return $this->_documents;
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/Exception.php b/library/Zend/Cloud/DocumentService/Exception.php
new file mode 100644
index 0000000..71d1e27
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/Exception.php
@@ -0,0 +1,38 @@
+foo('bar')
+ * but concrete adapters should be able to recognise it
+ *
+ * The call will be iterpreted as clause 'foo' with argument 'bar'
+ *
+ * @param string $name Clause/method name
+ * @param mixed $args
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function __call($name, $args)
+ {
+ $this->_clauses[] = array(strtolower($name), $args);
+ return $this;
+ }
+
+ /**
+ * SELECT clause (fields to be selected)
+ *
+ * @param null|string|array $select
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function select($select)
+ {
+ if (empty($select)) {
+ return $this;
+ }
+ if (!is_string($select) && !is_array($select)) {
+ require_once 'Zend/Cloud/DocumentService/Exception.php';
+ throw new Zend_Cloud_DocumentService_Exception("SELECT argument must be a string or an array of strings");
+ }
+ $this->_clauses[] = array(self::QUERY_SELECT, $select);
+ return $this;
+ }
+
+ /**
+ * FROM clause
+ *
+ * @param string $name Field names
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function from($name)
+ {
+ if(!is_string($name)) {
+ require_once 'Zend/Cloud/DocumentService/Exception.php';
+ throw new Zend_Cloud_DocumentService_Exception("FROM argument must be a string");
+ }
+ $this->_clauses[] = array(self::QUERY_FROM, $name);
+ return $this;
+ }
+
+ /**
+ * WHERE query
+ *
+ * @param string $cond Condition
+ * @param array $args Arguments to substitute instead of ?'s in condition
+ * @param string $op relation to other clauses - and/or
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function where($cond, $value = null, $op = 'and')
+ {
+ if (!is_string($cond)) {
+ require_once 'Zend/Cloud/DocumentService/Exception.php';
+ throw new Zend_Cloud_DocumentService_Exception("WHERE argument must be a string");
+ }
+ $this->_clauses[] = array(self::QUERY_WHERE, array($cond, $value, $op));
+ return $this;
+ }
+
+ /**
+ * Select record or fields by ID
+ *
+ * @param string|int $value Identifier to select by
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function whereId($value)
+ {
+ if (!is_scalar($value)) {
+ require_once 'Zend/Cloud/DocumentService/Exception.php';
+ throw new Zend_Cloud_DocumentService_Exception("WHEREID argument must be a scalar");
+ }
+ $this->_clauses[] = array(self::QUERY_WHEREID, $value);
+ return $this;
+ }
+
+ /**
+ * LIMIT clause (how many items to return)
+ *
+ * @param int $limit
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function limit($limit)
+ {
+ if ($limit != (int) $limit) {
+ require_once 'Zend/Cloud/DocumentService/Exception.php';
+ throw new Zend_Cloud_DocumentService_Exception("LIMIT argument must be an integer");
+ }
+ $this->_clauses[] = array(self::QUERY_LIMIT, $limit);
+ return $this;
+ }
+
+ /**
+ * ORDER clause; field or fields to sort by, and direction to sort
+ *
+ * @param string|int|array $sort
+ * @param string $direction
+ * @return Zend_Cloud_DocumentService_Query
+ */
+ public function order($sort, $direction = 'asc')
+ {
+ $this->_clauses[] = array(self::QUERY_ORDER, array($sort, $direction));
+ return $this;
+ }
+
+ /**
+ * "Assemble" the query
+ *
+ * Simply returns the clauses present.
+ *
+ * @return array
+ */
+ public function assemble()
+ {
+ return $this->getClauses();
+ }
+
+ /**
+ * Return query clauses as an array
+ *
+ * @return array Clauses in the query
+ */
+ public function getClauses()
+ {
+ return $this->_clauses;
+ }
+}
diff --git a/library/Zend/Cloud/DocumentService/QueryAdapter.php b/library/Zend/Cloud/DocumentService/QueryAdapter.php
new file mode 100644
index 0000000..cf4f1ab
--- /dev/null
+++ b/library/Zend/Cloud/DocumentService/QueryAdapter.php
@@ -0,0 +1,102 @@
+_clientException = $clientException;
+ parent::__construct($message, $code, $clientException);
+ }
+
+ public function getClientException() {
+ return $this->_getPrevious();
+ }
+}
+
diff --git a/library/Zend/Cloud/OperationNotAvailableException.php b/library/Zend/Cloud/OperationNotAvailableException.php
new file mode 100644
index 0000000..07f71ee
--- /dev/null
+++ b/library/Zend/Cloud/OperationNotAvailableException.php
@@ -0,0 +1,34 @@
+_messageClass = (string) $class;
+ return $this;
+ }
+
+ /**
+ * Get class to use for message objects
+ *
+ * @return string
+ */
+ public function getMessageClass()
+ {
+ return $this->_messageClass;
+ }
+
+ /**
+ * Set class to use for message collection objects
+ *
+ * @param string $class
+ * @return Zend_Cloud_QueueService_Adapter_AbstractAdapter
+ */
+ public function setMessageSetClass($class)
+ {
+ $this->_messageSetClass = (string) $class;
+ return $this;
+ }
+
+ /**
+ * Get class to use for message collection objects
+ *
+ * @return string
+ */
+ public function getMessageSetClass()
+ {
+ return $this->_messageSetClass;
+ }
+}
diff --git a/library/Zend/Cloud/QueueService/Adapter/Sqs.php b/library/Zend/Cloud/QueueService/Adapter/Sqs.php
new file mode 100644
index 0000000..253b2a1
--- /dev/null
+++ b/library/Zend/Cloud/QueueService/Adapter/Sqs.php
@@ -0,0 +1,278 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_QueueService_Exception('Invalid options provided');
+ }
+
+ if (isset($options[self::MESSAGE_CLASS])) {
+ $this->setMessageClass($options[self::MESSAGE_CLASS]);
+ }
+
+ if (isset($options[self::MESSAGESET_CLASS])) {
+ $this->setMessageSetClass($options[self::MESSAGESET_CLASS]);
+ }
+
+ try {
+ $this->_sqs = new Zend_Service_Amazon_Sqs(
+ $options[self::AWS_ACCESS_KEY], $options[self::AWS_SECRET_KEY]
+ );
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on create: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ if(isset($options[self::HTTP_ADAPTER])) {
+ $this->_sqs->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
+ }
+ }
+
+ /**
+ * Create a queue. Returns the ID of the created queue (typically the URL).
+ * It may take some time to create the queue. Check your vendor's
+ * documentation for details.
+ *
+ * @param string $name
+ * @param array $options
+ * @return string Queue ID (typically URL)
+ */
+ public function createQueue($name, $options = null)
+ {
+ try {
+ return $this->_sqs->create($name, $options[self::CREATE_TIMEOUT]);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on queue creation: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Delete a queue. All messages in the queue will also be deleted.
+ *
+ * @param string $queueId
+ * @param array $options
+ * @return boolean true if successful, false otherwise
+ */
+ public function deleteQueue($queueId, $options = null)
+{
+ try {
+ return $this->_sqs->delete($queueId);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw Zend_Cloud_QueueService_Exception('Error on queue deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * List all queues.
+ *
+ * @param array $options
+ * @return array Queue IDs
+ */
+ public function listQueues($options = null)
+ {
+ try {
+ return $this->_sqs->getQueues();
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on listing queues: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get a key/value array of metadata for the given queue.
+ *
+ * @param string $queueId
+ * @param array $options
+ * @return array
+ */
+ public function fetchQueueMetadata($queueId, $options = null)
+ {
+ try {
+ // TODO: ZF-9050 Fix the SQS client library in trunk to return all attribute values
+ $attributes = $this->_sqs->getAttribute($queueId, 'All');
+ if(is_array($attributes)) {
+ return $attributes;
+ } else {
+ return array('All' => $this->_sqs->getAttribute($queueId, 'All'));
+ }
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on fetching queue metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Store a key/value array of metadata for the specified queue.
+ * WARNING: This operation overwrites any metadata that is located at
+ * $destinationPath. Some adapters may not support this method.
+ *
+ * @param array $metadata
+ * @param string $queueId
+ * @param array $options
+ * @return void
+ */
+ public function storeQueueMetadata($queueId, $metadata, $options = null)
+ {
+ // TODO Add support for SetQueueAttributes to client library
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('Amazon SQS doesn\'t currently support storing metadata');
+ }
+
+ /**
+ * Send a message to the specified queue.
+ *
+ * @param string $message
+ * @param string $queueId
+ * @param array $options
+ * @return string Message ID
+ */
+ public function sendMessage($queueId, $message, $options = null)
+ {
+ try {
+ return $this->_sqs->send($queueId, $message);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on sending message: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Recieve at most $max messages from the specified queue and return the
+ * message IDs for messages recieved.
+ *
+ * @param string $queueId
+ * @param int $max
+ * @param array $options
+ * @return array
+ */
+ public function receiveMessages($queueId, $max = 1, $options = null)
+ {
+ try {
+ return $this->_makeMessages($this->_sqs->receive($queueId, $max, $options[self::VISIBILITY_TIMEOUT]));
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on recieving messages: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Create Zend_Cloud_QueueService_Message array for
+ * Sqs messages.
+ *
+ * @param array $messages
+ * @return Zend_Cloud_QueueService_Message[]
+ */
+ protected function _makeMessages($messages)
+ {
+ $messageClass = $this->getMessageClass();
+ $setClass = $this->getMessageSetClass();
+ $result = array();
+ foreach($messages as $message) {
+ $result[] = new $messageClass($message['body'], $message);
+ }
+ return new $setClass($result);
+ }
+
+ /**
+ * Delete the specified message from the specified queue.
+ *
+ * @param string $queueId
+ * @param Zend_Cloud_QueueService_Message $message
+ * @param array $options
+ * @return void
+ */
+ public function deleteMessage($queueId, $message, $options = null)
+ {
+ try {
+ if($message instanceof Zend_Cloud_QueueService_Message) {
+ $message = $message->getMessage();
+ }
+ $messageId = $message['handle'];
+ return $this->_sqs->deleteMessage($queueId, $messageId);
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on deleting a message: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Peek at the messages from the specified queue without removing them.
+ *
+ * @param string $queueId
+ * @param int $num How many messages
+ * @param array $options
+ * @return Zend_Cloud_QueueService_Message[]
+ */
+ public function peekMessages($queueId, $num = 1, $options = null)
+ {
+ try {
+ return $this->_makeMessages($this->_sqs->receive($queueId, $num, 0));
+ } catch(Zend_Service_Amazon_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on peeking messages: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get SQS implementation
+ * @return Zend_Service_Amazon_Sqs
+ */
+ public function getClient()
+ {
+ return $this->_sqs;
+ }
+}
diff --git a/library/Zend/Cloud/QueueService/Adapter/WindowsAzure.php b/library/Zend/Cloud/QueueService/Adapter/WindowsAzure.php
new file mode 100644
index 0000000..61a170e
--- /dev/null
+++ b/library/Zend/Cloud/QueueService/Adapter/WindowsAzure.php
@@ -0,0 +1,343 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_QueueService_Exception('Invalid options provided');
+ }
+
+ if (isset($options[self::MESSAGE_CLASS])) {
+ $this->setMessageClass($options[self::MESSAGE_CLASS]);
+ }
+
+ if (isset($options[self::MESSAGESET_CLASS])) {
+ $this->setMessageSetClass($options[self::MESSAGESET_CLASS]);
+ }
+
+ // Build Zend_Service_WindowsAzure_Storage_Blob instance
+ if (!isset($options[self::HOST])) {
+ $host = self::DEFAULT_HOST;
+ } else {
+ $host = $options[self::HOST];
+ }
+ if (! isset($options[self::ACCOUNT_NAME])) {
+ throw new Zend_Cloud_Storage_Exception('No Windows Azure account name provided.');
+ }
+ if (! isset($options[self::ACCOUNT_KEY])) {
+ throw new Zend_Cloud_Storage_Exception('No Windows Azure account key provided.');
+ }
+ try {
+ // TODO: support $usePathStyleUri and $retryPolicy
+ $this->_storageClient = new Zend_Service_WindowsAzure_Storage_Queue(
+ $host, $options[self::ACCOUNT_NAME], $options[self::ACCOUNT_KEY]);
+ // Parse other options
+ if (! empty($options[self::PROXY_HOST])) {
+ $proxyHost = $options[self::PROXY_HOST];
+ $proxyPort = isset($options[self::PROXY_PORT]) ? $options[self::PROXY_PORT] : 8080;
+ $proxyCredentials = isset($options[self::PROXY_CREDENTIALS]) ? $options[self::PROXY_CREDENTIALS] : '';
+ $this->_storageClient->setProxy(true, $proxyHost, $proxyPort, $proxyCredentials);
+ }
+ if (isset($options[self::HTTP_ADAPTER])) {
+ $this->_storageClient->setHttpClientChannel($httpAdapter);
+ }
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on create: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ }
+
+ /**
+ * Create a queue. Returns the ID of the created queue (typically the URL).
+ * It may take some time to create the queue. Check your vendor's
+ * documentation for details.
+ *
+ * @param string $name
+ * @param array $options
+ * @return string Queue ID (typically URL)
+ */
+ public function createQueue($name, $options = null)
+ {
+ try {
+ $queue = $this->_storageClient->createQueue($name, $options);
+ return $queue->Name;
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on queue creation: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Delete a queue. All messages in the queue will also be deleted.
+ *
+ * @param string $queueId
+ * @param array $options
+ * @return boolean true if successful, false otherwise
+ */
+ public function deleteQueue($queueId, $options = null)
+ {
+ try {
+ if ($queueId instanceof Zend_Service_WindowsAzure_Storage_QueueInstance) {
+ $queueId = $queueId->Name;
+ }
+ return $this->_storageClient->deleteQueue($queueId);
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on queue deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * List all queues.
+ *
+ * @param array $options
+ * @return array Queue IDs
+ */
+ public function listQueues($options = null)
+ {
+ $prefix = $maxResults = null;
+ if (is_array($options)) {
+ isset($options[self::LIST_PREFIX]) ? $prefix = $options[self::LIST_PREFIX] : null;
+ isset($options[self::LIST_MAX_RESULTS]) ? $maxResults = $options[self::LIST_MAX_RESULTS] : null;
+ }
+ try {
+ $queues = $this->_storageClient->listQueues($prefix, $maxResults);
+ $result = array();
+ foreach ($queues as $queue) {
+ $result[] = $queue->Name;
+ }
+ return $result;
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on listing queues: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get a key/value array of metadata for the given queue.
+ *
+ * @param string $queueId
+ * @param array $options
+ * @return array
+ */
+ public function fetchQueueMetadata($queueId, $options = null)
+ {
+ try {
+ if ($queueId instanceof Zend_Service_WindowsAzure_Storage_QueueInstance) {
+ $queueId = $queueId->Name;
+ }
+ return $this->_storageClient->getQueueMetadata($queueId);
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on fetching queue metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Store a key/value array of metadata for the specified queue.
+ * WARNING: This operation overwrites any metadata that is located at
+ * $destinationPath. Some adapters may not support this method.
+ *
+ * @param string $queueId
+ * @param array $metadata
+ * @param array $options
+ * @return void
+ */
+ public function storeQueueMetadata($queueId, $metadata, $options = null)
+ {
+ try {
+ if ($queueId instanceof Zend_Service_WindowsAzure_Storage_QueueInstance) {
+ $queueId = $queueId->Name;
+ }
+ return $this->_storageClient->setQueueMetadata($queueId, $metadata);
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on setting queue metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Send a message to the specified queue.
+ *
+ * @param string $queueId
+ * @param string $message
+ * @param array $options
+ * @return string Message ID
+ */
+ public function sendMessage($queueId, $message, $options = null)
+ {
+ try {
+ if ($queueId instanceof Zend_Service_WindowsAzure_Storage_QueueInstance) {
+ $queueId = $queueId->Name;
+ }
+ return $this->_storageClient->putMessage(
+ $queueId, $message, $options[self::MESSAGE_TTL]
+ );
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on sending message: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Recieve at most $max messages from the specified queue and return the
+ * message IDs for messages recieved.
+ *
+ * @param string $queueId
+ * @param int $max
+ * @param array $options
+ * @return Zend_Cloud_QueueService_Message[]
+ */
+ public function receiveMessages($queueId, $max = 1, $options = null)
+ {
+ try {
+ if ($queueId instanceof Zend_Service_WindowsAzure_Storage_QueueInstance) {
+ $queueId = $queueId->Name;
+ }
+ if (isset($options[self::VISIBILITY_TIMEOUT])) {
+ $visibility = $options[self::VISIBILITY_TIMEOUT];
+ } else {
+ $visibility = self::DEFAULT_TIMEOUT;
+ }
+ return $this->_makeMessages($this->_storageClient->getMessages($queueId, $max, $visibility, false));
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on recieving messages: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Create Zend_Cloud_QueueService_Message array for
+ * Azure messages.
+ *
+ * @param array $messages
+ * @return Zend_Cloud_QueueService_Message[]
+ */
+ protected function _makeMessages($messages)
+ {
+ $messageClass = $this->getMessageClass();
+ $setClass = $this->getMessageSetClass();
+ $result = array();
+ foreach ($messages as $message) {
+ $result[] = new $messageClass($message->MessageText, $message);
+ }
+ return new $setClass($result);
+ }
+
+ /**
+ * Delete the specified message from the specified queue.
+ *
+ * @param string $queueId
+ * @param Zend_Cloud_QueueService_Message $message Message ID or message
+ * @param array $options
+ * @return void
+ */
+ public function deleteMessage($queueId, $message, $options = null)
+ {
+ try {
+ if ($queueId instanceof Zend_Service_WindowsAzure_Storage_QueueInstance) {
+ $queueId = $queueId->Name;
+ }
+ if ($message instanceof Zend_Cloud_QueueService_Message) {
+ $message = $message->getMessage();
+ }
+ if ($message instanceof Zend_Service_WindowsAzure_Storage_QueueMessage) {
+ return $this->_storageClient->deleteMessage($queueId, $message);
+ } else {
+ throw new Zend_Cloud_QueueService_Exception('Cannot delete the message: message object required');
+ }
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on deleting a message: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Peek at the messages from the specified queue without removing them.
+ *
+ * @param string $queueId
+ * @param int $num How many messages
+ * @param array $options
+ * @return Zend_Cloud_QueueService_Message[]
+ */
+ public function peekMessages($queueId, $num = 1, $options = null)
+ {
+ try {
+ if ($queueId instanceof Zend_Service_WindowsAzure_Storage_QueueInstance) {
+ $queueId = $queueId->Name;
+ }
+ return $this->_makeMessages($this->_storageClient->peekMessages($queueId, $num));
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on peeking messages: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get Azure implementation
+ * @return Zend_Service_Azure_Storage_Queue
+ */
+ public function getClient()
+ {
+ return $this->_storageClient;
+ }
+}
diff --git a/library/Zend/Cloud/QueueService/Adapter/ZendQueue.php b/library/Zend/Cloud/QueueService/Adapter/ZendQueue.php
new file mode 100644
index 0000000..35acb4d
--- /dev/null
+++ b/library/Zend/Cloud/QueueService/Adapter/ZendQueue.php
@@ -0,0 +1,301 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_QueueService_Exception('Invalid options provided');
+ }
+
+ if (isset($options[self::MESSAGE_CLASS])) {
+ $this->setMessageClass($options[self::MESSAGE_CLASS]);
+ }
+
+ if (isset($options[self::MESSAGESET_CLASS])) {
+ $this->setMessageSetClass($options[self::MESSAGESET_CLASS]);
+ }
+
+ // Build Zend_Service_WindowsAzure_Storage_Blob instance
+ if (!isset($options[self::ADAPTER])) {
+ throw new Zend_Cloud_QueueService_Exception('No Zend_Queue adapter provided');
+ } else {
+ $adapter = $options[self::ADAPTER];
+ unset($options[self::ADAPTER]);
+ }
+ try {
+ $this->_queue = new Zend_Queue($adapter, $options);
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on create: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Create a queue. Returns the ID of the created queue (typically the URL).
+ * It may take some time to create the queue. Check your vendor's
+ * documentation for details.
+ *
+ * @param string $name
+ * @param array $options
+ * @return string Queue ID (typically URL)
+ */
+ public function createQueue($name, $options = null)
+ {
+ try {
+ $this->_queues[$name] = $this->_queue->createQueue($name, isset($options[Zend_Queue::TIMEOUT])?$options[Zend_Queue::TIMEOUT]:null);
+ return $name;
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on queue creation: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Delete a queue. All messages in the queue will also be deleted.
+ *
+ * @param string $queueId
+ * @param array $options
+ * @return boolean true if successful, false otherwise
+ */
+ public function deleteQueue($queueId, $options = null)
+ {
+ if (!isset($this->_queues[$queueId])) {
+ return false;
+ }
+ try {
+ if ($this->_queues[$queueId]->deleteQueue()) {
+ unset($this->_queues[$queueId]);
+ return true;
+ }
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on queue deletion: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * List all queues.
+ *
+ * @param array $options
+ * @return array Queue IDs
+ */
+ public function listQueues($options = null)
+ {
+ try {
+ return $this->_queue->getQueues();
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on listing queues: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get a key/value array of metadata for the given queue.
+ *
+ * @param string $queueId
+ * @param array $options
+ * @return array
+ */
+ public function fetchQueueMetadata($queueId, $options = null)
+ {
+ if (!isset($this->_queues[$queueId])) {
+ return false;
+ }
+ try {
+ return $this->_queues[$queueId]->getOptions();
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on fetching queue metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Store a key/value array of metadata for the specified queue.
+ * WARNING: This operation overwrites any metadata that is located at
+ * $destinationPath. Some adapters may not support this method.
+ *
+ * @param string $queueId
+ * @param array $metadata
+ * @param array $options
+ * @return void
+ */
+ public function storeQueueMetadata($queueId, $metadata, $options = null)
+ {
+ if (!isset($this->_queues[$queueId])) {
+ throw new Zend_Cloud_QueueService_Exception("No such queue: $queueId");
+ }
+ try {
+ return $this->_queues[$queueId]->setOptions($metadata);
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on setting queue metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Send a message to the specified queue.
+ *
+ * @param string $queueId
+ * @param string $message
+ * @param array $options
+ * @return string Message ID
+ */
+ public function sendMessage($queueId, $message, $options = null)
+ {
+ if (!isset($this->_queues[$queueId])) {
+ throw new Zend_Cloud_QueueService_Exception("No such queue: $queueId");
+ }
+ try {
+ return $this->_queues[$queueId]->send($message);
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on sending message: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Recieve at most $max messages from the specified queue and return the
+ * message IDs for messages recieved.
+ *
+ * @param string $queueId
+ * @param int $max
+ * @param array $options
+ * @return array
+ */
+ public function receiveMessages($queueId, $max = 1, $options = null)
+ {
+ if (!isset($this->_queues[$queueId])) {
+ throw new Zend_Cloud_QueueService_Exception("No such queue: $queueId");
+ }
+ try {
+ $res = $this->_queues[$queueId]->receive($max, isset($options[Zend_Queue::TIMEOUT])?$options[Zend_Queue::TIMEOUT]:null);
+ if ($res instanceof Iterator) {
+ return $this->_makeMessages($res);
+ } else {
+ return $this->_makeMessages(array($res));
+ }
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on recieving messages: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Create Zend_Cloud_QueueService_Message array for
+ * Azure messages.
+ *
+ * @param array $messages
+ * @return Zend_Cloud_QueueService_Message[]
+ */
+ protected function _makeMessages($messages)
+ {
+ $messageClass = $this->getMessageClass();
+ $setClass = $this->getMessageSetClass();
+ $result = array();
+ foreach ($messages as $message) {
+ $result[] = new $messageClass($message->body, $message);
+ }
+ return new $setClass($result);
+ }
+
+ /**
+ * Delete the specified message from the specified queue.
+ *
+ * @param string $queueId
+ * @param Zend_Cloud_QueueService_Message $message Message ID or message
+ * @param array $options
+ * @return void
+ */
+ public function deleteMessage($queueId, $message, $options = null)
+ {
+ if (!isset($this->_queues[$queueId])) {
+ throw new Zend_Cloud_QueueService_Exception("No such queue: $queueId");
+ }
+ try {
+ if ($message instanceof Zend_Cloud_QueueService_Message) {
+ $message = $message->getMessage();
+ }
+ if (!($message instanceof Zend_Queue_Message)) {
+ throw new Zend_Cloud_QueueService_Exception('Cannot delete the message: Zend_Queue_Message object required');
+ }
+
+ return $this->_queues[$queueId]->deleteMessage($message);
+ } catch (Zend_Queue_Exception $e) {
+ throw new Zend_Cloud_QueueService_Exception('Error on deleting a message: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Peek at the messages from the specified queue without removing them.
+ *
+ * @param string $queueId
+ * @param int $num How many messages
+ * @param array $options
+ * @return Zend_Cloud_QueueService_Message[]
+ */
+ public function peekMessages($queueId, $num = 1, $options = null)
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('ZendQueue doesn\'t currently support message peeking');
+ }
+
+ /**
+ * Get Azure implementation
+ * @return Zend_Queue
+ */
+ public function getClient()
+ {
+ return $this->_queue;
+ }
+}
diff --git a/library/Zend/Cloud/QueueService/Exception.php b/library/Zend/Cloud/QueueService/Exception.php
new file mode 100644
index 0000000..c782aab
--- /dev/null
+++ b/library/Zend/Cloud/QueueService/Exception.php
@@ -0,0 +1,37 @@
+_body = $body;
+ $this->_clientMessage = $message;
+ }
+
+ /**
+ * Get the message body
+ * @return string
+ */
+ public function getBody()
+ {
+ return $this->_body;
+ }
+
+ /**
+ * Get the original adapter-specific message
+ */
+ public function getMessage()
+ {
+ return $this->_clientMessage;
+ }
+}
diff --git a/library/Zend/Cloud/QueueService/MessageSet.php b/library/Zend/Cloud/QueueService/MessageSet.php
new file mode 100644
index 0000000..834939e
--- /dev/null
+++ b/library/Zend/Cloud/QueueService/MessageSet.php
@@ -0,0 +1,68 @@
+_messageCount = count($messages);
+ $this->_messages = new ArrayIterator($messages);
+ }
+
+ /**
+ * Countable: number of messages in collection
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->_messageCount;
+ }
+
+ /**
+ * IteratorAggregate: return iterable object
+ *
+ * @return Traversable
+ */
+ public function getIterator()
+ {
+ return $this->_messages;
+ }
+}
diff --git a/library/Zend/Cloud/StorageService/Adapter.php b/library/Zend/Cloud/StorageService/Adapter.php
new file mode 100644
index 0000000..bcb0225
--- /dev/null
+++ b/library/Zend/Cloud/StorageService/Adapter.php
@@ -0,0 +1,145 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_StorageService_Exception('Invalid options provided');
+ }
+
+ if (isset($options[self::LOCAL_DIRECTORY])) {
+ $this->_directory = $options[self::LOCAL_DIRECTORY];
+ } else {
+ $this->_directory = realpath(sys_get_temp_dir());
+ }
+ }
+
+ /**
+ * Get an item from the storage service.
+ *
+ * TODO: Support streaming
+ *
+ * @param string $path
+ * @param array $options
+ * @return false|string
+ */
+ public function fetchItem($path, $options = array())
+ {
+ $filepath = $this->_getFullPath($path);
+ $path = realpath($filepath);
+
+ if (!$path) {
+ return false;
+ }
+
+ return file_get_contents($path);
+ }
+
+ /**
+ * Store an item in the storage service.
+ *
+ * WARNING: This operation overwrites any item that is located at
+ * $destinationPath.
+ *
+ * @TODO Support streams
+ *
+ * @param string $destinationPath
+ * @param mixed $data
+ * @param array $options
+ * @return void
+ */
+ public function storeItem($destinationPath, $data, $options = array())
+ {
+ $path = $this->_getFullPath($destinationPath);
+ file_put_contents($path, $data);
+ chmod($path, 0777);
+ }
+
+ /**
+ * Delete an item in the storage service.
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ public function deleteItem($path, $options = array())
+ {
+ if (!isset($path)) {
+ return;
+ }
+
+ $filepath = $this->_getFullPath($path);
+ if (file_exists($filepath)) {
+ unlink($filepath);
+ }
+ }
+
+ /**
+ * Copy an item in the storage service to a given path.
+ *
+ * WARNING: This operation is *very* expensive for services that do not
+ * support copying an item natively.
+ *
+ * @TODO Support streams for those services that don't support natively
+ *
+ * @param string $sourcePath
+ * @param string $destination path
+ * @param array $options
+ * @return void
+ */
+ public function copyItem($sourcePath, $destinationPath, $options = array())
+ {
+ copy($this->_getFullPath($sourcePath), $this->_getFullPath($destinationPath));
+ }
+
+ /**
+ * Move an item in the storage service to a given path.
+ *
+ * WARNING: This operation is *very* expensive for services that do not
+ * support moving an item natively.
+ *
+ * @TODO Support streams for those services that don't support natively
+ *
+ * @param string $sourcePath
+ * @param string $destination path
+ * @param array $options
+ * @return void
+ */
+ public function moveItem($sourcePath, $destinationPath, $options = array())
+ {
+ rename($this->_getFullPath($sourcePath), $this->_getFullPath($destinationPath));
+ }
+
+ /**
+ * Rename an item in the storage service to a given name.
+ *
+ *
+ * @param string $path
+ * @param string $name
+ * @param array $options
+ * @return void
+ */
+ public function renameItem($path, $name, $options = null)
+ {
+ rename(
+ $this->_getFullPath($path),
+ dirname($this->_getFullPath($path)) . DIRECTORY_SEPARATOR . $name
+ );
+ }
+
+ /**
+ * List items in the given directory in the storage service
+ *
+ * The $path must be a directory
+ *
+ *
+ * @param string $path Must be a directory
+ * @param array $options
+ * @return array A list of item names
+ */
+ public function listItems($path, $options = null)
+ {
+ $listing = scandir($this->_getFullPath($path));
+
+ // Remove the hidden navigation directories
+ $listing = array_diff($listing, array('.', '..'));
+
+ return $listing;
+ }
+
+ /**
+ * Get a key/value array of metadata for the given path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return array
+ */
+ public function fetchMetadata($path, $options = array())
+ {
+ $fullPath = $this->_getFullPath($path);
+ $metadata = null;
+ if (file_exists($fullPath)) {
+ $metadata = stat(realpath($fullPath));
+ }
+
+ return isset($metadata) ? $metadata : false;
+ }
+
+ /**
+ * Store a key/value array of metadata at the given path.
+ * WARNING: This operation overwrites any metadata that is located at
+ * $destinationPath.
+ *
+ * @param string $destinationPath
+ * @param array $options
+ * @return void
+ */
+ public function storeMetadata($destinationPath, $metadata, $options = array())
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('Storing metadata not implemented');
+ }
+
+ /**
+ * Delete a key/value array of metadata at the given path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ public function deleteMetadata($path)
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('Deleting metadata not implemented');
+ }
+
+ /**
+ * Return the full path for the file.
+ *
+ * @param string $path
+ * @return string
+ */
+ private function _getFullPath($path)
+ {
+ return $this->_directory . DIRECTORY_SEPARATOR . $path;
+ }
+
+ /**
+ * Get the concrete client.
+ * @return strings
+ */
+ public function getClient()
+ {
+ return $this->_directory;
+ }
+}
diff --git a/library/Zend/Cloud/StorageService/Adapter/Nirvanix.php b/library/Zend/Cloud/StorageService/Adapter/Nirvanix.php
new file mode 100644
index 0000000..3d58344
--- /dev/null
+++ b/library/Zend/Cloud/StorageService/Adapter/Nirvanix.php
@@ -0,0 +1,399 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_StorageService_Exception('Invalid options provided');
+ }
+
+ $auth = array(
+ 'username' => $options[self::USERNAME],
+ 'password' => $options[self::PASSWORD],
+ 'appKey' => $options[self::APP_KEY],
+ );
+ $nirvanix_options = array();
+ if (isset($options[self::HTTP_ADAPTER])) {
+ $httpc = new Zend_Http_Client();
+ $httpc->setAdapter($options[self::HTTP_ADAPTER]);
+ $nirvanix_options['httpClient'] = $httpc;
+ }
+ try {
+ $this->_nirvanix = new Zend_Service_Nirvanix($auth, $nirvanix_options);
+ $this->_remoteDirectory = $options[self::REMOTE_DIRECTORY];
+ $this->_imfNs = $this->_nirvanix->getService('IMFS');
+ $this->_metadataNs = $this->_nirvanix->getService('Metadata');
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on create: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get an item from the storage service.
+ *
+ * @param string $path
+ * @param array $options
+ * @return mixed
+ */
+ public function fetchItem($path, $options = null)
+ {
+ $path = $this->_getFullPath($path);
+ try {
+ $item = $this->_imfNs->getContents($path);
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on fetch: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ return $item;
+ }
+
+ /**
+ * Store an item in the storage service.
+ * WARNING: This operation overwrites any item that is located at
+ * $destinationPath.
+ * @param string $destinationPath
+ * @param mixed $data
+ * @param array $options
+ * @return void
+ */
+ public function storeItem($destinationPath, $data, $options = null)
+ {
+ try {
+ $path = $this->_getFullPath($destinationPath);
+ $this->_imfNs->putContents($path, $data);
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on store: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ return true;
+ }
+
+ /**
+ * Delete an item in the storage service.
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ public function deleteItem($path, $options = null)
+ {
+ try {
+ $path = $this->_getFullPath($path);
+ $this->_imfNs->unlink($path);
+ } catch(Zend_Service_Nirvanix_Exception $e) {
+// if (trim(strtoupper($e->getMessage())) != 'INVALID PATH') {
+// // TODO Differentiate among errors in the Nirvanix adapter
+ throw new Zend_Cloud_StorageService_Exception('Error on delete: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Copy an item in the storage service to a given path.
+ * WARNING: This operation is *very* expensive for services that do not
+ * support copying an item natively.
+ *
+ * @param string $sourcePath
+ * @param string $destination path
+ * @param array $options
+ * @return void
+ */
+ public function copyItem($sourcePath, $destinationPath, $options = null)
+ {
+ try {
+ $sourcePath = $this->_getFullPath($sourcePath);
+ $destinationPath = $this->_getFullPath($destinationPath);
+ $this->_imfNs->CopyFiles(array('srcFilePath' => $sourcePath,
+ 'destFolderPath' => $destinationPath));
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on copy: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Move an item in the storage service to a given path.
+ * WARNING: This operation is *very* expensive for services that do not
+ * support moving an item natively.
+ *
+ * @param string $sourcePath
+ * @param string $destination path
+ * @param array $options
+ * @return void
+ */
+ public function moveItem($sourcePath, $destinationPath, $options = null)
+ {
+ try {
+ $sourcePath = $this->_getFullPath($sourcePath);
+ $destinationPath = $this->_getFullPath($destinationPath);
+ $this->_imfNs->RenameFile(array('filePath' => $sourcePath,
+ 'newFileName' => $destinationPath));
+ // $this->_imfNs->MoveFiles(array('srcFilePath' => $sourcePath,
+ // 'destFolderPath' => $destinationPath));
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on move: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Rename an item in the storage service to a given name.
+ *
+ *
+ * @param string $path
+ * @param string $name
+ * @param array $options
+ * @return void
+ */
+ public function renameItem($path, $name, $options = null)
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('Renaming not implemented');
+ }
+
+ /**
+ * Get a key/value array of metadata for the given path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return array An associative array of key/value pairs specifying the metadata for this object.
+ * If no metadata exists, an empty array is returned.
+ */
+ public function fetchMetadata($path, $options = null)
+ {
+ $path = $this->_getFullPath($path);
+ try {
+ $metadataNode = $this->_metadataNs->getMetadata(array('path' => $path));
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on fetching metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ $metadata = array();
+ $length = count($metadataNode->Metadata);
+
+ // Need to special case this as Nirvanix returns an array if there is
+ // more than one, but doesn't return an array if there is only one.
+ if ($length == 1)
+ {
+ $metadata[(string)$metadataNode->Metadata->Type->value] = (string)$metadataNode->Metadata->Value;
+ }
+ else if ($length > 1)
+ {
+ for ($i=0; $i<$length; $i++)
+ {
+ $metadata[(string)$metadataNode->Metadata[$i]->Type] = (string)$metadataNode->Metadata[$i]->Value;
+ }
+ }
+ return $metadata;
+ }
+
+ /**
+ * Store a key/value array of metadata at the given path.
+ * WARNING: This operation overwrites any metadata that is located at
+ * $destinationPath.
+ *
+ * @param string $destinationPath
+ * @param array $metadata associative array specifying the key/value pairs for the metadata.
+ * @param array $options
+ * @return void
+ */
+ public function storeMetadata($destinationPath, $metadata, $options = null)
+ {
+ $destinationPath = $this->_getFullPath($destinationPath);
+ if ($metadata != null) {
+ try {
+ foreach ($metadata AS $key=>$value) {
+ $metadataString = $key . ":" . $value;
+ $this->_metadataNs->SetMetadata(array(
+ 'path' => $destinationPath,
+ 'metadata' => $metadataString,
+ ));
+ }
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on storing metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ }
+
+ /**
+ * Delete a key/value array of metadata at the given path.
+ *
+ * @param string $path
+ * @param array $metadata - An associative array specifying the key/value pairs for the metadata
+ * to be deleted. If null, all metadata associated with the object will
+ * be deleted.
+ * @param array $options
+ * @return void
+ */
+ public function deleteMetadata($path, $metadata = null, $options = null)
+ {
+ $path = $this->_getFullPath($path);
+ try {
+ if ($metadata == null) {
+ $this->_metadataNs->DeleteAllMetadata(array('path' => $path));
+ } else {
+ foreach ($metadata AS $key=>$value) {
+ $this->_metadataNs->DeleteMetadata(array(
+ 'path' => $path,
+ 'metadata' => $key,
+ ));
+ }
+ }
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on deleting metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /*
+ * Recursively traverse all the folders and build an array that contains
+ * the path names for each folder.
+ *
+ * @param string $path folder path to get the list of folders from.
+ * @param array& $resultArray reference to the array that contains the path names
+ * for each folder.
+ */
+ private function getAllFolders($path, &$resultArray)
+ {
+ $response = $this->_imfNs->ListFolder(array(
+ 'folderPath' => $path,
+ 'pageNumber' => 1,
+ 'pageSize' => $this->maxPageSize,
+ ));
+ $numFolders = $response->ListFolder->TotalFolderCount;
+ if ($numFolders == 0) {
+ return;
+ } else {
+ //Need to special case this as Nirvanix returns an array if there is
+ //more than one, but doesn't return an array if there is only one.
+ if ($numFolders == 1) {
+ $folderPath = $response->ListFolder->Folder->Path;
+ array_push($resultArray, $folderPath);
+ $this->getAllFolders('/' . $folderPath, $resultArray);
+ } else {
+ foreach ($response->ListFolder->Folder as $arrayElem) {
+ $folderPath = $arrayElem->Path;
+ array_push($resultArray, $folderPath);
+ $this->getAllFolders('/' . $folderPath, $resultArray);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return an array of the items contained in the given path. The items
+ * returned are the files or objects that in the specified path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return array
+ */
+ public function listItems($path, $options = null)
+ {
+ $path = $this->_getFullPath($path);
+ $resultArray = array();
+
+ if (!isset($path)) {
+ return false;
+ } else {
+ try {
+ $response = $this->_imfNs->ListFolder(array(
+ 'folderPath' => $path,
+ 'pageNumber' => 1,
+ 'pageSize' => $this->maxPageSize,
+ ));
+ } catch (Zend_Service_Nirvanix_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on list: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ $numFiles = $response->ListFolder->TotalFileCount;
+
+ //Add the file names to the array
+ if ($numFiles != 0) {
+ //Need to special case this as Nirvanix returns an array if there is
+ //more than one, but doesn't return an array if there is only one.
+ if ($numFiles == 1) {
+ $resultArray[] = (string)$response->ListFolder->File->Name;
+ }
+ else {
+ foreach ($response->ListFolder->File as $arrayElem) {
+ $resultArray[] = (string) $arrayElem->Name;
+ }
+ }
+ }
+ }
+
+ return $resultArray;
+ }
+
+ /**
+ * Get full path to an object
+ *
+ * @param string $path
+ * @return string
+ */
+ private function _getFullPath($path)
+ {
+ return $this->_remoteDirectory . $path;
+ }
+
+ /**
+ * Get the concrete client.
+ * @return Zend_Service_Nirvanix
+ */
+ public function getClient()
+ {
+ return $this->_nirvanix;
+ }
+}
diff --git a/library/Zend/Cloud/StorageService/Adapter/S3.php b/library/Zend/Cloud/StorageService/Adapter/S3.php
new file mode 100644
index 0000000..21672e6
--- /dev/null
+++ b/library/Zend/Cloud/StorageService/Adapter/S3.php
@@ -0,0 +1,327 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_StorageService_Exception('Invalid options provided');
+ }
+
+ if (!isset($options[self::AWS_ACCESS_KEY]) || !isset($options[self::AWS_SECRET_KEY])) {
+ throw new Zend_Cloud_StorageService_Exception('AWS keys not specified!');
+ }
+
+ try {
+ $this->_s3 = new Zend_Service_Amazon_S3($options[self::AWS_ACCESS_KEY],
+ $options[self::AWS_SECRET_KEY]);
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on create: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ if (isset($options[self::HTTP_ADAPTER])) {
+ $this->_s3->getHttpClient()->setAdapter($options[self::HTTP_ADAPTER]);
+ }
+
+ if (isset($options[self::BUCKET_NAME])) {
+ $this->_defaultBucketName = $options[self::BUCKET_NAME];
+ }
+
+ if (isset($options[self::BUCKET_AS_DOMAIN])) {
+ $this->_defaultBucketAsDomain = $options[self::BUCKET_AS_DOMAIN];
+ }
+ }
+
+ /**
+ * Get an item from the storage service.
+ *
+ * @TODO Support streams
+ *
+ * @param string $path
+ * @param array $options
+ * @return string
+ */
+ public function fetchItem($path, $options = array())
+ {
+ $fullPath = $this->_getFullPath($path, $options);
+ try {
+ if (!empty($options[self::FETCH_STREAM])) {
+ return $this->_s3->getObjectStream($fullPath, $options[self::FETCH_STREAM]);
+ } else {
+ return $this->_s3->getObject($fullPath);
+ }
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on fetch: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Store an item in the storage service.
+ *
+ * WARNING: This operation overwrites any item that is located at
+ * $destinationPath.
+ *
+ * @TODO Support streams
+ *
+ * @param string $destinationPath
+ * @param string|resource $data
+ * @param array $options
+ * @return void
+ */
+ public function storeItem($destinationPath, $data, $options = array())
+ {
+ try {
+ $fullPath = $this->_getFullPath($destinationPath, $options);
+ return $this->_s3->putObject(
+ $fullPath,
+ $data,
+ empty($options[self::METADATA]) ? null : $options[self::METADATA]
+ );
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on store: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Delete an item in the storage service.
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ public function deleteItem($path, $options = array())
+ {
+ try {
+ $this->_s3->removeObject($this->_getFullPath($path, $options));
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on delete: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Copy an item in the storage service to a given path.
+ *
+ * WARNING: This operation is *very* expensive for services that do not
+ * support copying an item natively.
+ *
+ * @TODO Support streams for those services that don't support natively
+ *
+ * @param string $sourcePath
+ * @param string $destination path
+ * @param array $options
+ * @return void
+ */
+ public function copyItem($sourcePath, $destinationPath, $options = array())
+ {
+ try {
+ // TODO We *really* need to add support for object copying in the S3 adapter
+ $item = $this->fetch($_getFullPath(sourcePath), $options);
+ $this->storeItem($item, $destinationPath, $options);
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on copy: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Move an item in the storage service to a given path.
+ *
+ * @TODO Support streams for those services that don't support natively
+ *
+ * @param string $sourcePath
+ * @param string $destination path
+ * @param array $options
+ * @return void
+ */
+ public function moveItem($sourcePath, $destinationPath, $options = array())
+ {
+ try {
+ $fullSourcePath = $this->_getFullPath($sourcePath, $options);
+ $fullDestPath = $this->_getFullPath($destinationPath, $options);
+ return $this->_s3->moveObject(
+ $fullSourcePath,
+ $fullDestPath,
+ empty($options[self::METADATA]) ? null : $options[self::METADATA]
+ );
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on move: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Rename an item in the storage service to a given name.
+ *
+ *
+ * @param string $path
+ * @param string $name
+ * @param array $options
+ * @return void
+ */
+ public function renameItem($path, $name, $options = null)
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('Rename not implemented');
+ }
+
+ /**
+ * List items in the given directory in the storage service
+ *
+ * The $path must be a directory
+ *
+ *
+ * @param string $path Must be a directory
+ * @param array $options
+ * @return array A list of item names
+ */
+ public function listItems($path, $options = null)
+ {
+ try {
+ // TODO Support 'prefix' parameter for Zend_Service_Amazon_S3::getObjectsByBucket()
+ return $this->_s3->getObjectsByBucket($this->_defaultBucketName);
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on list: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get a key/value array of metadata for the given path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return array
+ */
+ public function fetchMetadata($path, $options = array())
+ {
+ try {
+ return $this->_s3->getInfo($this->_getFullPath($path, $options));
+ } catch (Zend_Service_Amazon_S3_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on fetch: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Store a key/value array of metadata at the given path.
+ * WARNING: This operation overwrites any metadata that is located at
+ * $destinationPath.
+ *
+ * @param string $destinationPath
+ * @param array $options
+ * @return void
+ */
+ public function storeMetadata($destinationPath, $metadata, $options = array())
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('Storing separate metadata is not supported, use storeItem() with \'metadata\' option key');
+ }
+
+ /**
+ * Delete a key/value array of metadata at the given path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ public function deleteMetadata($path)
+ {
+ require_once 'Zend/Cloud/OperationNotAvailableException.php';
+ throw new Zend_Cloud_OperationNotAvailableException('Deleting metadata not supported');
+ }
+
+ /**
+ * Get full path, including bucket, for an object
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ protected function _getFullPath($path, $options)
+ {
+ if (isset($options[self::BUCKET_NAME])) {
+ $bucket = $options[self::BUCKET_NAME];
+ } else if (isset($this->_defaultBucketName)) {
+ $bucket = $this->_defaultBucketName;
+ } else {
+ require_once 'Zend/Cloud/StorageService/Exception.php';
+ throw new Zend_Cloud_StorageService_Exception('Bucket name must be specified for S3 adapter.');
+ }
+
+ if (isset($options[self::BUCKET_AS_DOMAIN])) {
+ // TODO: support bucket domain names
+ require_once 'Zend/Cloud/StorageService/Exception.php';
+ throw new Zend_Cloud_StorageService_Exception('The S3 adapter does not currently support buckets in domain names.');
+ }
+
+ return trim($bucket) . '/' . trim($path);
+ }
+
+ /**
+ * Get the concrete client.
+ * @return Zend_Service_Amazon_S3
+ */
+ public function getClient()
+ {
+ return $this->_s3;
+ }
+}
diff --git a/library/Zend/Cloud/StorageService/Adapter/WindowsAzure.php b/library/Zend/Cloud/StorageService/Adapter/WindowsAzure.php
new file mode 100644
index 0000000..d883aa7
--- /dev/null
+++ b/library/Zend/Cloud/StorageService/Adapter/WindowsAzure.php
@@ -0,0 +1,443 @@
+toArray();
+ }
+
+ if (!is_array($options)) {
+ throw new Zend_Cloud_StorageService_Exception('Invalid options provided');
+ }
+
+ // Build Zend_Service_WindowsAzure_Storage_Blob instance
+ if (!isset($options[self::HOST])) {
+ $host = self::DEFAULT_HOST;
+ } else {
+ $host = $options[self::HOST];
+ }
+
+ if (!isset($options[self::ACCOUNT_NAME])) {
+ throw new Zend_Cloud_StorageService_Exception('No Windows Azure account name provided.');
+ }
+ if (!isset($options[self::ACCOUNT_KEY])) {
+ throw new Zend_Cloud_StorageService_Exception('No Windows Azure account key provided.');
+ }
+
+ $this->_storageClient = new Zend_Service_WindowsAzure_Storage_Blob($host,
+ $options[self::ACCOUNT_NAME], $options[self::ACCOUNT_KEY]);
+
+ // Parse other options
+ if (!empty($options[self::PROXY_HOST])) {
+ $proxyHost = $options[self::PROXY_HOST];
+ $proxyPort = isset($options[self::PROXY_PORT]) ? $options[self::PROXY_PORT] : 8080;
+ $proxyCredentials = isset($options[self::PROXY_CREDENTIALS]) ? $options[self::PROXY_CREDENTIALS] : '';
+
+ $this->_storageClient->setProxy(true, $proxyHost, $proxyPort, $proxyCredentials);
+ }
+
+ if (isset($options[self::HTTP_ADAPTER])) {
+ $this->_storageClient->setHttpClientChannel($options[self::HTTP_ADAPTER]);
+ }
+
+ // Set container
+ $this->_container = $options[self::CONTAINER];
+
+ // Make sure the container exists
+ if (!$this->_storageClient->containerExists($this->_container)) {
+ $this->_storageClient->createContainer($this->_container);
+ }
+ }
+
+ /**
+ * Get an item from the storage service.
+ *
+ * @param string $path
+ * @param array $options
+ * @return mixed
+ */
+ public function fetchItem($path, $options = null)
+ {
+ // Options
+ $returnType = self::RETURN_STRING;
+ $returnPath = tempnam('', 'azr');
+ $openMode = 'r';
+
+ // Parse options
+ if (is_array($options)) {
+ if (isset($options[self::RETURN_TYPE])) {
+ $returnType = $options[self::RETURN_TYPE];
+ }
+
+ if (isset($options[self::RETURN_PATHNAME])) {
+ $returnPath = $options[self::RETURN_PATHNAME];
+ }
+
+ if (isset($options[self::RETURN_OPENMODE])) {
+ $openMode = $options[self::RETURN_OPENMODE];
+ }
+ }
+
+ // Fetch the blob
+ try {
+ $this->_storageClient->getBlob(
+ $this->_container,
+ $path,
+ $returnPath
+ );
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "does not exist") !== false) {
+ return false;
+ }
+ throw new Zend_Cloud_StorageService_Exception('Error on fetch: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ // Return value
+ if ($returnType == self::RETURN_PATH) {
+ return $returnPath;
+ }
+ if ($returnType == self::RETURN_STRING) {
+ return file_get_contents($returnPath);
+ }
+ if ($returnType == self::RETURN_STREAM) {
+ return fopen($returnPath, $openMode);
+ }
+ }
+
+ /**
+ * Store an item in the storage service.
+ * WARNING: This operation overwrites any item that is located at
+ * $destinationPath.
+ * @param string $destinationPath
+ * @param mixed $data
+ * @param array $options
+ * @return boolean
+ */
+ public function storeItem($destinationPath, $data, $options = null)
+ {
+ // Create a temporary file that will be uploaded
+ $temporaryFilePath = '';
+ $removeTemporaryFilePath = false;
+
+ if (is_resource($data)) {
+ $temporaryFilePath = tempnam('', 'azr');
+ $fpDestination = fopen($temporaryFilePath, 'w');
+
+ $fpSource = $data;
+ rewind($fpSource);
+ while (!feof($fpSource)) {
+ fwrite($fpDestination, fread($fpSource, 8192));
+ }
+
+ fclose($fpDestination);
+
+ $removeTemporaryFilePath = true;
+ } elseif (file_exists($data)) {
+ $temporaryFilePath = $data;
+ $removeTemporaryFilePath = false;
+ } else {
+ $temporaryFilePath = tempnam('', 'azr');
+ file_put_contents($temporaryFilePath, $data);
+ $removeTemporaryFilePath = true;
+ }
+
+ try {
+ // Upload data
+ $this->_storageClient->putBlob(
+ $this->_container,
+ $destinationPath,
+ $temporaryFilePath
+ );
+ } catch(Zend_Service_WindowsAzure_Exception $e) {
+ @unlink($temporaryFilePath);
+ throw new Zend_Cloud_StorageService_Exception('Error on store: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ if ($removeTemporaryFilePath) {
+ @unlink($temporaryFilePath);
+ }
+ }
+
+ /**
+ * Delete an item in the storage service.
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ public function deleteItem($path, $options = null)
+ {
+ try {
+ $this->_storageClient->deleteBlob(
+ $this->_container,
+ $path
+ );
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on delete: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Copy an item in the storage service to a given path.
+ *
+ * @param string $sourcePath
+ * @param string $destinationPath
+ * @param array $options
+ * @return void
+ */
+ public function copyItem($sourcePath, $destinationPath, $options = null)
+ {
+ try {
+ $this->_storageClient->copyBlob(
+ $this->_container,
+ $sourcePath,
+ $this->_container,
+ $destinationPath
+ );
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on copy: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Move an item in the storage service to a given path.
+ *
+ * @param string $sourcePath
+ * @param string $destinationPath
+ * @param array $options
+ * @return void
+ */
+ public function moveItem($sourcePath, $destinationPath, $options = null)
+ {
+ try {
+ $this->_storageClient->copyBlob(
+ $this->_container,
+ $sourcePath,
+ $this->_container,
+ $destinationPath
+ );
+
+ $this->_storageClient->deleteBlob(
+ $this->_container,
+ $sourcePath
+ );
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on move: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ }
+
+ /**
+ * Rename an item in the storage service to a given name.
+ *
+ *
+ * @param string $path
+ * @param string $name
+ * @param array $options
+ * @return void
+ */
+ public function renameItem($path, $name, $options = null)
+ {
+ return $this->moveItem($path, $name, $options);
+ }
+
+ /**
+ * List items in the given directory in the storage service
+ *
+ * The $path must be a directory
+ *
+ *
+ * @param string $path Must be a directory
+ * @param array $options
+ * @return array A list of item names
+ */
+ public function listItems($path, $options = null)
+ {
+ // Options
+ $returnType = self::RETURN_NAMES; // 1: return list of paths, 2: return raw output from underlying provider
+
+ // Parse options
+ if (is_array($options)&& isset($options[self::RETURN_TYPE])) {
+ $returnType = $options[self::RETURN_TYPE];
+ }
+
+ try {
+ // Fetch list
+ $blobList = $this->_storageClient->listBlobs(
+ $this->_container,
+ $path
+ );
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on list: '.$e->getMessage(), $e->getCode(), $e);
+ }
+
+ // Return
+ if ($returnType == self::RETURN_LIST) {
+ return $blobList;
+ }
+
+ $returnValue = array();
+ foreach ($blobList as $blob) {
+ $returnValue[] = $blob->Name;
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Get a key/value array of metadata for the given path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return array
+ */
+ public function fetchMetadata($path, $options = null)
+ {
+ try {
+ return $this->_storageClient->getBlobMetaData(
+ $this->_container,
+ $path
+ );
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "could not be accessed") !== false) {
+ return false;
+ }
+ throw new Zend_Cloud_StorageService_Exception('Error on fetch: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Store a key/value array of metadata at the given path.
+ * WARNING: This operation overwrites any metadata that is located at
+ * $destinationPath.
+ *
+ * @param string $destinationPath
+ * @param array $options
+ * @return void
+ */
+ public function storeMetadata($destinationPath, $metadata, $options = null)
+ {
+ try {
+ $this->_storageClient->setBlobMetadata($this->_container, $destinationPath, $metadata);
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "could not be accessed") === false) {
+ throw new Zend_Cloud_StorageService_Exception('Error on store metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ }
+
+ /**
+ * Delete a key/value array of metadata at the given path.
+ *
+ * @param string $path
+ * @param array $options
+ * @return void
+ */
+ public function deleteMetadata($path, $options = null)
+ {
+ try {
+ $this->_storageClient->setBlobMetadata($this->_container, $destinationPath, array());
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ if (strpos($e->getMessage(), "could not be accessed") === false) {
+ throw new Zend_Cloud_StorageService_Exception('Error on delete metadata: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ }
+
+ /**
+ * Delete container
+ *
+ * @return void
+ */
+ public function deleteContainer()
+ {
+ try {
+ $this->_storageClient->deleteContainer($this->_container);
+ } catch (Zend_Service_WindowsAzure_Exception $e) {
+ throw new Zend_Cloud_StorageService_Exception('Error on delete: '.$e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Get the concrete adapter.
+ * @return Zend_Service_Azure_Storage_Blob
+ */
+ public function getClient()
+ {
+ return $this->_storageClient;
+ }
+}
diff --git a/library/Zend/Cloud/StorageService/Exception.php b/library/Zend/Cloud/StorageService/Exception.php
new file mode 100644
index 0000000..3cab170
--- /dev/null
+++ b/library/Zend/Cloud/StorageService/Exception.php
@@ -0,0 +1,38 @@
+_init();
+ if ($options != null) {
+ // use Zend_Config objects if provided
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ }
+ // pass arrays to setOptions
+ if (is_array($options)) {
+ $this->setOptions($options);
+ }
+ }
+ $this->_prepare();
+ }
+
+ /**
+ * setConfig()
+ *
+ * @param Zend_Config $config
+ * @return Zend_CodeGenerator_Abstract
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ $this->setOptions($config->toArray());
+ return $this;
+ }
+
+ /**
+ * setOptions()
+ *
+ * @param array $options
+ * @return Zend_CodeGenerator_Abstract
+ */
+ public function setOptions(Array $options)
+ {
+ foreach ($options as $optionName => $optionValue) {
+ $methodName = 'set' . $optionName;
+ if (method_exists($this, $methodName)) {
+ $this->{$methodName}($optionValue);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * setSourceContent()
+ *
+ * @param string $sourceContent
+ */
+ public function setSourceContent($sourceContent)
+ {
+ $this->_sourceContent = $sourceContent;
+ return;
+ }
+
+ /**
+ * getSourceContent()
+ *
+ * @return string
+ */
+ public function getSourceContent()
+ {
+ return $this->_sourceContent;
+ }
+
+ /**
+ * _init() - this is called before the constuctor
+ *
+ */
+ protected function _init()
+ {
+
+ }
+
+ /**
+ * _prepare() - this is called at construction completion
+ *
+ */
+ protected function _prepare()
+ {
+
+ }
+
+ /**
+ * generate() - must be implemented by the child
+ *
+ */
+ abstract public function generate();
+
+ /**
+ * __toString() - casting to a string will in turn call generate()
+ *
+ * @return string
+ */
+ final public function __toString()
+ {
+ return $this->generate();
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Exception.php b/library/Zend/CodeGenerator/Exception.php
new file mode 100644
index 0000000..df40dbd
--- /dev/null
+++ b/library/Zend/CodeGenerator/Exception.php
@@ -0,0 +1,35 @@
+_isSourceDirty = ($isSourceDirty) ? true : false;
+ return $this;
+ }
+
+ /**
+ * isSourceDirty()
+ *
+ * @return bool
+ */
+ public function isSourceDirty()
+ {
+ return $this->_isSourceDirty;
+ }
+
+ /**
+ * setIndentation()
+ *
+ * @param string|int $indentation
+ * @return Zend_CodeGenerator_Php_Abstract
+ */
+ public function setIndentation($indentation)
+ {
+ $this->_indentation = $indentation;
+ return $this;
+ }
+
+ /**
+ * getIndentation()
+ *
+ * @return string|int
+ */
+ public function getIndentation()
+ {
+ return $this->_indentation;
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Body.php b/library/Zend/CodeGenerator/Php/Body.php
new file mode 100644
index 0000000..4061d50
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Body.php
@@ -0,0 +1,73 @@
+_content = $content;
+ return $this;
+ }
+
+ /**
+ * getContent()
+ *
+ * @return string
+ */
+ public function getContent()
+ {
+ return (string) $this->_content;
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ return $this->getContent();
+ }
+}
diff --git a/library/Zend/CodeGenerator/Php/Class.php b/library/Zend/CodeGenerator/Php/Class.php
new file mode 100644
index 0000000..1c943c1
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Class.php
@@ -0,0 +1,513 @@
+setSourceContent($class->getSourceContent());
+ $class->setSourceDirty(false);
+
+ if ($reflectionClass->getDocComment() != '') {
+ $class->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($reflectionClass->getDocblock()));
+ }
+
+ $class->setAbstract($reflectionClass->isAbstract());
+ $class->setName($reflectionClass->getName());
+
+ if ($parentClass = $reflectionClass->getParentClass()) {
+ $class->setExtendedClass($parentClass->getName());
+ $interfaces = array_diff($reflectionClass->getInterfaces(), $parentClass->getInterfaces());
+ } else {
+ $interfaces = $reflectionClass->getInterfaces();
+ }
+
+ $interfaceNames = array();
+ foreach($interfaces AS $interface) {
+ $interfaceNames[] = $interface->getName();
+ }
+
+ $class->setImplementedInterfaces($interfaceNames);
+
+ $properties = array();
+ foreach ($reflectionClass->getProperties() as $reflectionProperty) {
+ if ($reflectionProperty->getDeclaringClass()->getName() == $class->getName()) {
+ $properties[] = Zend_CodeGenerator_Php_Property::fromReflection($reflectionProperty);
+ }
+ }
+ $class->setProperties($properties);
+
+ $methods = array();
+ foreach ($reflectionClass->getMethods() as $reflectionMethod) {
+ if ($reflectionMethod->getDeclaringClass()->getName() == $class->getName()) {
+ $methods[] = Zend_CodeGenerator_Php_Method::fromReflection($reflectionMethod);
+ }
+ }
+ $class->setMethods($methods);
+
+ return $class;
+ }
+
+ /**
+ * setDocblock() Set the docblock
+ *
+ * @param Zend_CodeGenerator_Php_Docblock|array|string $docblock
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public function setDocblock($docblock)
+ {
+ if (is_string($docblock)) {
+ $docblock = array('shortDescription' => $docblock);
+ }
+
+ if (is_array($docblock)) {
+ $docblock = new Zend_CodeGenerator_Php_Docblock($docblock);
+ } elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock');
+ }
+
+ $this->_docblock = $docblock;
+ return $this;
+ }
+
+ /**
+ * getDocblock()
+ *
+ * @return Zend_CodeGenerator_Php_Docblock
+ */
+ public function getDocblock()
+ {
+ return $this->_docblock;
+ }
+
+ /**
+ * setName()
+ *
+ * @param string $name
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setName($name)
+ {
+ $this->_name = $name;
+ return $this;
+ }
+
+ /**
+ * getName()
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * setAbstract()
+ *
+ * @param bool $isAbstract
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setAbstract($isAbstract)
+ {
+ $this->_isAbstract = ($isAbstract) ? true : false;
+ return $this;
+ }
+
+ /**
+ * isAbstract()
+ *
+ * @return bool
+ */
+ public function isAbstract()
+ {
+ return $this->_isAbstract;
+ }
+
+ /**
+ * setExtendedClass()
+ *
+ * @param string $extendedClass
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setExtendedClass($extendedClass)
+ {
+ $this->_extendedClass = $extendedClass;
+ return $this;
+ }
+
+ /**
+ * getExtendedClass()
+ *
+ * @return string
+ */
+ public function getExtendedClass()
+ {
+ return $this->_extendedClass;
+ }
+
+ /**
+ * setImplementedInterfaces()
+ *
+ * @param array $implementedInterfaces
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setImplementedInterfaces(Array $implementedInterfaces)
+ {
+ $this->_implementedInterfaces = $implementedInterfaces;
+ return $this;
+ }
+
+ /**
+ * getImplementedInterfaces
+ *
+ * @return array
+ */
+ public function getImplementedInterfaces()
+ {
+ return $this->_implementedInterfaces;
+ }
+
+ /**
+ * setProperties()
+ *
+ * @param array $properties
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setProperties(Array $properties)
+ {
+ foreach ($properties as $property) {
+ $this->setProperty($property);
+ }
+
+ return $this;
+ }
+
+ /**
+ * setProperty()
+ *
+ * @param array|Zend_CodeGenerator_Php_Property $property
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setProperty($property)
+ {
+ if (is_array($property)) {
+ $property = new Zend_CodeGenerator_Php_Property($property);
+ $propertyName = $property->getName();
+ } elseif ($property instanceof Zend_CodeGenerator_Php_Property) {
+ $propertyName = $property->getName();
+ } else {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('setProperty() expects either an array of property options or an instance of Zend_CodeGenerator_Php_Property');
+ }
+
+ if (isset($this->_properties[$propertyName])) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('A property by name ' . $propertyName . ' already exists in this class.');
+ }
+
+ $this->_properties[$propertyName] = $property;
+ return $this;
+ }
+
+ /**
+ * getProperties()
+ *
+ * @return array
+ */
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+
+ /**
+ * getProperty()
+ *
+ * @param string $propertyName
+ * @return Zend_CodeGenerator_Php_Property
+ */
+ public function getProperty($propertyName)
+ {
+ foreach ($this->_properties as $property) {
+ if ($property->getName() == $propertyName) {
+ return $property;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * hasProperty()
+ *
+ * @param string $propertyName
+ * @return bool
+ */
+ public function hasProperty($propertyName)
+ {
+ return isset($this->_properties[$propertyName]);
+ }
+
+ /**
+ * setMethods()
+ *
+ * @param array $methods
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setMethods(Array $methods)
+ {
+ foreach ($methods as $method) {
+ $this->setMethod($method);
+ }
+ return $this;
+ }
+
+ /**
+ * setMethod()
+ *
+ * @param array|Zend_CodeGenerator_Php_Method $method
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function setMethod($method)
+ {
+ if (is_array($method)) {
+ $method = new Zend_CodeGenerator_Php_Method($method);
+ $methodName = $method->getName();
+ } elseif ($method instanceof Zend_CodeGenerator_Php_Method) {
+ $methodName = $method->getName();
+ } else {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('setMethod() expects either an array of method options or an instance of Zend_CodeGenerator_Php_Method');
+ }
+
+ if (isset($this->_methods[$methodName])) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('A method by name ' . $methodName . ' already exists in this class.');
+ }
+
+ $this->_methods[$methodName] = $method;
+ return $this;
+ }
+
+ /**
+ * getMethods()
+ *
+ * @return array
+ */
+ public function getMethods()
+ {
+ return $this->_methods;
+ }
+
+ /**
+ * getMethod()
+ *
+ * @param string $methodName
+ * @return Zend_CodeGenerator_Php_Method
+ */
+ public function getMethod($methodName)
+ {
+ foreach ($this->_methods as $method) {
+ if ($method->getName() == $methodName) {
+ return $method;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * hasMethod()
+ *
+ * @param string $methodName
+ * @return bool
+ */
+ public function hasMethod($methodName)
+ {
+ return isset($this->_methods[$methodName]);
+ }
+
+ /**
+ * isSourceDirty()
+ *
+ * @return bool
+ */
+ public function isSourceDirty()
+ {
+ if (($docblock = $this->getDocblock()) && $docblock->isSourceDirty()) {
+ return true;
+ }
+
+ foreach ($this->_properties as $property) {
+ if ($property->isSourceDirty()) {
+ return true;
+ }
+ }
+
+ foreach ($this->_methods as $method) {
+ if ($method->isSourceDirty()) {
+ return true;
+ }
+ }
+
+ return parent::isSourceDirty();
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ if (!$this->isSourceDirty()) {
+ return $this->getSourceContent();
+ }
+
+ $output = '';
+
+ if (null !== ($docblock = $this->getDocblock())) {
+ $docblock->setIndentation('');
+ $output .= $docblock->generate();
+ }
+
+ if ($this->isAbstract()) {
+ $output .= 'abstract ';
+ }
+
+ $output .= 'class ' . $this->getName();
+
+ if ( !empty( $this->_extendedClass) ) {
+ $output .= ' extends ' . $this->_extendedClass;
+ }
+
+ $implemented = $this->getImplementedInterfaces();
+ if (!empty($implemented)) {
+ $output .= ' implements ' . implode(', ', $implemented);
+ }
+
+ $output .= self::LINE_FEED . '{' . self::LINE_FEED . self::LINE_FEED;
+
+ $properties = $this->getProperties();
+ if (!empty($properties)) {
+ foreach ($properties as $property) {
+ $output .= $property->generate() . self::LINE_FEED . self::LINE_FEED;
+ }
+ }
+
+ $methods = $this->getMethods();
+ if (!empty($methods)) {
+ foreach ($methods as $method) {
+ $output .= $method->generate() . self::LINE_FEED;
+ }
+ }
+
+ $output .= self::LINE_FEED . '}' . self::LINE_FEED;
+
+ return $output;
+ }
+
+ /**
+ * _init() - is called at construction time
+ *
+ */
+ protected function _init()
+ {
+ $this->_properties = new Zend_CodeGenerator_Php_Member_Container(Zend_CodeGenerator_Php_Member_Container::TYPE_PROPERTY);
+ $this->_methods = new Zend_CodeGenerator_Php_Member_Container(Zend_CodeGenerator_Php_Member_Container::TYPE_METHOD);
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Docblock.php b/library/Zend/CodeGenerator/Php/Docblock.php
new file mode 100644
index 0000000..58b378c
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Docblock.php
@@ -0,0 +1,224 @@
+setSourceContent($reflectionDocblock->getContents());
+ $docblock->setSourceDirty(false);
+
+ $docblock->setShortDescription($reflectionDocblock->getShortDescription());
+ $docblock->setLongDescription($reflectionDocblock->getLongDescription());
+
+ foreach ($reflectionDocblock->getTags() as $tag) {
+ $docblock->setTag(Zend_CodeGenerator_Php_Docblock_Tag::fromReflection($tag));
+ }
+
+ return $docblock;
+ }
+
+ /**
+ * setShortDescription()
+ *
+ * @param string $shortDescription
+ * @return Zend_CodeGenerator_Php_Docblock
+ */
+ public function setShortDescription($shortDescription)
+ {
+ $this->_shortDescription = $shortDescription;
+ return $this;
+ }
+
+ /**
+ * getShortDescription()
+ *
+ * @return string
+ */
+ public function getShortDescription()
+ {
+ return $this->_shortDescription;
+ }
+
+ /**
+ * setLongDescription()
+ *
+ * @param string $longDescription
+ * @return Zend_CodeGenerator_Php_Docblock
+ */
+ public function setLongDescription($longDescription)
+ {
+ $this->_longDescription = $longDescription;
+ return $this;
+ }
+
+ /**
+ * getLongDescription()
+ *
+ * @return string
+ */
+ public function getLongDescription()
+ {
+ return $this->_longDescription;
+ }
+
+ /**
+ * setTags()
+ *
+ * @param array $tags
+ * @return Zend_CodeGenerator_Php_Docblock
+ */
+ public function setTags(Array $tags)
+ {
+ foreach ($tags as $tag) {
+ $this->setTag($tag);
+ }
+
+ return $this;
+ }
+
+ /**
+ * setTag()
+ *
+ * @param array|Zend_CodeGenerator_Php_Docblock_Tag $tag
+ * @return Zend_CodeGenerator_Php_Docblock
+ */
+ public function setTag($tag)
+ {
+ if (is_array($tag)) {
+ $tag = new Zend_CodeGenerator_Php_Docblock_Tag($tag);
+ } elseif (!$tag instanceof Zend_CodeGenerator_Php_Docblock_Tag) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception(
+ 'setTag() expects either an array of method options or an '
+ . 'instance of Zend_CodeGenerator_Php_Docblock_Tag'
+ );
+ }
+
+ $this->_tags[] = $tag;
+ return $this;
+ }
+
+ /**
+ * getTags
+ *
+ * @return array Array of Zend_CodeGenerator_Php_Docblock_Tag
+ */
+ public function getTags()
+ {
+ return $this->_tags;
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ if (!$this->isSourceDirty()) {
+ return $this->_docCommentize($this->getSourceContent());
+ }
+
+ $output = '';
+ if (null !== ($sd = $this->getShortDescription())) {
+ $output .= $sd . self::LINE_FEED . self::LINE_FEED;
+ }
+ if (null !== ($ld = $this->getLongDescription())) {
+ $output .= $ld . self::LINE_FEED . self::LINE_FEED;
+ }
+
+ foreach ($this->getTags() as $tag) {
+ $output .= $tag->generate() . self::LINE_FEED;
+ }
+
+ return $this->_docCommentize(trim($output));
+ }
+
+ /**
+ * _docCommentize()
+ *
+ * @param string $content
+ * @return string
+ */
+ protected function _docCommentize($content)
+ {
+ $indent = $this->getIndentation();
+ $output = $indent . '/**' . self::LINE_FEED;
+ $content = wordwrap($content, 80, self::LINE_FEED);
+ $lines = explode(self::LINE_FEED, $content);
+ foreach ($lines as $line) {
+ $output .= $indent . ' *';
+ if ($line) {
+ $output .= " $line";
+ }
+ $output .= self::LINE_FEED;
+ }
+ $output .= $indent . ' */' . self::LINE_FEED;
+ return $output;
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Docblock/Tag.php b/library/Zend/CodeGenerator/Php/Docblock/Tag.php
new file mode 100644
index 0000000..459f616
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Docblock/Tag.php
@@ -0,0 +1,178 @@
+getName();
+
+ $codeGenDocblockTag = self::factory($tagName);
+
+ // transport any properties via accessors and mutators from reflection to codegen object
+ $reflectionClass = new ReflectionClass($reflectionTag);
+ foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
+ if (substr($method->getName(), 0, 3) == 'get') {
+ $propertyName = substr($method->getName(), 3);
+ if (method_exists($codeGenDocblockTag, 'set' . $propertyName)) {
+ $codeGenDocblockTag->{'set' . $propertyName}($reflectionTag->{'get' . $propertyName}());
+ }
+ }
+ }
+
+ return $codeGenDocblockTag;
+ }
+
+ /**
+ * setPluginLoader()
+ *
+ * @param Zend_Loader_PluginLoader $pluginLoader
+ */
+ public static function setPluginLoader(Zend_Loader_PluginLoader $pluginLoader)
+ {
+ self::$_pluginLoader = $pluginLoader;
+ return;
+ }
+
+ /**
+ * getPluginLoader()
+ *
+ * @return Zend_Loader_PluginLoader
+ */
+ public static function getPluginLoader()
+ {
+ if (self::$_pluginLoader == null) {
+ require_once 'Zend/Loader/PluginLoader.php';
+ self::setPluginLoader(new Zend_Loader_PluginLoader(array(
+ 'Zend_CodeGenerator_Php_Docblock_Tag' => dirname(__FILE__) . '/Tag/'))
+ );
+ }
+
+ return self::$_pluginLoader;
+ }
+
+ public static function factory($tagName)
+ {
+ $pluginLoader = self::getPluginLoader();
+
+ try {
+ $tagClass = $pluginLoader->load($tagName);
+ } catch (Zend_Loader_Exception $exception) {
+ $tagClass = 'Zend_CodeGenerator_Php_Docblock_Tag';
+ }
+
+ $tag = new $tagClass(array('name' => $tagName));
+ return $tag;
+ }
+
+ /**
+ * setName()
+ *
+ * @param string $name
+ * @return Zend_CodeGenerator_Php_Docblock_Tag
+ */
+ public function setName($name)
+ {
+ $this->_name = ltrim($name, '@');
+ return $this;
+ }
+
+ /**
+ * getName()
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * setDescription()
+ *
+ * @param string $description
+ * @return Zend_CodeGenerator_Php_Docblock_Tag
+ */
+ public function setDescription($description)
+ {
+ $this->_description = $description;
+ return $this;
+ }
+
+ /**
+ * getDescription()
+ *
+ * @return string
+ */
+ public function getDescription()
+ {
+ return $this->_description;
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $tag = '@' . $this->_name;
+ if ($this->_description) {
+ $tag .= ' ' . $this->_description;
+ }
+ return $tag;
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Docblock/Tag/License.php b/library/Zend/CodeGenerator/Php/Docblock/Tag/License.php
new file mode 100644
index 0000000..f6a3056
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Docblock/Tag/License.php
@@ -0,0 +1,98 @@
+setName('license');
+ $returnTag->setUrl($reflectionTagLicense->getUrl());
+ $returnTag->setDescription($reflectionTagLicense->getDescription());
+
+ return $returnTag;
+ }
+
+ /**
+ * setUrl()
+ *
+ * @param string $url
+ * @return Zend_CodeGenerator_Php_Docblock_Tag_License
+ */
+ public function setUrl($url)
+ {
+ $this->_url = $url;
+ return $this;
+ }
+
+ /**
+ * getUrl()
+ *
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->_url;
+ }
+
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $output = '@license ' . $this->_url . ' ' . $this->_description . self::LINE_FEED;
+ return $output;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/CodeGenerator/Php/Docblock/Tag/Param.php b/library/Zend/CodeGenerator/Php/Docblock/Tag/Param.php
new file mode 100644
index 0000000..dd0179c
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Docblock/Tag/Param.php
@@ -0,0 +1,128 @@
+setName('param');
+ $paramTag->setDatatype($reflectionTagParam->getType()); // @todo rename
+ $paramTag->setParamName($reflectionTagParam->getVariableName());
+ $paramTag->setDescription($reflectionTagParam->getDescription());
+
+ return $paramTag;
+ }
+
+ /**
+ * setDatatype()
+ *
+ * @param string $datatype
+ * @return Zend_CodeGenerator_Php_Docblock_Tag_Param
+ */
+ public function setDatatype($datatype)
+ {
+ $this->_datatype = $datatype;
+ return $this;
+ }
+
+ /**
+ * getDatatype
+ *
+ * @return string
+ */
+ public function getDatatype()
+ {
+ return $this->_datatype;
+ }
+
+ /**
+ * setParamName()
+ *
+ * @param string $paramName
+ * @return Zend_CodeGenerator_Php_Docblock_Tag_Param
+ */
+ public function setParamName($paramName)
+ {
+ $this->_paramName = $paramName;
+ return $this;
+ }
+
+ /**
+ * getParamName()
+ *
+ * @return string
+ */
+ public function getParamName()
+ {
+ return $this->_paramName;
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $output = '@param '
+ . (($this->_datatype != null) ? $this->_datatype : 'unknown')
+ . (($this->_paramName != null) ? ' $' . $this->_paramName : '')
+ . (($this->_description != null) ? ' ' . $this->_description : '');
+ return $output;
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Docblock/Tag/Return.php b/library/Zend/CodeGenerator/Php/Docblock/Tag/Return.php
new file mode 100644
index 0000000..43f7b4f
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Docblock/Tag/Return.php
@@ -0,0 +1,98 @@
+setName('return');
+ $returnTag->setDatatype($reflectionTagReturn->getType()); // @todo rename
+ $returnTag->setDescription($reflectionTagReturn->getDescription());
+
+ return $returnTag;
+ }
+
+ /**
+ * setDatatype()
+ *
+ * @param string $datatype
+ * @return Zend_CodeGenerator_Php_Docblock_Tag_Return
+ */
+ public function setDatatype($datatype)
+ {
+ $this->_datatype = $datatype;
+ return $this;
+ }
+
+ /**
+ * getDatatype()
+ *
+ * @return string
+ */
+ public function getDatatype()
+ {
+ return $this->_datatype;
+ }
+
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $output = '@return ' . $this->_datatype . ' ' . $this->_description;
+ return $output;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/CodeGenerator/Php/Exception.php b/library/Zend/CodeGenerator/Php/Exception.php
new file mode 100644
index 0000000..883d823
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Exception.php
@@ -0,0 +1,37 @@
+getFilename();
+ }
+
+ if ($fileName == '') {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('FileName does not exist.');
+ }
+
+ // cannot use realpath since the file might not exist, but we do need to have the index
+ // in the same DIRECTORY_SEPARATOR that realpath would use:
+ $fileName = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $fileName);
+
+ self::$_fileCodeGenerators[$fileName] = $fileCodeGenerator;
+
+ }
+
+ /**
+ * fromReflectedFileName() - use this if you intend on generating code generation objects based on the same file.
+ * This will keep previous changes to the file in tact during the same PHP process
+ *
+ * @param string $filePath
+ * @param bool $usePreviousCodeGeneratorIfItExists
+ * @param bool $includeIfNotAlreadyIncluded
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public static function fromReflectedFileName($filePath, $usePreviousCodeGeneratorIfItExists = true, $includeIfNotAlreadyIncluded = true)
+ {
+ $realpath = realpath($filePath);
+
+ if ($realpath === false) {
+ if ( ($realpath = Zend_Reflection_file::findRealpathInIncludePath($filePath)) === false) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('No file for ' . $realpath . ' was found.');
+ }
+ }
+
+ if ($usePreviousCodeGeneratorIfItExists && isset(self::$_fileCodeGenerators[$realpath])) {
+ return self::$_fileCodeGenerators[$realpath];
+ }
+
+ if ($includeIfNotAlreadyIncluded && !in_array($realpath, get_included_files())) {
+ include $realpath;
+ }
+
+ $codeGenerator = self::fromReflection(($fileReflector = new Zend_Reflection_File($realpath)));
+
+ if (!isset(self::$_fileCodeGenerators[$fileReflector->getFileName()])) {
+ self::$_fileCodeGenerators[$fileReflector->getFileName()] = $codeGenerator;
+ }
+
+ return $codeGenerator;
+ }
+
+ /**
+ * fromReflection()
+ *
+ * @param Zend_Reflection_File $reflectionFile
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public static function fromReflection(Zend_Reflection_File $reflectionFile)
+ {
+ $file = new self();
+
+ $file->setSourceContent($reflectionFile->getContents());
+ $file->setSourceDirty(false);
+
+ $body = $reflectionFile->getContents();
+
+ // @todo this whole area needs to be reworked with respect to how body lines are processed
+ foreach ($reflectionFile->getClasses() as $class) {
+ $file->setClass(Zend_CodeGenerator_Php_Class::fromReflection($class));
+ $classStartLine = $class->getStartLine(true);
+ $classEndLine = $class->getEndLine();
+
+ $bodyLines = explode("\n", $body);
+ $bodyReturn = array();
+ for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
+ if ($lineNum == $classStartLine) {
+ $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerClass); //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
+ $lineNum = $classEndLine;
+ } else {
+ $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
+ }
+ }
+ $body = implode("\n", $bodyReturn);
+ unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
+ }
+
+ if (($reflectionFile->getDocComment() != '')) {
+ $docblock = $reflectionFile->getDocblock();
+ $file->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($docblock));
+
+ $bodyLines = explode("\n", $body);
+ $bodyReturn = array();
+ for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
+ if ($lineNum == $docblock->getStartLine()) {
+ $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerDocblock); //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
+ $lineNum = $docblock->getEndLine();
+ } else {
+ $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
+ }
+ }
+ $body = implode("\n", $bodyReturn);
+ unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
+ }
+
+ $file->setBody($body);
+
+ return $file;
+ }
+
+ /**
+ * setDocblock() Set the docblock
+ *
+ * @param Zend_CodeGenerator_Php_Docblock|array|string $docblock
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public function setDocblock($docblock)
+ {
+ if (is_string($docblock)) {
+ $docblock = array('shortDescription' => $docblock);
+ }
+
+ if (is_array($docblock)) {
+ $docblock = new Zend_CodeGenerator_Php_Docblock($docblock);
+ } elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock');
+ }
+
+ $this->_docblock = $docblock;
+ return $this;
+ }
+
+ /**
+ * Get docblock
+ *
+ * @return Zend_CodeGenerator_Php_Docblock
+ */
+ public function getDocblock()
+ {
+ return $this->_docblock;
+ }
+
+ /**
+ * setRequiredFiles
+ *
+ * @param array $requiredFiles
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public function setRequiredFiles($requiredFiles)
+ {
+ $this->_requiredFiles = $requiredFiles;
+ return $this;
+ }
+
+ /**
+ * getRequiredFiles()
+ *
+ * @return array
+ */
+ public function getRequiredFiles()
+ {
+ return $this->_requiredFiles;
+ }
+
+ /**
+ * setClasses()
+ *
+ * @param array $classes
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public function setClasses(Array $classes)
+ {
+ foreach ($classes as $class) {
+ $this->setClass($class);
+ }
+ return $this;
+ }
+
+ /**
+ * getClass()
+ *
+ * @param string $name
+ * @return Zend_CodeGenerator_Php_Class
+ */
+ public function getClass($name = null)
+ {
+ if ($name == null) {
+ reset($this->_classes);
+ return current($this->_classes);
+ }
+
+ return $this->_classes[$name];
+ }
+
+ /**
+ * setClass()
+ *
+ * @param Zend_CodeGenerator_Php_Class|array $class
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public function setClass($class)
+ {
+ if (is_array($class)) {
+ $class = new Zend_CodeGenerator_Php_Class($class);
+ $className = $class->getName();
+ } elseif ($class instanceof Zend_CodeGenerator_Php_Class) {
+ $className = $class->getName();
+ } else {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('Expecting either an array or an instance of Zend_CodeGenerator_Php_Class');
+ }
+
+ // @todo check for dup here
+
+ $this->_classes[$className] = $class;
+ return $this;
+ }
+
+ /**
+ * setFilename()
+ *
+ * @param string $filename
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public function setFilename($filename)
+ {
+ $this->_filename = $filename;
+ return $this;
+ }
+
+ /**
+ * getFilename()
+ *
+ * @return string
+ */
+ public function getFilename()
+ {
+ return $this->_filename;
+ }
+
+ /**
+ * getClasses()
+ *
+ * @return array Array of Zend_CodeGenerator_Php_Class
+ */
+ public function getClasses()
+ {
+ return $this->_classes;
+ }
+
+ /**
+ * setBody()
+ *
+ * @param string $body
+ * @return Zend_CodeGenerator_Php_File
+ */
+ public function setBody($body)
+ {
+ $this->_body = $body;
+ return $this;
+ }
+
+ /**
+ * getBody()
+ *
+ * @return string
+ */
+ public function getBody()
+ {
+ return $this->_body;
+ }
+
+ /**
+ * isSourceDirty()
+ *
+ * @return bool
+ */
+ public function isSourceDirty()
+ {
+ if (($docblock = $this->getDocblock()) && $docblock->isSourceDirty()) {
+ return true;
+ }
+
+ foreach ($this->_classes as $class) {
+ if ($class->isSourceDirty()) {
+ return true;
+ }
+ }
+
+ return parent::isSourceDirty();
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ if ($this->isSourceDirty() === false) {
+ return $this->_sourceContent;
+ }
+
+ $output = '';
+
+ // start with the body (if there), or open tag
+ if (preg_match('#(?:\s*)<\?php#', $this->getBody()) == false) {
+ $output = 'getBody();
+ if (preg_match('#/\* Zend_CodeGenerator_Php_File-(.*?)Marker:#', $body)) {
+ $output .= $body;
+ $body = '';
+ }
+
+ // Add file docblock, if any
+ if (null !== ($docblock = $this->getDocblock())) {
+ $docblock->setIndentation('');
+ $regex = preg_quote(self::$_markerDocblock, '#');
+ if (preg_match('#'.$regex.'#', $output)) {
+ $output = preg_replace('#'.$regex.'#', $docblock->generate(), $output, 1);
+ } else {
+ $output .= $docblock->generate() . self::LINE_FEED;
+ }
+ }
+
+ // newline
+ $output .= self::LINE_FEED;
+
+ // process required files
+ // @todo marker replacement for required files
+ $requiredFiles = $this->getRequiredFiles();
+ if (!empty($requiredFiles)) {
+ foreach ($requiredFiles as $requiredFile) {
+ $output .= 'require_once \'' . $requiredFile . '\';' . self::LINE_FEED;
+ }
+
+ $output .= self::LINE_FEED;
+ }
+
+ // process classes
+ $classes = $this->getClasses();
+ if (!empty($classes)) {
+ foreach ($classes as $class) {
+ $regex = str_replace('?', $class->getName(), self::$_markerClass);
+ $regex = preg_quote($regex, '#');
+ if (preg_match('#'.$regex.'#', $output)) {
+ $output = preg_replace('#'.$regex.'#', $class->generate(), $output, 1);
+ } else {
+ $output .= $class->generate() . self::LINE_FEED;
+ }
+ }
+
+ }
+
+ if (!empty($body)) {
+
+ // add an extra space betwee clsses and
+ if (!empty($classes)) {
+ $output .= self::LINE_FEED;
+ }
+
+ $output .= $body;
+ }
+
+ return $output;
+ }
+
+ public function write()
+ {
+ if ($this->_filename == '' || !is_writable(dirname($this->_filename))) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('This code generator object is not writable.');
+ }
+ file_put_contents($this->_filename, $this->generate());
+ return $this;
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Member/Abstract.php b/library/Zend/CodeGenerator/Php/Member/Abstract.php
new file mode 100644
index 0000000..6d96d48
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Member/Abstract.php
@@ -0,0 +1,222 @@
+ $docblock);
+ }
+
+ if (is_array($docblock)) {
+ $docblock = new Zend_CodeGenerator_Php_Docblock($docblock);
+ } elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock');
+ }
+
+ $this->_docblock = $docblock;
+ return $this;
+ }
+
+ /**
+ * getDocblock()
+ *
+ * @return Zend_CodeGenerator_Php_Docblock
+ */
+ public function getDocblock()
+ {
+ return $this->_docblock;
+ }
+
+ /**
+ * setAbstract()
+ *
+ * @param bool $isAbstract
+ * @return Zend_CodeGenerator_Php_Member_Abstract
+ */
+ public function setAbstract($isAbstract)
+ {
+ $this->_isAbstract = ($isAbstract) ? true : false;
+ return $this;
+ }
+
+ /**
+ * isAbstract()
+ *
+ * @return bool
+ */
+ public function isAbstract()
+ {
+ return $this->_isAbstract;
+ }
+
+ /**
+ * setFinal()
+ *
+ * @param bool $isFinal
+ * @return Zend_CodeGenerator_Php_Member_Abstract
+ */
+ public function setFinal($isFinal)
+ {
+ $this->_isFinal = ($isFinal) ? true : false;
+ return $this;
+ }
+
+ /**
+ * isFinal()
+ *
+ * @return bool
+ */
+ public function isFinal()
+ {
+ return $this->_isFinal;
+ }
+
+ /**
+ * setStatic()
+ *
+ * @param bool $isStatic
+ * @return Zend_CodeGenerator_Php_Member_Abstract
+ */
+ public function setStatic($isStatic)
+ {
+ $this->_isStatic = ($isStatic) ? true : false;
+ return $this;
+ }
+
+ /**
+ * isStatic()
+ *
+ * @return bool
+ */
+ public function isStatic()
+ {
+ return $this->_isStatic;
+ }
+
+ /**
+ * setVisitibility()
+ *
+ * @param const $visibility
+ * @return Zend_CodeGenerator_Php_Member_Abstract
+ */
+ public function setVisibility($visibility)
+ {
+ $this->_visibility = $visibility;
+ return $this;
+ }
+
+ /**
+ * getVisibility()
+ *
+ * @return const
+ */
+ public function getVisibility()
+ {
+ return $this->_visibility;
+ }
+
+ /**
+ * setName()
+ *
+ * @param string $name
+ * @return Zend_CodeGenerator_Php_Member_Abstract
+ */
+ public function setName($name)
+ {
+ $this->_name = $name;
+ return $this;
+ }
+
+ /**
+ * getName()
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+}
diff --git a/library/Zend/CodeGenerator/Php/Member/Container.php b/library/Zend/CodeGenerator/Php/Member/Container.php
new file mode 100644
index 0000000..2447416
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Member/Container.php
@@ -0,0 +1,55 @@
+_type = $type;
+ parent::__construct(array(), self::ARRAY_AS_PROPS);
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/CodeGenerator/Php/Method.php b/library/Zend/CodeGenerator/Php/Method.php
new file mode 100644
index 0000000..6e38cd4
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Method.php
@@ -0,0 +1,236 @@
+setSourceContent($reflectionMethod->getContents(false));
+ $method->setSourceDirty(false);
+
+ if ($reflectionMethod->getDocComment() != '') {
+ $method->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($reflectionMethod->getDocblock()));
+ }
+
+ $method->setFinal($reflectionMethod->isFinal());
+
+ if ($reflectionMethod->isPrivate()) {
+ $method->setVisibility(self::VISIBILITY_PRIVATE);
+ } elseif ($reflectionMethod->isProtected()) {
+ $method->setVisibility(self::VISIBILITY_PROTECTED);
+ } else {
+ $method->setVisibility(self::VISIBILITY_PUBLIC);
+ }
+
+ $method->setStatic($reflectionMethod->isStatic());
+
+ $method->setName($reflectionMethod->getName());
+
+ foreach ($reflectionMethod->getParameters() as $reflectionParameter) {
+ $method->setParameter(Zend_CodeGenerator_Php_Parameter::fromReflection($reflectionParameter));
+ }
+
+ $method->setBody($reflectionMethod->getBody());
+
+ return $method;
+ }
+
+ /**
+ * setFinal()
+ *
+ * @param bool $isFinal
+ */
+ public function setFinal($isFinal)
+ {
+ $this->_isFinal = ($isFinal) ? true : false;
+ }
+
+ /**
+ * setParameters()
+ *
+ * @param array $parameters
+ * @return Zend_CodeGenerator_Php_Method
+ */
+ public function setParameters(Array $parameters)
+ {
+ foreach ($parameters as $parameter) {
+ $this->setParameter($parameter);
+ }
+ return $this;
+ }
+
+ /**
+ * setParameter()
+ *
+ * @param Zend_CodeGenerator_Php_Parameter|array $parameter
+ * @return Zend_CodeGenerator_Php_Method
+ */
+ public function setParameter($parameter)
+ {
+ if (is_array($parameter)) {
+ $parameter = new Zend_CodeGenerator_Php_Parameter($parameter);
+ $parameterName = $parameter->getName();
+ } elseif ($parameter instanceof Zend_CodeGenerator_Php_Parameter) {
+ $parameterName = $parameter->getName();
+ } else {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('setParameter() expects either an array of method options or an instance of Zend_CodeGenerator_Php_Parameter');
+ }
+
+ $this->_parameters[$parameterName] = $parameter;
+ return $this;
+ }
+
+ /**
+ * getParameters()
+ *
+ * @return array Array of Zend_CodeGenerator_Php_Parameter
+ */
+ public function getParameters()
+ {
+ return $this->_parameters;
+ }
+
+ /**
+ * setBody()
+ *
+ * @param string $body
+ * @return Zend_CodeGenerator_Php_Method
+ */
+ public function setBody($body)
+ {
+ $this->_body = $body;
+ return $this;
+ }
+
+ /**
+ * getBody()
+ *
+ * @return string
+ */
+ public function getBody()
+ {
+ return $this->_body;
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $output = '';
+
+ $indent = $this->getIndentation();
+
+ if (($docblock = $this->getDocblock()) !== null) {
+ $docblock->setIndentation($indent);
+ $output .= $docblock->generate();
+ }
+
+ $output .= $indent;
+
+ if ($this->isAbstract()) {
+ $output .= 'abstract ';
+ } else {
+ $output .= (($this->isFinal()) ? 'final ' : '');
+ }
+
+ $output .= $this->getVisibility()
+ . (($this->isStatic()) ? ' static' : '')
+ . ' function ' . $this->getName() . '(';
+
+ $parameters = $this->getParameters();
+ if (!empty($parameters)) {
+ foreach ($parameters as $parameter) {
+ $parameterOuput[] = $parameter->generate();
+ }
+
+ $output .= implode(', ', $parameterOuput);
+ }
+
+ $output .= ')' . self::LINE_FEED . $indent . '{' . self::LINE_FEED;
+
+ if ($this->_body && $this->isSourceDirty()) {
+ $output .= ' '
+ . str_replace(self::LINE_FEED, self::LINE_FEED . $indent . $indent, trim($this->_body))
+ . self::LINE_FEED;
+ } elseif ($this->_body) {
+ $output .= $this->_body . self::LINE_FEED;
+ }
+
+ $output .= $indent . '}' . self::LINE_FEED;
+
+ return $output;
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Parameter.php b/library/Zend/CodeGenerator/Php/Parameter.php
new file mode 100644
index 0000000..869d4fe
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Parameter.php
@@ -0,0 +1,250 @@
+setName($reflectionParameter->getName());
+
+ if($reflectionParameter->isArray()) {
+ $param->setType('array');
+ } else {
+ $typeClass = $reflectionParameter->getClass();
+ if($typeClass !== null) {
+ $param->setType($typeClass->getName());
+ }
+ }
+
+ $param->setPosition($reflectionParameter->getPosition());
+
+ if($reflectionParameter->isOptional()) {
+ $param->setDefaultValue($reflectionParameter->getDefaultValue());
+ }
+ $param->setPassedByReference($reflectionParameter->isPassedByReference());
+
+ return $param;
+ }
+
+ /**
+ * setType()
+ *
+ * @param string $type
+ * @return Zend_CodeGenerator_Php_Parameter
+ */
+ public function setType($type)
+ {
+ $this->_type = $type;
+ return $this;
+ }
+
+ /**
+ * getType()
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * setName()
+ *
+ * @param string $name
+ * @return Zend_CodeGenerator_Php_Parameter
+ */
+ public function setName($name)
+ {
+ $this->_name = $name;
+ return $this;
+ }
+
+ /**
+ * getName()
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * Set the default value of the parameter.
+ *
+ * Certain variables are difficult to expres
+ *
+ * @param null|bool|string|int|float|Zend_CodeGenerator_Php_Parameter_DefaultValue $defaultValue
+ * @return Zend_CodeGenerator_Php_Parameter
+ */
+ public function setDefaultValue($defaultValue)
+ {
+ if($defaultValue === null) {
+ $this->_defaultValue = new Zend_CodeGenerator_Php_Parameter_DefaultValue("null");
+ } else if(is_array($defaultValue)) {
+ $defaultValue = str_replace(array("\r", "\n"), "", var_export($defaultValue, true));
+ $this->_defaultValue = new Zend_CodeGenerator_Php_Parameter_DefaultValue($defaultValue);
+ } else if(is_bool($defaultValue)) {
+ if($defaultValue == true) {
+ $this->_defaultValue = new Zend_CodeGenerator_Php_Parameter_DefaultValue("true");
+ } else {
+ $this->_defaultValue = new Zend_CodeGenerator_Php_Parameter_DefaultValue("false");
+ }
+ } else {
+ $this->_defaultValue = $defaultValue;
+ }
+ return $this;
+ }
+
+ /**
+ * getDefaultValue()
+ *
+ * @return string
+ */
+ public function getDefaultValue()
+ {
+ return $this->_defaultValue;
+ }
+
+ /**
+ * setPosition()
+ *
+ * @param int $position
+ * @return Zend_CodeGenerator_Php_Parameter
+ */
+ public function setPosition($position)
+ {
+ $this->_position = $position;
+ return $this;
+ }
+
+ /**
+ * getPosition()
+ *
+ * @return int
+ */
+ public function getPosition()
+ {
+ return $this->_position;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getPassedByReference()
+ {
+ return $this->_passedByReference;
+ }
+
+ /**
+ * @param bool $passedByReference
+ * @return Zend_CodeGenerator_Php_Parameter
+ */
+ public function setPassedByReference($passedByReference)
+ {
+ $this->_passedByReference = $passedByReference;
+ return $this;
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $output = '';
+
+ if ($this->_type) {
+ $output .= $this->_type . ' ';
+ }
+
+ if($this->_passedByReference === true) {
+ $output .= '&';
+ }
+
+ $output .= '$' . $this->_name;
+
+ if ($this->_defaultValue !== null) {
+ $output .= ' = ';
+ if (is_string($this->_defaultValue)) {
+ $output .= '\'' . $this->_defaultValue . '\'';
+ } else if($this->_defaultValue instanceof Zend_CodeGenerator_Php_Parameter_DefaultValue) {
+ $output .= (string)$this->_defaultValue;
+ } else {
+ $output .= $this->_defaultValue;
+ }
+ }
+
+ return $output;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/CodeGenerator/Php/Parameter/DefaultValue.php b/library/Zend/CodeGenerator/Php/Parameter/DefaultValue.php
new file mode 100644
index 0000000..c678fa3
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Parameter/DefaultValue.php
@@ -0,0 +1,60 @@
+_defaultValue = $defaultValue;
+ }
+
+ public function __toString()
+ {
+ return $this->_defaultValue;
+ }
+}
\ No newline at end of file
diff --git a/library/Zend/CodeGenerator/Php/Property.php b/library/Zend/CodeGenerator/Php/Property.php
new file mode 100644
index 0000000..ae07ab1
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Property.php
@@ -0,0 +1,179 @@
+setName($reflectionProperty->getName());
+
+ $allDefaultProperties = $reflectionProperty->getDeclaringClass()->getDefaultProperties();
+
+ $property->setDefaultValue($allDefaultProperties[$reflectionProperty->getName()]);
+
+ if ($reflectionProperty->getDocComment() != '') {
+ $property->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($reflectionProperty->getDocComment()));
+ }
+
+ if ($reflectionProperty->isStatic()) {
+ $property->setStatic(true);
+ }
+
+ if ($reflectionProperty->isPrivate()) {
+ $property->setVisibility(self::VISIBILITY_PRIVATE);
+ } elseif ($reflectionProperty->isProtected()) {
+ $property->setVisibility(self::VISIBILITY_PROTECTED);
+ } else {
+ $property->setVisibility(self::VISIBILITY_PUBLIC);
+ }
+
+ $property->setSourceDirty(false);
+
+ return $property;
+ }
+
+ /**
+ * setConst()
+ *
+ * @param bool $const
+ * @return Zend_CodeGenerator_Php_Property
+ */
+ public function setConst($const)
+ {
+ $this->_isConst = $const;
+ return $this;
+ }
+
+ /**
+ * isConst()
+ *
+ * @return bool
+ */
+ public function isConst()
+ {
+ return ($this->_isConst) ? true : false;
+ }
+
+ /**
+ * setDefaultValue()
+ *
+ * @param Zend_CodeGenerator_Php_Property_DefaultValue|string|array $defaultValue
+ * @return Zend_CodeGenerator_Php_Property
+ */
+ public function setDefaultValue($defaultValue)
+ {
+ // if it looks like
+ if (is_array($defaultValue)
+ && array_key_exists('value', $defaultValue)
+ && array_key_exists('type', $defaultValue)) {
+ $defaultValue = new Zend_CodeGenerator_Php_Property_DefaultValue($defaultValue);
+ }
+
+ if (!($defaultValue instanceof Zend_CodeGenerator_Php_Property_DefaultValue)) {
+ $defaultValue = new Zend_CodeGenerator_Php_Property_DefaultValue(array('value' => $defaultValue));
+ }
+
+ $this->_defaultValue = $defaultValue;
+ return $this;
+ }
+
+ /**
+ * getDefaultValue()
+ *
+ * @return Zend_CodeGenerator_Php_Property_DefaultValue
+ */
+ public function getDefaultValue()
+ {
+ return $this->_defaultValue;
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $name = $this->getName();
+ $defaultValue = $this->getDefaultValue();
+
+ $output = '';
+
+ if (($docblock = $this->getDocblock()) !== null) {
+ $docblock->setIndentation(' ');
+ $output .= $docblock->generate();
+ }
+
+ if ($this->isConst()) {
+ if ($defaultValue != null && !$defaultValue->isValidConstantType()) {
+ require_once 'Zend/CodeGenerator/Php/Exception.php';
+ throw new Zend_CodeGenerator_Php_Exception('The property ' . $this->_name . ' is said to be '
+ . 'constant but does not have a valid constant value.');
+ }
+ $output .= $this->_indentation . 'const ' . $name . ' = '
+ . (($defaultValue !== null) ? $defaultValue->generate() : 'null;');
+ } else {
+ $output .= $this->_indentation
+ . $this->getVisibility()
+ . (($this->isStatic()) ? ' static' : '')
+ . ' $' . $name . ' = '
+ . (($defaultValue !== null) ? $defaultValue->generate() : 'null;');
+ }
+ return $output;
+ }
+
+}
diff --git a/library/Zend/CodeGenerator/Php/Property/DefaultValue.php b/library/Zend/CodeGenerator/Php/Property/DefaultValue.php
new file mode 100644
index 0000000..8044c19
--- /dev/null
+++ b/library/Zend/CodeGenerator/Php/Property/DefaultValue.php
@@ -0,0 +1,325 @@
+getConstants();
+ unset($reflect);
+ }
+ }
+
+ /**
+ * isValidConstantType()
+ *
+ * @return bool
+ */
+ public function isValidConstantType()
+ {
+ if ($this->_type == self::TYPE_AUTO) {
+ $type = $this->_getAutoDeterminedType($this->_value);
+ } else {
+ $type = $this->_type;
+ }
+
+ // valid types for constants
+ $scalarTypes = array(
+ self::TYPE_BOOLEAN,
+ self::TYPE_BOOL,
+ self::TYPE_NUMBER,
+ self::TYPE_INTEGER,
+ self::TYPE_INT,
+ self::TYPE_FLOAT,
+ self::TYPE_DOUBLE,
+ self::TYPE_STRING,
+ self::TYPE_CONSTANT,
+ self::TYPE_NULL
+ );
+
+ return in_array($type, $scalarTypes);
+ }
+
+ /**
+ * setValue()
+ *
+ * @param mixed $value
+ * @return Zend_CodeGenerator_Php_Property_DefaultValue
+ */
+ public function setValue($value)
+ {
+ $this->_value = $value;
+ return $this;
+ }
+
+ /**
+ * getValue()
+ *
+ * @return mixed
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * setType()
+ *
+ * @param string $type
+ * @return Zend_CodeGenerator_Php_Property_DefaultValue
+ */
+ public function setType($type)
+ {
+ $this->_type = $type;
+ return $this;
+ }
+
+ /**
+ * getType()
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * setArrayDepth()
+ *
+ * @param int $arrayDepth
+ * @return Zend_CodeGenerator_Php_Property_DefaultValue
+ */
+ public function setArrayDepth($arrayDepth)
+ {
+ $this->_arrayDepth = $arrayDepth;
+ return $this;
+ }
+
+ /**
+ * getArrayDepth()
+ *
+ * @return int
+ */
+ public function getArrayDepth()
+ {
+ return $this->_arrayDepth;
+ }
+
+ /**
+ * _getValidatedType()
+ *
+ * @param string $type
+ * @return string
+ */
+ protected function _getValidatedType($type)
+ {
+ if (($constName = array_search($type, self::$_constants)) !== false) {
+ return $type;
+ }
+
+ return self::TYPE_AUTO;
+ }
+
+ /**
+ * _getAutoDeterminedType()
+ *
+ * @param mixed $value
+ * @return string
+ */
+ public function _getAutoDeterminedType($value)
+ {
+ switch (gettype($value)) {
+ case 'boolean':
+ return self::TYPE_BOOLEAN;
+ case 'integer':
+ return self::TYPE_INT;
+ case 'string':
+ return self::TYPE_STRING;
+ case 'double':
+ case 'float':
+ case 'integer':
+ return self::TYPE_NUMBER;
+ case 'array':
+ return self::TYPE_ARRAY;
+ case 'NULL':
+ return self::TYPE_NULL;
+ case 'object':
+ case 'resource':
+ case 'unknown type':
+ default:
+ return self::TYPE_OTHER;
+ }
+ }
+
+ /**
+ * generate()
+ *
+ * @return string
+ */
+ public function generate()
+ {
+ $type = $this->_type;
+
+ if ($type != self::TYPE_AUTO) {
+ $type = $this->_getValidatedType($type);
+ }
+
+ $value = $this->_value;
+
+ if ($type == self::TYPE_AUTO) {
+ $type = $this->_getAutoDeterminedType($value);
+
+ if ($type == self::TYPE_ARRAY) {
+ $rii = new RecursiveIteratorIterator(
+ $it = new RecursiveArrayIterator($value),
+ RecursiveIteratorIterator::SELF_FIRST
+ );
+ foreach ($rii as $curKey => $curValue) {
+ if (!$curValue instanceof Zend_CodeGenerator_Php_Property_DefaultValue) {
+ $curValue = new self(array('value' => $curValue));
+ $rii->getSubIterator()->offsetSet($curKey, $curValue);
+ }
+ $curValue->setArrayDepth($rii->getDepth());
+ }
+ $value = $rii->getSubIterator()->getArrayCopy();
+ }
+
+ }
+
+ $output = '';
+
+ switch ($type) {
+ case self::TYPE_BOOLEAN:
+ case self::TYPE_BOOL:
+ $output .= ( $value ? 'true' : 'false' );
+ break;
+ case self::TYPE_STRING:
+ $output .= "'" . addcslashes($value, "'") . "'";
+ break;
+ case self::TYPE_NULL:
+ $output .= 'null';
+ break;
+ case self::TYPE_NUMBER:
+ case self::TYPE_INTEGER:
+ case self::TYPE_INT:
+ case self::TYPE_FLOAT:
+ case self::TYPE_DOUBLE:
+ case self::TYPE_CONSTANT:
+ $output .= $value;
+ break;
+ case self::TYPE_ARRAY:
+ $output .= 'array(';
+ $curArrayMultiblock = false;
+ if (count($value) > 1) {
+ $curArrayMultiblock = true;
+ $output .= PHP_EOL . str_repeat($this->_indentation, $this->_arrayDepth+1);
+ }
+ $outputParts = array();
+ $noKeyIndex = 0;
+ foreach ($value as $n => $v) {
+ $v->setArrayDepth($this->_arrayDepth + 1);
+ $partV = $v->generate();
+ $partV = substr($partV, 0, strlen($partV)-1);
+ if ($n === $noKeyIndex) {
+ $outputParts[] = $partV;
+ $noKeyIndex++;
+ } else {
+ $outputParts[] = (is_int($n) ? $n : "'" . addcslashes($n, "'") . "'") . ' => ' . $partV;
+ }
+
+ }
+ $output .= implode(',' . PHP_EOL . str_repeat($this->_indentation, $this->_arrayDepth+1), $outputParts);
+ if ($curArrayMultiblock == true) {
+ $output .= PHP_EOL . str_repeat($this->_indentation, $this->_arrayDepth+1);
+ }
+ $output .= ')';
+ break;
+ case self::TYPE_OTHER:
+ default:
+ require_once "Zend/CodeGenerator/Php/Exception.php";
+ throw new Zend_CodeGenerator_Php_Exception(
+ "Type '".get_class($value)."' is unknown or cannot be used as property default value."
+ );
+ }
+
+ $output .= ';';
+
+ return $output;
+ }
+}
diff --git a/library/Zend/Config.php b/library/Zend/Config.php
new file mode 100644
index 0000000..d9edfea
--- /dev/null
+++ b/library/Zend/Config.php
@@ -0,0 +1,484 @@
+_allowModifications = (boolean) $allowModifications;
+ $this->_loadedSection = null;
+ $this->_index = 0;
+ $this->_data = array();
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ $this->_data[$key] = new self($value, $this->_allowModifications);
+ } else {
+ $this->_data[$key] = $value;
+ }
+ }
+ $this->_count = count($this->_data);
+ }
+
+ /**
+ * Retrieve a value and return $default if there is no element set.
+ *
+ * @param string $name
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($name, $default = null)
+ {
+ $result = $default;
+ if (array_key_exists($name, $this->_data)) {
+ $result = $this->_data[$name];
+ }
+ return $result;
+ }
+
+ /**
+ * Magic function so that $obj->value will work.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return $this->get($name);
+ }
+
+ /**
+ * Only allow setting of a property if $allowModifications
+ * was set to true on construction. Otherwise, throw an exception.
+ *
+ * @param string $name
+ * @param mixed $value
+ * @throws Zend_Config_Exception
+ * @return void
+ */
+ public function __set($name, $value)
+ {
+ if ($this->_allowModifications) {
+ if (is_array($value)) {
+ $this->_data[$name] = new self($value, true);
+ } else {
+ $this->_data[$name] = $value;
+ }
+ $this->_count = count($this->_data);
+ } else {
+ /** @see Zend_Config_Exception */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Zend_Config is read only');
+ }
+ }
+
+ /**
+ * Deep clone of this instance to ensure that nested Zend_Configs
+ * are also cloned.
+ *
+ * @return void
+ */
+ public function __clone()
+ {
+ $array = array();
+ foreach ($this->_data as $key => $value) {
+ if ($value instanceof Zend_Config) {
+ $array[$key] = clone $value;
+ } else {
+ $array[$key] = $value;
+ }
+ }
+ $this->_data = $array;
+ }
+
+ /**
+ * Return an associative array of the stored data.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ $array = array();
+ $data = $this->_data;
+ foreach ($data as $key => $value) {
+ if ($value instanceof Zend_Config) {
+ $array[$key] = $value->toArray();
+ } else {
+ $array[$key] = $value;
+ }
+ }
+ return $array;
+ }
+
+ /**
+ * Support isset() overloading on PHP 5.1
+ *
+ * @param string $name
+ * @return boolean
+ */
+ public function __isset($name)
+ {
+ return isset($this->_data[$name]);
+ }
+
+ /**
+ * Support unset() overloading on PHP 5.1
+ *
+ * @param string $name
+ * @throws Zend_Config_Exception
+ * @return void
+ */
+ public function __unset($name)
+ {
+ if ($this->_allowModifications) {
+ unset($this->_data[$name]);
+ $this->_count = count($this->_data);
+ $this->_skipNextIteration = true;
+ } else {
+ /** @see Zend_Config_Exception */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Zend_Config is read only');
+ }
+
+ }
+
+ /**
+ * Defined by Countable interface
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->_count;
+ }
+
+ /**
+ * Defined by Iterator interface
+ *
+ * @return mixed
+ */
+ public function current()
+ {
+ $this->_skipNextIteration = false;
+ return current($this->_data);
+ }
+
+ /**
+ * Defined by Iterator interface
+ *
+ * @return mixed
+ */
+ public function key()
+ {
+ return key($this->_data);
+ }
+
+ /**
+ * Defined by Iterator interface
+ *
+ */
+ public function next()
+ {
+ if ($this->_skipNextIteration) {
+ $this->_skipNextIteration = false;
+ return;
+ }
+ next($this->_data);
+ $this->_index++;
+ }
+
+ /**
+ * Defined by Iterator interface
+ *
+ */
+ public function rewind()
+ {
+ $this->_skipNextIteration = false;
+ reset($this->_data);
+ $this->_index = 0;
+ }
+
+ /**
+ * Defined by Iterator interface
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->_index < $this->_count;
+ }
+
+ /**
+ * Returns the section name(s) loaded.
+ *
+ * @return mixed
+ */
+ public function getSectionName()
+ {
+ if(is_array($this->_loadedSection) && count($this->_loadedSection) == 1) {
+ $this->_loadedSection = $this->_loadedSection[0];
+ }
+ return $this->_loadedSection;
+ }
+
+ /**
+ * Returns true if all sections were loaded
+ *
+ * @return boolean
+ */
+ public function areAllSectionsLoaded()
+ {
+ return $this->_loadedSection === null;
+ }
+
+
+ /**
+ * Merge another Zend_Config with this one. The items
+ * in $merge will override the same named items in
+ * the current config.
+ *
+ * @param Zend_Config $merge
+ * @return Zend_Config
+ */
+ public function merge(Zend_Config $merge)
+ {
+ foreach($merge as $key => $item) {
+ if(array_key_exists($key, $this->_data)) {
+ if($item instanceof Zend_Config && $this->$key instanceof Zend_Config) {
+ $this->$key = $this->$key->merge(new Zend_Config($item->toArray(), !$this->readOnly()));
+ } else {
+ $this->$key = $item;
+ }
+ } else {
+ if($item instanceof Zend_Config) {
+ $this->$key = new Zend_Config($item->toArray(), !$this->readOnly());
+ } else {
+ $this->$key = $item;
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Prevent any more modifications being made to this instance. Useful
+ * after merge() has been used to merge multiple Zend_Config objects
+ * into one object which should then not be modified again.
+ *
+ */
+ public function setReadOnly()
+ {
+ $this->_allowModifications = false;
+ foreach ($this->_data as $key => $value) {
+ if ($value instanceof Zend_Config) {
+ $value->setReadOnly();
+ }
+ }
+ }
+
+ /**
+ * Returns if this Zend_Config object is read only or not.
+ *
+ * @return boolean
+ */
+ public function readOnly()
+ {
+ return !$this->_allowModifications;
+ }
+
+ /**
+ * Get the current extends
+ *
+ * @return array
+ */
+ public function getExtends()
+ {
+ return $this->_extends;
+ }
+
+ /**
+ * Set an extend for Zend_Config_Writer
+ *
+ * @param string $extendingSection
+ * @param string $extendedSection
+ * @return void
+ */
+ public function setExtend($extendingSection, $extendedSection = null)
+ {
+ if ($extendedSection === null && isset($this->_extends[$extendingSection])) {
+ unset($this->_extends[$extendingSection]);
+ } else if ($extendedSection !== null) {
+ $this->_extends[$extendingSection] = $extendedSection;
+ }
+ }
+
+ /**
+ * Throws an exception if $extendingSection may not extend $extendedSection,
+ * and tracks the section extension if it is valid.
+ *
+ * @param string $extendingSection
+ * @param string $extendedSection
+ * @throws Zend_Config_Exception
+ * @return void
+ */
+ protected function _assertValidExtend($extendingSection, $extendedSection)
+ {
+ // detect circular section inheritance
+ $extendedSectionCurrent = $extendedSection;
+ while (array_key_exists($extendedSectionCurrent, $this->_extends)) {
+ if ($this->_extends[$extendedSectionCurrent] == $extendingSection) {
+ /** @see Zend_Config_Exception */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Illegal circular inheritance detected');
+ }
+ $extendedSectionCurrent = $this->_extends[$extendedSectionCurrent];
+ }
+ // remember that this section extends another section
+ $this->_extends[$extendingSection] = $extendedSection;
+ }
+
+ /**
+ * Handle any errors from simplexml_load_file or parse_ini_file
+ *
+ * @param integer $errno
+ * @param string $errstr
+ * @param string $errfile
+ * @param integer $errline
+ */
+ protected function _loadFileErrorHandler($errno, $errstr, $errfile, $errline)
+ {
+ if ($this->_loadFileErrorStr === null) {
+ $this->_loadFileErrorStr = $errstr;
+ } else {
+ $this->_loadFileErrorStr .= (PHP_EOL . $errstr);
+ }
+ }
+
+ /**
+ * Merge two arrays recursively, overwriting keys of the same name
+ * in $firstArray with the value in $secondArray.
+ *
+ * @param mixed $firstArray First array
+ * @param mixed $secondArray Second array to merge into first array
+ * @return array
+ */
+ protected function _arrayMergeRecursive($firstArray, $secondArray)
+ {
+ if (is_array($firstArray) && is_array($secondArray)) {
+ foreach ($secondArray as $key => $value) {
+ if (isset($firstArray[$key])) {
+ $firstArray[$key] = $this->_arrayMergeRecursive($firstArray[$key], $value);
+ } else {
+ if($key === 0) {
+ $firstArray= array(0=>$this->_arrayMergeRecursive($firstArray, $value));
+ } else {
+ $firstArray[$key] = $value;
+ }
+ }
+ }
+ } else {
+ $firstArray = $secondArray;
+ }
+
+ return $firstArray;
+ }
+}
\ No newline at end of file
diff --git a/library/Zend/Config/Exception.php b/library/Zend/Config/Exception.php
new file mode 100644
index 0000000..1a26a2b
--- /dev/null
+++ b/library/Zend/Config/Exception.php
@@ -0,0 +1,33 @@
+hostname === "staging"
+ * $data->db->connection === "database"
+ *
+ * The $options parameter may be provided as either a boolean or an array.
+ * If provided as a boolean, this sets the $allowModifications option of
+ * Zend_Config. If provided as an array, there are three configuration
+ * directives that may be set. For example:
+ *
+ * $options = array(
+ * 'allowModifications' => false,
+ * 'nestSeparator' => ':',
+ * 'skipExtends' => false,
+ * );
+ *
+ * @param string $filename
+ * @param mixed $section
+ * @param boolean|array $options
+ * @throws Zend_Config_Exception
+ * @return void
+ */
+ public function __construct($filename, $section = null, $options = false)
+ {
+ if (empty($filename)) {
+ /**
+ * @see Zend_Config_Exception
+ */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Filename is not set');
+ }
+
+ $allowModifications = false;
+ if (is_bool($options)) {
+ $allowModifications = $options;
+ } elseif (is_array($options)) {
+ if (isset($options['allowModifications'])) {
+ $allowModifications = (bool) $options['allowModifications'];
+ }
+ if (isset($options['nestSeparator'])) {
+ $this->_nestSeparator = (string) $options['nestSeparator'];
+ }
+ if (isset($options['skipExtends'])) {
+ $this->_skipExtends = (bool) $options['skipExtends'];
+ }
+ }
+
+ $iniArray = $this->_loadIniFile($filename);
+
+ if (null === $section) {
+ // Load entire file
+ $dataArray = array();
+ foreach ($iniArray as $sectionName => $sectionData) {
+ if(!is_array($sectionData)) {
+ $dataArray = $this->_arrayMergeRecursive($dataArray, $this->_processKey(array(), $sectionName, $sectionData));
+ } else {
+ $dataArray[$sectionName] = $this->_processSection($iniArray, $sectionName);
+ }
+ }
+ parent::__construct($dataArray, $allowModifications);
+ } else {
+ // Load one or more sections
+ if (!is_array($section)) {
+ $section = array($section);
+ }
+ $dataArray = array();
+ foreach ($section as $sectionName) {
+ if (!isset($iniArray[$sectionName])) {
+ /**
+ * @see Zend_Config_Exception
+ */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Section '$sectionName' cannot be found in $filename");
+ }
+ $dataArray = $this->_arrayMergeRecursive($this->_processSection($iniArray, $sectionName), $dataArray);
+
+ }
+ parent::__construct($dataArray, $allowModifications);
+ }
+
+ $this->_loadedSection = $section;
+ }
+
+ /**
+ * Load the INI file from disk using parse_ini_file(). Use a private error
+ * handler to convert any loading errors into a Zend_Config_Exception
+ *
+ * @param string $filename
+ * @throws Zend_Config_Exception
+ * @return array
+ */
+ protected function _parseIniFile($filename)
+ {
+ set_error_handler(array($this, '_loadFileErrorHandler'));
+ $iniArray = parse_ini_file($filename, true); // Warnings and errors are suppressed
+ restore_error_handler();
+
+ // Check if there was a error while loading file
+ if ($this->_loadFileErrorStr !== null) {
+ /**
+ * @see Zend_Config_Exception
+ */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception($this->_loadFileErrorStr);
+ }
+
+ return $iniArray;
+ }
+
+ /**
+ * Load the ini file and preprocess the section separator (':' in the
+ * section name (that is used for section extension) so that the resultant
+ * array has the correct section names and the extension information is
+ * stored in a sub-key called ';extends'. We use ';extends' as this can
+ * never be a valid key name in an INI file that has been loaded using
+ * parse_ini_file().
+ *
+ * @param string $filename
+ * @throws Zend_Config_Exception
+ * @return array
+ */
+ protected function _loadIniFile($filename)
+ {
+ $loaded = $this->_parseIniFile($filename);
+ $iniArray = array();
+ foreach ($loaded as $key => $data)
+ {
+ $pieces = explode($this->_sectionSeparator, $key);
+ $thisSection = trim($pieces[0]);
+ switch (count($pieces)) {
+ case 1:
+ $iniArray[$thisSection] = $data;
+ break;
+
+ case 2:
+ $extendedSection = trim($pieces[1]);
+ $iniArray[$thisSection] = array_merge(array(';extends'=>$extendedSection), $data);
+ break;
+
+ default:
+ /**
+ * @see Zend_Config_Exception
+ */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Section '$thisSection' may not extend multiple sections in $filename");
+ }
+ }
+
+ return $iniArray;
+ }
+
+ /**
+ * Process each element in the section and handle the ";extends" inheritance
+ * key. Passes control to _processKey() to handle the nest separator
+ * sub-property syntax that may be used within the key name.
+ *
+ * @param array $iniArray
+ * @param string $section
+ * @param array $config
+ * @throws Zend_Config_Exception
+ * @return array
+ */
+ protected function _processSection($iniArray, $section, $config = array())
+ {
+ $thisSection = $iniArray[$section];
+
+ foreach ($thisSection as $key => $value) {
+ if (strtolower($key) == ';extends') {
+ if (isset($iniArray[$value])) {
+ $this->_assertValidExtend($section, $value);
+
+ if (!$this->_skipExtends) {
+ $config = $this->_processSection($iniArray, $value, $config);
+ }
+ } else {
+ /**
+ * @see Zend_Config_Exception
+ */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Parent section '$section' cannot be found");
+ }
+ } else {
+ $config = $this->_processKey($config, $key, $value);
+ }
+ }
+ return $config;
+ }
+
+ /**
+ * Assign the key's value to the property list. Handles the
+ * nest separator for sub-properties.
+ *
+ * @param array $config
+ * @param string $key
+ * @param string $value
+ * @throws Zend_Config_Exception
+ * @return array
+ */
+ protected function _processKey($config, $key, $value)
+ {
+ if (strpos($key, $this->_nestSeparator) !== false) {
+ $pieces = explode($this->_nestSeparator, $key, 2);
+ if (strlen($pieces[0]) && strlen($pieces[1])) {
+ if (!isset($config[$pieces[0]])) {
+ if ($pieces[0] === '0' && !empty($config)) {
+ // convert the current values in $config into an array
+ $config = array($pieces[0] => $config);
+ } else {
+ $config[$pieces[0]] = array();
+ }
+ } elseif (!is_array($config[$pieces[0]])) {
+ /**
+ * @see Zend_Config_Exception
+ */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Cannot create sub-key for '{$pieces[0]}' as key already exists");
+ }
+ $config[$pieces[0]] = $this->_processKey($config[$pieces[0]], $pieces[1], $value);
+ } else {
+ /**
+ * @see Zend_Config_Exception
+ */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Invalid key '$key'");
+ }
+ } else {
+ $config[$key] = $value;
+ }
+ return $config;
+ }
+}
diff --git a/library/Zend/Config/Json.php b/library/Zend/Config/Json.php
new file mode 100644
index 0000000..0575cb7
--- /dev/null
+++ b/library/Zend/Config/Json.php
@@ -0,0 +1,240 @@
+ $value) {
+ switch (strtolower($key)) {
+ case 'allow_modifications':
+ case 'allowmodifications':
+ $allowModifications = (bool) $value;
+ break;
+ case 'skip_extends':
+ case 'skipextends':
+ $this->_skipExtends = (bool) $value;
+ break;
+ case 'ignore_constants':
+ case 'ignoreconstants':
+ $this->_ignoreConstants = (bool) $value;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ set_error_handler(array($this, '_loadFileErrorHandler')); // Warnings and errors are suppressed
+ if ($json[0] != '{') {
+ $json = file_get_contents($json);
+ }
+ restore_error_handler();
+
+ // Check if there was a error while loading file
+ if ($this->_loadFileErrorStr !== null) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception($this->_loadFileErrorStr);
+ }
+
+ // Replace constants
+ if (!$this->_ignoreConstants) {
+ $json = $this->_replaceConstants($json);
+ }
+
+ // Parse/decode
+ $config = Zend_Json::decode($json);
+
+ if (null === $config) {
+ // decode failed
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Error parsing JSON data");
+ }
+
+ if ($section === null) {
+ $dataArray = array();
+ foreach ($config as $sectionName => $sectionData) {
+ $dataArray[$sectionName] = $this->_processExtends($config, $sectionName);
+ }
+
+ parent::__construct($dataArray, $allowModifications);
+ } elseif (is_array($section)) {
+ $dataArray = array();
+ foreach ($section as $sectionName) {
+ if (!isset($config[$sectionName])) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $sectionName));
+ }
+
+ $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray);
+ }
+
+ parent::__construct($dataArray, $allowModifications);
+ } else {
+ if (!isset($config[$section])) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+ }
+
+ $dataArray = $this->_processExtends($config, $section);
+ if (!is_array($dataArray)) {
+ // Section in the JSON data contains just one top level string
+ $dataArray = array($section => $dataArray);
+ }
+
+ parent::__construct($dataArray, $allowModifications);
+ }
+
+ $this->_loadedSection = $section;
+ }
+
+ /**
+ * Helper function to process each element in the section and handle
+ * the "_extends" inheritance attribute.
+ *
+ * @param array $data Data array to process
+ * @param string $section Section to process
+ * @param array $config Configuration which was parsed yet
+ * @throws Zend_Config_Exception When $section cannot be found
+ * @return array
+ */
+ protected function _processExtends(array $data, $section, array $config = array())
+ {
+ if (!isset($data[$section])) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+ }
+
+ $thisSection = $data[$section];
+
+ if (is_array($thisSection) && isset($thisSection[self::EXTENDS_NAME])) {
+ if (is_array($thisSection[self::EXTENDS_NAME])) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Invalid extends clause: must be a string; array received');
+ }
+ $this->_assertValidExtend($section, $thisSection[self::EXTENDS_NAME]);
+
+ if (!$this->_skipExtends) {
+ $config = $this->_processExtends($data, $thisSection[self::EXTENDS_NAME], $config);
+ }
+ unset($thisSection[self::EXTENDS_NAME]);
+ }
+
+ $config = $this->_arrayMergeRecursive($config, $thisSection);
+
+ return $config;
+ }
+
+ /**
+ * Replace any constants referenced in a string with their values
+ *
+ * @param string $value
+ * @return string
+ */
+ protected function _replaceConstants($value)
+ {
+ foreach ($this->_getConstants() as $constant) {
+ if (strstr($value, $constant)) {
+ $value = str_replace($constant, constant($constant), $value);
+ }
+ }
+ return $value;
+ }
+
+ /**
+ * Get (reverse) sorted list of defined constant names
+ *
+ * @return array
+ */
+ protected function _getConstants()
+ {
+ $constants = array_keys(get_defined_constants());
+ rsort($constants, SORT_STRING);
+ return $constants;
+ }
+}
diff --git a/library/Zend/Config/Writer.php b/library/Zend/Config/Writer.php
new file mode 100644
index 0000000..689ec43
--- /dev/null
+++ b/library/Zend/Config/Writer.php
@@ -0,0 +1,101 @@
+setOptions($options);
+ }
+ }
+
+ /**
+ * Set options via a Zend_Config instance
+ *
+ * @param Zend_Config $config
+ * @return Zend_Config_Writer
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ $this->_config = $config;
+
+ return $this;
+ }
+
+ /**
+ * Set options via an array
+ *
+ * @param array $options
+ * @return Zend_Config_Writer
+ */
+ public function setOptions(array $options)
+ {
+ foreach ($options as $key => $value) {
+ if (in_array(strtolower($key), $this->_skipOptions)) {
+ continue;
+ }
+
+ $method = 'set' . ucfirst($key);
+ if (method_exists($this, $method)) {
+ $this->$method($value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Write a Zend_Config object to it's target
+ *
+ * @return void
+ */
+ abstract public function write();
+}
diff --git a/library/Zend/Config/Writer/Array.php b/library/Zend/Config/Writer/Array.php
new file mode 100644
index 0000000..9a35989
--- /dev/null
+++ b/library/Zend/Config/Writer/Array.php
@@ -0,0 +1,55 @@
+_config->toArray();
+ $sectionName = $this->_config->getSectionName();
+
+ if (is_string($sectionName)) {
+ $data = array($sectionName => $data);
+ }
+
+ $arrayString = "_filename = $filename;
+
+ return $this;
+ }
+
+ /**
+ * Set wether to exclusively lock the file or not
+ *
+ * @param boolean $exclusiveLock
+ * @return Zend_Config_Writer_Array
+ */
+ public function setExclusiveLock($exclusiveLock)
+ {
+ $this->_exclusiveLock = $exclusiveLock;
+
+ return $this;
+ }
+
+ /**
+ * Write configuration to file.
+ *
+ * @param string $filename
+ * @param Zend_Config $config
+ * @param bool $exclusiveLock
+ * @return void
+ */
+ public function write($filename = null, Zend_Config $config = null, $exclusiveLock = null)
+ {
+ if ($filename !== null) {
+ $this->setFilename($filename);
+ }
+
+ if ($config !== null) {
+ $this->setConfig($config);
+ }
+
+ if ($exclusiveLock !== null) {
+ $this->setExclusiveLock($exclusiveLock);
+ }
+
+ if ($this->_filename === null) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('No filename was set');
+ }
+
+ if ($this->_config === null) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('No config was set');
+ }
+
+ $configString = $this->render();
+
+ $flags = 0;
+
+ if ($this->_exclusiveLock) {
+ $flags |= LOCK_EX;
+ }
+
+ $result = @file_put_contents($this->_filename, $configString, $flags);
+
+ if ($result === false) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Could not write to file "' . $this->_filename . '"');
+ }
+ }
+
+ /**
+ * Render a Zend_Config into a config file string.
+ *
+ * @since 1.10
+ * @todo For 2.0 this should be redone into an abstract method.
+ * @return string
+ */
+ public function render()
+ {
+ return "";
+ }
+}
\ No newline at end of file
diff --git a/library/Zend/Config/Writer/Ini.php b/library/Zend/Config/Writer/Ini.php
new file mode 100644
index 0000000..d618ccf
--- /dev/null
+++ b/library/Zend/Config/Writer/Ini.php
@@ -0,0 +1,193 @@
+_nestSeparator = $separator;
+
+ return $this;
+ }
+
+ /**
+ * Set if rendering should occour without sections or not.
+ *
+ * If set to true, the INI file is rendered without sections completely
+ * into the global namespace of the INI file.
+ *
+ * @param bool $withoutSections
+ * @return Zend_Config_Writer_Ini
+ */
+ public function setRenderWithoutSections($withoutSections=true)
+ {
+ $this->_renderWithoutSections = (bool)$withoutSections;
+ return $this;
+ }
+
+ /**
+ * Render a Zend_Config into a INI config string.
+ *
+ * @since 1.10
+ * @return string
+ */
+ public function render()
+ {
+ $iniString = '';
+ $extends = $this->_config->getExtends();
+ $sectionName = $this->_config->getSectionName();
+
+ if($this->_renderWithoutSections == true) {
+ $iniString .= $this->_addBranch($this->_config);
+ } else if (is_string($sectionName)) {
+ $iniString .= '[' . $sectionName . ']' . "\n"
+ . $this->_addBranch($this->_config)
+ . "\n";
+ } else {
+ $config = $this->_sortRootElements($this->_config);
+ foreach ($config as $sectionName => $data) {
+ if (!($data instanceof Zend_Config)) {
+ $iniString .= $sectionName
+ . ' = '
+ . $this->_prepareValue($data)
+ . "\n";
+ } else {
+ if (isset($extends[$sectionName])) {
+ $sectionName .= ' : ' . $extends[$sectionName];
+ }
+
+ $iniString .= '[' . $sectionName . ']' . "\n"
+ . $this->_addBranch($data)
+ . "\n";
+ }
+ }
+ }
+
+ return $iniString;
+ }
+
+ /**
+ * Add a branch to an INI string recursively
+ *
+ * @param Zend_Config $config
+ * @return void
+ */
+ protected function _addBranch(Zend_Config $config, $parents = array())
+ {
+ $iniString = '';
+
+ foreach ($config as $key => $value) {
+ $group = array_merge($parents, array($key));
+
+ if ($value instanceof Zend_Config) {
+ $iniString .= $this->_addBranch($value, $group);
+ } else {
+ $iniString .= implode($this->_nestSeparator, $group)
+ . ' = '
+ . $this->_prepareValue($value)
+ . "\n";
+ }
+ }
+
+ return $iniString;
+ }
+
+ /**
+ * Prepare a value for INI
+ *
+ * @param mixed $value
+ * @return string
+ */
+ protected function _prepareValue($value)
+ {
+ if (is_integer($value) || is_float($value)) {
+ return $value;
+ } elseif (is_bool($value)) {
+ return ($value ? 'true' : 'false');
+ } elseif (strpos($value, '"') === false) {
+ return '"' . $value . '"';
+ } else {
+ /** @see Zend_Config_Exception */
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Value can not contain double quotes "');
+ }
+ }
+
+ /**
+ * Root elements that are not assigned to any section needs to be
+ * on the top of config.
+ *
+ * @see http://framework.zend.com/issues/browse/ZF-6289
+ * @param Zend_Config
+ * @return Zend_Config
+ */
+ protected function _sortRootElements(Zend_Config $config)
+ {
+ $configArray = $config->toArray();
+ $sections = array();
+
+ // remove sections from config array
+ foreach ($configArray as $key => $value) {
+ if (is_array($value)) {
+ $sections[$key] = $value;
+ unset($configArray[$key]);
+ }
+ }
+
+ // readd sections to the end
+ foreach ($sections as $key => $value) {
+ $configArray[$key] = $value;
+ }
+
+ return new Zend_Config($configArray);
+ }
+}
diff --git a/library/Zend/Config/Writer/Json.php b/library/Zend/Config/Writer/Json.php
new file mode 100644
index 0000000..1bcbddc
--- /dev/null
+++ b/library/Zend/Config/Writer/Json.php
@@ -0,0 +1,106 @@
+_prettyPrint;
+ }
+
+ /**
+ * Set prettyPrint flag
+ *
+ * @param bool $prettyPrint PrettyPrint flag
+ * @return Zend_Config_Writer_Json
+ */
+ public function setPrettyPrint($flag)
+ {
+ $this->_prettyPrint = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Render a Zend_Config into a JSON config string.
+ *
+ * @since 1.10
+ * @return string
+ */
+ public function render()
+ {
+ $data = $this->_config->toArray();
+ $sectionName = $this->_config->getSectionName();
+ $extends = $this->_config->getExtends();
+
+ if (is_string($sectionName)) {
+ $data = array($sectionName => $data);
+ }
+
+ foreach ($extends as $section => $parentSection) {
+ $data[$section][Zend_Config_Json::EXTENDS_NAME] = $parentSection;
+ }
+
+ // Ensure that each "extends" section actually exists
+ foreach ($data as $section => $sectionData) {
+ if (is_array($sectionData) && isset($sectionData[Zend_Config_Json::EXTENDS_NAME])) {
+ $sectionExtends = $sectionData[Zend_Config_Json::EXTENDS_NAME];
+ if (!isset($data[$sectionExtends])) {
+ // Remove "extends" declaration if section does not exist
+ unset($data[$section][Zend_Config_Json::EXTENDS_NAME]);
+ }
+ }
+ }
+
+ $out = Zend_Json::encode($data);
+ if ($this->prettyPrint()) {
+ $out = Zend_Json::prettyPrint($out);
+ }
+ return $out;
+ }
+}
diff --git a/library/Zend/Config/Writer/Xml.php b/library/Zend/Config/Writer/Xml.php
new file mode 100644
index 0000000..be13752
--- /dev/null
+++ b/library/Zend/Config/Writer/Xml.php
@@ -0,0 +1,127 @@
+');
+ $extends = $this->_config->getExtends();
+ $sectionName = $this->_config->getSectionName();
+
+ if (is_string($sectionName)) {
+ $child = $xml->addChild($sectionName);
+
+ $this->_addBranch($this->_config, $child, $xml);
+ } else {
+ foreach ($this->_config as $sectionName => $data) {
+ if (!($data instanceof Zend_Config)) {
+ $xml->addChild($sectionName, (string) $data);
+ } else {
+ $child = $xml->addChild($sectionName);
+
+ if (isset($extends[$sectionName])) {
+ $child->addAttribute('zf:extends', $extends[$sectionName], Zend_Config_Xml::XML_NAMESPACE);
+ }
+
+ $this->_addBranch($data, $child, $xml);
+ }
+ }
+ }
+
+ $dom = dom_import_simplexml($xml)->ownerDocument;
+ $dom->formatOutput = true;
+
+ $xmlString = $dom->saveXML();
+
+ return $xmlString;
+ }
+
+ /**
+ * Add a branch to an XML object recursively
+ *
+ * @param Zend_Config $config
+ * @param SimpleXMLElement $xml
+ * @param SimpleXMLElement $parent
+ * @return void
+ */
+ protected function _addBranch(Zend_Config $config, SimpleXMLElement $xml, SimpleXMLElement $parent)
+ {
+ $branchType = null;
+
+ foreach ($config as $key => $value) {
+ if ($branchType === null) {
+ if (is_numeric($key)) {
+ $branchType = 'numeric';
+ $branchName = $xml->getName();
+ $xml = $parent;
+
+ unset($parent->{$branchName});
+ } else {
+ $branchType = 'string';
+ }
+ } else if ($branchType !== (is_numeric($key) ? 'numeric' : 'string')) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Mixing of string and numeric keys is not allowed');
+ }
+
+ if ($branchType === 'numeric') {
+ if ($value instanceof Zend_Config) {
+ $child = $parent->addChild($branchName);
+
+ $this->_addBranch($value, $child, $parent);
+ } else {
+ $parent->addChild($branchName, (string) $value);
+ }
+ } else {
+ if ($value instanceof Zend_Config) {
+ $child = $xml->addChild($key);
+
+ $this->_addBranch($value, $child, $xml);
+ } else {
+ $xml->addChild($key, (string) $value);
+ }
+ }
+ }
+ }
+}
diff --git a/library/Zend/Config/Writer/Yaml.php b/library/Zend/Config/Writer/Yaml.php
new file mode 100644
index 0000000..434d2fb
--- /dev/null
+++ b/library/Zend/Config/Writer/Yaml.php
@@ -0,0 +1,144 @@
+_yamlEncoder;
+ }
+
+ /**
+ * Set callback for decoding YAML
+ *
+ * @param callable $yamlEncoder the decoder to set
+ * @return Zend_Config_Yaml
+ */
+ public function setYamlEncoder($yamlEncoder)
+ {
+ if (!is_callable($yamlEncoder)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Invalid parameter to setYamlEncoder - must be callable');
+ }
+
+ $this->_yamlEncoder = $yamlEncoder;
+ return $this;
+ }
+
+ /**
+ * Render a Zend_Config into a YAML config string.
+ *
+ * @since 1.10
+ * @return string
+ */
+ public function render()
+ {
+ $data = $this->_config->toArray();
+ $sectionName = $this->_config->getSectionName();
+ $extends = $this->_config->getExtends();
+
+ if (is_string($sectionName)) {
+ $data = array($sectionName => $data);
+ }
+
+ foreach ($extends as $section => $parentSection) {
+ $data[$section][Zend_Config_Yaml::EXTENDS_NAME] = $parentSection;
+ }
+
+ // Ensure that each "extends" section actually exists
+ foreach ($data as $section => $sectionData) {
+ if (is_array($sectionData) && isset($sectionData[Zend_Config_Yaml::EXTENDS_NAME])) {
+ $sectionExtends = $sectionData[Zend_Config_Yaml::EXTENDS_NAME];
+ if (!isset($data[$sectionExtends])) {
+ // Remove "extends" declaration if section does not exist
+ unset($data[$section][Zend_Config_Yaml::EXTENDS_NAME]);
+ }
+ }
+ }
+
+ return call_user_func($this->getYamlEncoder(), $data);
+ }
+
+ /**
+ * Very dumb YAML encoder
+ *
+ * Until we have Zend_Yaml...
+ *
+ * @param array $data YAML data
+ * @return string
+ */
+ public static function encode($data)
+ {
+ return self::_encodeYaml(0, $data);
+ }
+
+ /**
+ * Service function for encoding YAML
+ *
+ * @param int $indent Current indent level
+ * @param array $data Data to encode
+ * @return string
+ */
+ protected static function _encodeYaml($indent, $data)
+ {
+ reset($data);
+ $result = "";
+ $numeric = is_numeric(key($data));
+
+ foreach($data as $key => $value) {
+ if(is_array($value)) {
+ $encoded = "\n".self::_encodeYaml($indent+1, $value);
+ } else {
+ $encoded = (string)$value."\n";
+ }
+ $result .= str_repeat(" ", $indent).($numeric?"- ":"$key: ").$encoded;
+ }
+ return $result;
+ }
+}
diff --git a/library/Zend/Config/Xml.php b/library/Zend/Config/Xml.php
new file mode 100644
index 0000000..4e41784
--- /dev/null
+++ b/library/Zend/Config/Xml.php
@@ -0,0 +1,296 @@
+ false,
+ * 'skipExtends' => false
+ * );
+ *
+ * @param string $xml XML file or string to process
+ * @param mixed $section Section to process
+ * @param array|boolean $options
+ * @throws Zend_Config_Exception When xml is not set or cannot be loaded
+ * @throws Zend_Config_Exception When section $sectionName cannot be found in $xml
+ */
+ public function __construct($xml, $section = null, $options = false)
+ {
+ if (empty($xml)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Filename is not set');
+ }
+
+ $allowModifications = false;
+ if (is_bool($options)) {
+ $allowModifications = $options;
+ } elseif (is_array($options)) {
+ if (isset($options['allowModifications'])) {
+ $allowModifications = (bool) $options['allowModifications'];
+ }
+ if (isset($options['skipExtends'])) {
+ $this->_skipExtends = (bool) $options['skipExtends'];
+ }
+ }
+
+ set_error_handler(array($this, '_loadFileErrorHandler')); // Warnings and errors are suppressed
+ if (strstr($xml, '_loadFileErrorStr !== null) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception($this->_loadFileErrorStr);
+ }
+
+ if ($section === null) {
+ $dataArray = array();
+ foreach ($config as $sectionName => $sectionData) {
+ $dataArray[$sectionName] = $this->_processExtends($config, $sectionName);
+ }
+
+ parent::__construct($dataArray, $allowModifications);
+ } else if (is_array($section)) {
+ $dataArray = array();
+ foreach ($section as $sectionName) {
+ if (!isset($config->$sectionName)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Section '$sectionName' cannot be found in $xml");
+ }
+
+ $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray);
+ }
+
+ parent::__construct($dataArray, $allowModifications);
+ } else {
+ if (!isset($config->$section)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Section '$section' cannot be found in $xml");
+ }
+
+ $dataArray = $this->_processExtends($config, $section);
+ if (!is_array($dataArray)) {
+ // Section in the XML file contains just one top level string
+ $dataArray = array($section => $dataArray);
+ }
+
+ parent::__construct($dataArray, $allowModifications);
+ }
+
+ $this->_loadedSection = $section;
+ }
+
+ /**
+ * Helper function to process each element in the section and handle
+ * the "extends" inheritance attribute.
+ *
+ * @param SimpleXMLElement $element XML Element to process
+ * @param string $section Section to process
+ * @param array $config Configuration which was parsed yet
+ * @throws Zend_Config_Exception When $section cannot be found
+ * @return array
+ */
+ protected function _processExtends(SimpleXMLElement $element, $section, array $config = array())
+ {
+ if (!isset($element->$section)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Section '$section' cannot be found");
+ }
+
+ $thisSection = $element->$section;
+ $nsAttributes = $thisSection->attributes(self::XML_NAMESPACE);
+
+ if (isset($thisSection['extends']) || isset($nsAttributes['extends'])) {
+ $extendedSection = (string) (isset($nsAttributes['extends']) ? $nsAttributes['extends'] : $thisSection['extends']);
+ $this->_assertValidExtend($section, $extendedSection);
+
+ if (!$this->_skipExtends) {
+ $config = $this->_processExtends($element, $extendedSection, $config);
+ }
+ }
+
+ $config = $this->_arrayMergeRecursive($config, $this->_toArray($thisSection));
+
+ return $config;
+ }
+
+ /**
+ * Returns a string or an associative and possibly multidimensional array from
+ * a SimpleXMLElement.
+ *
+ * @param SimpleXMLElement $xmlObject Convert a SimpleXMLElement into an array
+ * @return array|string
+ */
+ protected function _toArray(SimpleXMLElement $xmlObject)
+ {
+ $config = array();
+ $nsAttributes = $xmlObject->attributes(self::XML_NAMESPACE);
+
+ // Search for parent node values
+ if (count($xmlObject->attributes()) > 0) {
+ foreach ($xmlObject->attributes() as $key => $value) {
+ if ($key === 'extends') {
+ continue;
+ }
+
+ $value = (string) $value;
+
+ if (array_key_exists($key, $config)) {
+ if (!is_array($config[$key])) {
+ $config[$key] = array($config[$key]);
+ }
+
+ $config[$key][] = $value;
+ } else {
+ $config[$key] = $value;
+ }
+ }
+ }
+
+ // Search for local 'const' nodes and replace them
+ if (count($xmlObject->children(self::XML_NAMESPACE)) > 0) {
+ if (count($xmlObject->children()) > 0) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("A node with a 'const' childnode may not have any other children");
+ }
+
+ $dom = dom_import_simplexml($xmlObject);
+ $namespaceChildNodes = array();
+
+ // We have to store them in an array, as replacing nodes will
+ // confuse the DOMNodeList later
+ foreach ($dom->childNodes as $node) {
+ if ($node instanceof DOMElement && $node->namespaceURI === self::XML_NAMESPACE) {
+ $namespaceChildNodes[] = $node;
+ }
+ }
+
+ foreach ($namespaceChildNodes as $node) {
+ switch ($node->localName) {
+ case 'const':
+ if (!$node->hasAttributeNS(self::XML_NAMESPACE, 'name')) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Misssing 'name' attribute in 'const' node");
+ }
+
+ $constantName = $node->getAttributeNS(self::XML_NAMESPACE, 'name');
+
+ if (!defined($constantName)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Constant with name '$constantName' was not defined");
+ }
+
+ $constantValue = constant($constantName);
+
+ $dom->replaceChild($dom->ownerDocument->createTextNode($constantValue), $node);
+ break;
+
+ default:
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Unknown node with name '$node->localName' found");
+ }
+ }
+
+ return (string) simplexml_import_dom($dom);
+ }
+
+ // Search for children
+ if (count($xmlObject->children()) > 0) {
+ foreach ($xmlObject->children() as $key => $value) {
+ if (count($value->children()) > 0 || count($value->children(self::XML_NAMESPACE)) > 0) {
+ $value = $this->_toArray($value);
+ } else if (count($value->attributes()) > 0) {
+ $attributes = $value->attributes();
+ if (isset($attributes['value'])) {
+ $value = (string) $attributes['value'];
+ } else {
+ $value = $this->_toArray($value);
+ }
+ } else {
+ $value = (string) $value;
+ }
+
+ if (array_key_exists($key, $config)) {
+ if (!is_array($config[$key]) || !array_key_exists(0, $config[$key])) {
+ $config[$key] = array($config[$key]);
+ }
+
+ $config[$key][] = $value;
+ } else {
+ $config[$key] = $value;
+ }
+ }
+ } else if (!isset($xmlObject['extends']) && !isset($nsAttributes['extends']) && (count($config) === 0)) {
+ // Object has no children nor attributes and doesn't use the extends
+ // attribute: it's a string
+ $config = (string) $xmlObject;
+ }
+
+ return $config;
+ }
+}
diff --git a/library/Zend/Config/Yaml.php b/library/Zend/Config/Yaml.php
new file mode 100644
index 0000000..5f5ba84
--- /dev/null
+++ b/library/Zend/Config/Yaml.php
@@ -0,0 +1,381 @@
+_yamlDecoder;
+ }
+
+ /**
+ * Set callback for decoding YAML
+ *
+ * @param callable $yamlDecoder the decoder to set
+ * @return Zend_Config_Yaml
+ */
+ public function setYamlDecoder($yamlDecoder)
+ {
+ if (!is_callable($yamlDecoder)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Invalid parameter to setYamlDecoder() - must be callable');
+ }
+
+ $this->_yamlDecoder = $yamlDecoder;
+ return $this;
+ }
+
+ /**
+ * Loads the section $section from the config file encoded as YAML
+ *
+ * Sections are defined as properties of the main object
+ *
+ * In order to extend another section, a section defines the "_extends"
+ * property having a value of the section name from which the extending
+ * section inherits values.
+ *
+ * Note that the keys in $section will override any keys of the same
+ * name in the sections that have been included via "_extends".
+ *
+ * Options may include:
+ * - allow_modifications: whether or not the config object is mutable
+ * - skip_extends: whether or not to skip processing of parent configuration
+ * - yaml_decoder: a callback to use to decode the Yaml source
+ *
+ * @param string $yaml YAML file to process
+ * @param mixed $section Section to process
+ * @param array|boolean $options
+ */
+ public function __construct($yaml, $section = null, $options = false)
+ {
+ if (empty($yaml)) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception('Filename is not set');
+ }
+
+ $ignoreConstants = $staticIgnoreConstants = self::ignoreConstants();
+ $allowModifications = false;
+ if (is_bool($options)) {
+ $allowModifications = $options;
+ } elseif (is_array($options)) {
+ foreach ($options as $key => $value) {
+ switch (strtolower($key)) {
+ case 'allow_modifications':
+ case 'allowmodifications':
+ $allowModifications = (bool) $value;
+ break;
+ case 'skip_extends':
+ case 'skipextends':
+ $this->_skipExtends = (bool) $value;
+ break;
+ case 'ignore_constants':
+ case 'ignoreconstants':
+ $ignoreConstants = (bool) $value;
+ break;
+ case 'yaml_decoder':
+ case 'yamldecoder':
+ $this->setYamlDecoder($value);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Suppress warnings and errors while loading file
+ set_error_handler(array($this, '_loadFileErrorHandler'));
+ $yaml = file_get_contents($yaml);
+ restore_error_handler();
+
+ // Check if there was a error while loading file
+ if ($this->_loadFileErrorStr !== null) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception($this->_loadFileErrorStr);
+ }
+
+ // Override static value for ignore_constants if provided in $options
+ self::setIgnoreConstants($ignoreConstants);
+
+ // Parse YAML
+ $config = call_user_func($this->getYamlDecoder(), $yaml);
+
+ // Reset original static state of ignore_constants
+ self::setIgnoreConstants($staticIgnoreConstants);
+
+ if (null === $config) {
+ // decode failed
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception("Error parsing YAML data");
+ }
+
+ if (null === $section) {
+ $dataArray = array();
+ foreach ($config as $sectionName => $sectionData) {
+ $dataArray[$sectionName] = $this->_processExtends($config, $sectionName);
+ }
+ parent::__construct($dataArray, $allowModifications);
+ } elseif (is_array($section)) {
+ $dataArray = array();
+ foreach ($section as $sectionName) {
+ if (!isset($config[$sectionName])) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+ }
+
+ $dataArray = array_merge($this->_processExtends($config, $sectionName), $dataArray);
+ }
+ parent::__construct($dataArray, $allowModifications);
+ } else {
+ if (!isset($config[$section])) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+ }
+
+ $dataArray = $this->_processExtends($config, $section);
+ if (!is_array($dataArray)) {
+ // Section in the yaml data contains just one top level string
+ $dataArray = array($section => $dataArray);
+ }
+ parent::__construct($dataArray, $allowModifications);
+ }
+
+ $this->_loadedSection = $section;
+ }
+
+ /**
+ * Helper function to process each element in the section and handle
+ * the "_extends" inheritance attribute.
+ *
+ * @param array $data Data array to process
+ * @param string $section Section to process
+ * @param array $config Configuration which was parsed yet
+ * @return array
+ * @throws Zend_Config_Exception When $section cannot be found
+ */
+ protected function _processExtends(array $data, $section, array $config = array())
+ {
+ if (!isset($data[$section])) {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception(sprintf('Section "%s" cannot be found', $section));
+ }
+
+ $thisSection = $data[$section];
+
+ if (is_array($thisSection) && isset($thisSection[self::EXTENDS_NAME])) {
+ $this->_assertValidExtend($section, $thisSection[self::EXTENDS_NAME]);
+
+ if (!$this->_skipExtends) {
+ $config = $this->_processExtends($data, $thisSection[self::EXTENDS_NAME], $config);
+ }
+ unset($thisSection[self::EXTENDS_NAME]);
+ }
+
+ $config = $this->_arrayMergeRecursive($config, $thisSection);
+
+ return $config;
+ }
+
+ /**
+ * Very dumb YAML parser
+ *
+ * Until we have Zend_Yaml...
+ *
+ * @param string $yaml YAML source
+ * @return array Decoded data
+ */
+ public static function decode($yaml)
+ {
+ $lines = explode("\n", $yaml);
+ reset($lines);
+ return self::_decodeYaml(0, $lines);
+ }
+
+ /**
+ * Service function to decode YAML
+ *
+ * @param int $currentIndent Current indent level
+ * @param array $lines YAML lines
+ * @return array|string
+ */
+ protected static function _decodeYaml($currentIndent, &$lines)
+ {
+ $config = array();
+ $inIndent = false;
+ while (list($n, $line) = each($lines)) {
+ $lineno = $n + 1;
+
+ $line = rtrim(preg_replace("/#.*$/", "", $line));
+ if (strlen($line) == 0) {
+ continue;
+ }
+
+ $indent = strspn($line, " ");
+
+ // line without the spaces
+ $line = trim($line);
+ if (strlen($line) == 0) {
+ continue;
+ }
+
+ if ($indent < $currentIndent) {
+ // this level is done
+ prev($lines);
+ return $config;
+ }
+
+ if (!$inIndent) {
+ $currentIndent = $indent;
+ $inIndent = true;
+ }
+
+ if (preg_match("/(\w+):\s*(.*)/", $line, $m)) {
+ // key: value
+ if (strlen($m[2])) {
+ // simple key: value
+ $value = rtrim(preg_replace("/#.*$/", "", $m[2]));
+ // Check for booleans and constants
+ if (preg_match('/^(t(rue)?|on|y(es)?)$/i', $value)) {
+ $value = true;
+ } elseif (preg_match('/^(f(alse)?|off|n(o)?)$/i', $value)) {
+ $value = false;
+ } elseif (!self::$_ignoreConstants) {
+ // test for constants
+ $value = self::_replaceConstants($value);
+ }
+ } else {
+ // key: and then values on new lines
+ $value = self::_decodeYaml($currentIndent + 1, $lines);
+ if (is_array($value) && !count($value)) {
+ $value = "";
+ }
+ }
+ $config[$m[1]] = $value;
+ } elseif ($line[0] == "-") {
+ // item in the list:
+ // - FOO
+ if (strlen($line) > 2) {
+ $config[] = substr($line, 2);
+ } else {
+ $config[] = self::_decodeYaml($currentIndent + 1, $lines);
+ }
+ } else {
+ require_once 'Zend/Config/Exception.php';
+ throw new Zend_Config_Exception(sprintf(
+ 'Error parsing YAML at line %d - unsupported syntax: "%s"',
+ $lineno, $line
+ ));
+ }
+ }
+ return $config;
+ }
+
+ /**
+ * Replace any constants referenced in a string with their values
+ *
+ * @param string $value
+ * @return string
+ */
+ protected static function _replaceConstants($value)
+ {
+ foreach (self::_getConstants() as $constant) {
+ if (strstr($value, $constant)) {
+ $value = str_replace($constant, constant($constant), $value);
+ }
+ }
+ return $value;
+ }
+
+ /**
+ * Get (reverse) sorted list of defined constant names
+ *
+ * @return array
+ */
+ protected static function _getConstants()
+ {
+ $constants = array_keys(get_defined_constants());
+ rsort($constants, SORT_STRING);
+ return $constants;
+ }
+}
diff --git a/library/Zend/Console/Getopt.php b/library/Zend/Console/Getopt.php
new file mode 100644
index 0000000..d603566
--- /dev/null
+++ b/library/Zend/Console/Getopt.php
@@ -0,0 +1,970 @@
+ self::MODE_ZEND,
+ self::CONFIG_DASHDASH => true,
+ self::CONFIG_IGNORECASE => false,
+ self::CONFIG_PARSEALL => true,
+ );
+
+ /**
+ * Stores the command-line arguments for the calling applicaion.
+ *
+ * @var array
+ */
+ protected $_argv = array();
+
+ /**
+ * Stores the name of the calling applicaion.
+ *
+ * @var string
+ */
+ protected $_progname = '';
+
+ /**
+ * Stores the list of legal options for this application.
+ *
+ * @var array
+ */
+ protected $_rules = array();
+
+ /**
+ * Stores alternate spellings of legal options.
+ *
+ * @var array
+ */
+ protected $_ruleMap = array();
+
+ /**
+ * Stores options given by the user in the current invocation
+ * of the application, as well as parameters given in options.
+ *
+ * @var array
+ */
+ protected $_options = array();
+
+ /**
+ * Stores the command-line arguments other than options.
+ *
+ * @var array
+ */
+ protected $_remainingArgs = array();
+
+ /**
+ * State of the options: parsed or not yet parsed?
+ *
+ * @var boolean
+ */
+ protected $_parsed = false;
+
+ /**
+ * The constructor takes one to three parameters.
+ *
+ * The first parameter is $rules, which may be a string for
+ * gnu-style format, or a structured array for Zend-style format.
+ *
+ * The second parameter is $argv, and it is optional. If not
+ * specified, $argv is inferred from the global argv.
+ *
+ * The third parameter is an array of configuration parameters
+ * to control the behavior of this instance of Getopt; it is optional.
+ *
+ * @param array $rules
+ * @param array $argv
+ * @param array $getoptConfig
+ * @return void
+ */
+ public function __construct($rules, $argv = null, $getoptConfig = array())
+ {
+ if (!isset($_SERVER['argv'])) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ if (ini_get('register_argc_argv') == false) {
+ throw new Zend_Console_Getopt_Exception(
+ "argv is not available, because ini option 'register_argc_argv' is set Off"
+ );
+ } else {
+ throw new Zend_Console_Getopt_Exception(
+ '$_SERVER["argv"] is not set, but Zend_Console_Getopt cannot work without this information.'
+ );
+ }
+ }
+
+ $this->_progname = $_SERVER['argv'][0];
+ $this->setOptions($getoptConfig);
+ $this->addRules($rules);
+ if (!is_array($argv)) {
+ $argv = array_slice($_SERVER['argv'], 1);
+ }
+ if (isset($argv)) {
+ $this->addArguments((array)$argv);
+ }
+ }
+
+ /**
+ * Return the state of the option seen on the command line of the
+ * current application invocation. This function returns true, or the
+ * parameter to the option, if any. If the option was not given,
+ * this function returns null.
+ *
+ * The magic __get method works in the context of naming the option
+ * as a virtual member of this class.
+ *
+ * @param string $key
+ * @return string
+ */
+ public function __get($key)
+ {
+ return $this->getOption($key);
+ }
+
+ /**
+ * Test whether a given option has been seen.
+ *
+ * @param string $key
+ * @return boolean
+ */
+ public function __isset($key)
+ {
+ $this->parse();
+ if (isset($this->_ruleMap[$key])) {
+ $key = $this->_ruleMap[$key];
+ return isset($this->_options[$key]);
+ }
+ return false;
+ }
+
+ /**
+ * Set the value for a given option.
+ *
+ * @param string $key
+ * @param string $value
+ * @return void
+ */
+ public function __set($key, $value)
+ {
+ $this->parse();
+ if (isset($this->_ruleMap[$key])) {
+ $key = $this->_ruleMap[$key];
+ $this->_options[$key] = $value;
+ }
+ }
+
+ /**
+ * Return the current set of options and parameters seen as a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * Unset an option.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function __unset($key)
+ {
+ $this->parse();
+ if (isset($this->_ruleMap[$key])) {
+ $key = $this->_ruleMap[$key];
+ unset($this->_options[$key]);
+ }
+ }
+
+ /**
+ * Define additional command-line arguments.
+ * These are appended to those defined when the constructor was called.
+ *
+ * @param array $argv
+ * @throws Zend_Console_Getopt_Exception When not given an array as parameter
+ * @return Zend_Console_Getopt Provides a fluent interface
+ */
+ public function addArguments($argv)
+ {
+ if(!is_array($argv)) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Parameter #1 to addArguments should be an array");
+ }
+ $this->_argv = array_merge($this->_argv, $argv);
+ $this->_parsed = false;
+ return $this;
+ }
+
+ /**
+ * Define full set of command-line arguments.
+ * These replace any currently defined.
+ *
+ * @param array $argv
+ * @throws Zend_Console_Getopt_Exception When not given an array as parameter
+ * @return Zend_Console_Getopt Provides a fluent interface
+ */
+ public function setArguments($argv)
+ {
+ if(!is_array($argv)) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Parameter #1 to setArguments should be an array");
+ }
+ $this->_argv = $argv;
+ $this->_parsed = false;
+ return $this;
+ }
+
+ /**
+ * Define multiple configuration options from an associative array.
+ * These are not program options, but properties to configure
+ * the behavior of Zend_Console_Getopt.
+ *
+ * @param array $getoptConfig
+ * @return Zend_Console_Getopt Provides a fluent interface
+ */
+ public function setOptions($getoptConfig)
+ {
+ if (isset($getoptConfig)) {
+ foreach ($getoptConfig as $key => $value) {
+ $this->setOption($key, $value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Define one configuration option as a key/value pair.
+ * These are not program options, but properties to configure
+ * the behavior of Zend_Console_Getopt.
+ *
+ * @param string $configKey
+ * @param string $configValue
+ * @return Zend_Console_Getopt Provides a fluent interface
+ */
+ public function setOption($configKey, $configValue)
+ {
+ if ($configKey !== null) {
+ $this->_getoptConfig[$configKey] = $configValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Define additional option rules.
+ * These are appended to the rules defined when the constructor was called.
+ *
+ * @param array $rules
+ * @return Zend_Console_Getopt Provides a fluent interface
+ */
+ public function addRules($rules)
+ {
+ $ruleMode = $this->_getoptConfig['ruleMode'];
+ switch ($this->_getoptConfig['ruleMode']) {
+ case self::MODE_ZEND:
+ if (is_array($rules)) {
+ $this->_addRulesModeZend($rules);
+ break;
+ }
+ // intentional fallthrough
+ case self::MODE_GNU:
+ $this->_addRulesModeGnu($rules);
+ break;
+ default:
+ /**
+ * Call addRulesModeFoo() for ruleMode 'foo'.
+ * The developer should subclass Getopt and
+ * provide this method.
+ */
+ $method = '_addRulesMode' . ucfirst($ruleMode);
+ $this->$method($rules);
+ }
+ $this->_parsed = false;
+ return $this;
+ }
+
+ /**
+ * Return the current set of options and parameters seen as a string.
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ $this->parse();
+ $s = array();
+ foreach ($this->_options as $flag => $value) {
+ $s[] = $flag . '=' . ($value === true ? 'true' : $value);
+ }
+ return implode(' ', $s);
+ }
+
+ /**
+ * Return the current set of options and parameters seen
+ * as an array of canonical options and parameters.
+ *
+ * Clusters have been expanded, and option aliases
+ * have been mapped to their primary option names.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ $this->parse();
+ $s = array();
+ foreach ($this->_options as $flag => $value) {
+ $s[] = $flag;
+ if ($value !== true) {
+ $s[] = $value;
+ }
+ }
+ return $s;
+ }
+
+ /**
+ * Return the current set of options and parameters seen in Json format.
+ *
+ * @return string
+ */
+ public function toJson()
+ {
+ $this->parse();
+ $j = array();
+ foreach ($this->_options as $flag => $value) {
+ $j['options'][] = array(
+ 'option' => array(
+ 'flag' => $flag,
+ 'parameter' => $value
+ )
+ );
+ }
+
+ /**
+ * @see Zend_Json
+ */
+ require_once 'Zend/Json.php';
+ $json = Zend_Json::encode($j);
+
+ return $json;
+ }
+
+ /**
+ * Return the current set of options and parameters seen in XML format.
+ *
+ * @return string
+ */
+ public function toXml()
+ {
+ $this->parse();
+ $doc = new DomDocument('1.0', 'utf-8');
+ $optionsNode = $doc->createElement('options');
+ $doc->appendChild($optionsNode);
+ foreach ($this->_options as $flag => $value) {
+ $optionNode = $doc->createElement('option');
+ $optionNode->setAttribute('flag', utf8_encode($flag));
+ if ($value !== true) {
+ $optionNode->setAttribute('parameter', utf8_encode($value));
+ }
+ $optionsNode->appendChild($optionNode);
+ }
+ $xml = $doc->saveXML();
+ return $xml;
+ }
+
+ /**
+ * Return a list of options that have been seen in the current argv.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ $this->parse();
+ return array_keys($this->_options);
+ }
+
+ /**
+ * Return the state of the option seen on the command line of the
+ * current application invocation.
+ *
+ * This function returns true, or the parameter value to the option, if any.
+ * If the option was not given, this function returns false.
+ *
+ * @param string $flag
+ * @return mixed
+ */
+ public function getOption($flag)
+ {
+ $this->parse();
+ if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ }
+ if (isset($this->_ruleMap[$flag])) {
+ $flag = $this->_ruleMap[$flag];
+ if (isset($this->_options[$flag])) {
+ return $this->_options[$flag];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the arguments from the command-line following all options found.
+ *
+ * @return array
+ */
+ public function getRemainingArgs()
+ {
+ $this->parse();
+ return $this->_remainingArgs;
+ }
+
+ /**
+ * Return a useful option reference, formatted for display in an
+ * error message.
+ *
+ * Note that this usage information is provided in most Exceptions
+ * generated by this class.
+ *
+ * @return string
+ */
+ public function getUsageMessage()
+ {
+ $usage = "Usage: {$this->_progname} [ options ]\n";
+ $maxLen = 20;
+ $lines = array();
+ foreach ($this->_rules as $rule) {
+ $flags = array();
+ if (is_array($rule['alias'])) {
+ foreach ($rule['alias'] as $flag) {
+ $flags[] = (strlen($flag) == 1 ? '-' : '--') . $flag;
+ }
+ }
+ $linepart['name'] = implode('|', $flags);
+ if (isset($rule['param']) && $rule['param'] != 'none') {
+ $linepart['name'] .= ' ';
+ switch ($rule['param']) {
+ case 'optional':
+ $linepart['name'] .= "[ <{$rule['paramType']}> ]";
+ break;
+ case 'required':
+ $linepart['name'] .= "<{$rule['paramType']}>";
+ break;
+ }
+ }
+ if (strlen($linepart['name']) > $maxLen) {
+ $maxLen = strlen($linepart['name']);
+ }
+ $linepart['help'] = '';
+ if (isset($rule['help'])) {
+ $linepart['help'] .= $rule['help'];
+ }
+ $lines[] = $linepart;
+ }
+ foreach ($lines as $linepart) {
+ $usage .= sprintf("%s %s\n",
+ str_pad($linepart['name'], $maxLen),
+ $linepart['help']);
+ }
+ return $usage;
+ }
+
+ /**
+ * Define aliases for options.
+ *
+ * The parameter $aliasMap is an associative array
+ * mapping option name (short or long) to an alias.
+ *
+ * @param array $aliasMap
+ * @throws Zend_Console_Getopt_Exception
+ * @return Zend_Console_Getopt Provides a fluent interface
+ */
+ public function setAliases($aliasMap)
+ {
+ foreach ($aliasMap as $flag => $alias)
+ {
+ if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ $alias = strtolower($alias);
+ }
+ if (!isset($this->_ruleMap[$flag])) {
+ continue;
+ }
+ $flag = $this->_ruleMap[$flag];
+ if (isset($this->_rules[$alias]) || isset($this->_ruleMap[$alias])) {
+ $o = (strlen($alias) == 1 ? '-' : '--') . $alias;
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Option \"$o\" is being defined more than once.");
+ }
+ $this->_rules[$flag]['alias'][] = $alias;
+ $this->_ruleMap[$alias] = $flag;
+ }
+ return $this;
+ }
+
+ /**
+ * Define help messages for options.
+ *
+ * The parameter $help_map is an associative array
+ * mapping option name (short or long) to the help string.
+ *
+ * @param array $helpMap
+ * @return Zend_Console_Getopt Provides a fluent interface
+ */
+ public function setHelp($helpMap)
+ {
+ foreach ($helpMap as $flag => $help)
+ {
+ if (!isset($this->_ruleMap[$flag])) {
+ continue;
+ }
+ $flag = $this->_ruleMap[$flag];
+ $this->_rules[$flag]['help'] = $help;
+ }
+ return $this;
+ }
+
+ /**
+ * Parse command-line arguments and find both long and short
+ * options.
+ *
+ * Also find option parameters, and remaining arguments after
+ * all options have been parsed.
+ *
+ * @return Zend_Console_Getopt|null Provides a fluent interface
+ */
+ public function parse()
+ {
+ if ($this->_parsed === true) {
+ return;
+ }
+ $argv = $this->_argv;
+ $this->_options = array();
+ $this->_remainingArgs = array();
+ while (count($argv) > 0) {
+ if ($argv[0] == '--') {
+ array_shift($argv);
+ if ($this->_getoptConfig[self::CONFIG_DASHDASH]) {
+ $this->_remainingArgs = array_merge($this->_remainingArgs, $argv);
+ break;
+ }
+ }
+ if (substr($argv[0], 0, 2) == '--') {
+ $this->_parseLongOption($argv);
+ } else if (substr($argv[0], 0, 1) == '-' && ('-' != $argv[0] || count($argv) >1)) {
+ $this->_parseShortOptionCluster($argv);
+ } else if($this->_getoptConfig[self::CONFIG_PARSEALL]) {
+ $this->_remainingArgs[] = array_shift($argv);
+ } else {
+ /*
+ * We should put all other arguments in _remainingArgs and stop parsing
+ * since CONFIG_PARSEALL is false.
+ */
+ $this->_remainingArgs = array_merge($this->_remainingArgs, $argv);
+ break;
+ }
+ }
+ $this->_parsed = true;
+ return $this;
+ }
+
+ /**
+ * Parse command-line arguments for a single long option.
+ * A long option is preceded by a double '--' character.
+ * Long options may not be clustered.
+ *
+ * @param mixed &$argv
+ * @return void
+ */
+ protected function _parseLongOption(&$argv)
+ {
+ $optionWithParam = ltrim(array_shift($argv), '-');
+ $l = explode('=', $optionWithParam, 2);
+ $flag = array_shift($l);
+ $param = array_shift($l);
+ if (isset($param)) {
+ array_unshift($argv, $param);
+ }
+ $this->_parseSingleOption($flag, $argv);
+ }
+
+ /**
+ * Parse command-line arguments for short options.
+ * Short options are those preceded by a single '-' character.
+ * Short options may be clustered.
+ *
+ * @param mixed &$argv
+ * @return void
+ */
+ protected function _parseShortOptionCluster(&$argv)
+ {
+ $flagCluster = ltrim(array_shift($argv), '-');
+ foreach (str_split($flagCluster) as $flag) {
+ $this->_parseSingleOption($flag, $argv);
+ }
+ }
+
+ /**
+ * Parse command-line arguments for a single option.
+ *
+ * @param string $flag
+ * @param mixed $argv
+ * @throws Zend_Console_Getopt_Exception
+ * @return void
+ */
+ protected function _parseSingleOption($flag, &$argv)
+ {
+ if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ }
+ if (!isset($this->_ruleMap[$flag])) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Option \"$flag\" is not recognized.",
+ $this->getUsageMessage());
+ }
+ $realFlag = $this->_ruleMap[$flag];
+ switch ($this->_rules[$realFlag]['param']) {
+ case 'required':
+ if (count($argv) > 0) {
+ $param = array_shift($argv);
+ $this->_checkParameterType($realFlag, $param);
+ } else {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Option \"$flag\" requires a parameter.",
+ $this->getUsageMessage());
+ }
+ break;
+ case 'optional':
+ if (count($argv) > 0 && substr($argv[0], 0, 1) != '-') {
+ $param = array_shift($argv);
+ $this->_checkParameterType($realFlag, $param);
+ } else {
+ $param = true;
+ }
+ break;
+ default:
+ $param = true;
+ }
+ $this->_options[$realFlag] = $param;
+ }
+
+ /**
+ * Return true if the parameter is in a valid format for
+ * the option $flag.
+ * Throw an exception in most other cases.
+ *
+ * @param string $flag
+ * @param string $param
+ * @throws Zend_Console_Getopt_Exception
+ * @return bool
+ */
+ protected function _checkParameterType($flag, $param)
+ {
+ $type = 'string';
+ if (isset($this->_rules[$flag]['paramType'])) {
+ $type = $this->_rules[$flag]['paramType'];
+ }
+ switch ($type) {
+ case 'word':
+ if (preg_match('/\W/', $param)) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Option \"$flag\" requires a single-word parameter, but was given \"$param\".",
+ $this->getUsageMessage());
+ }
+ break;
+ case 'integer':
+ if (preg_match('/\D/', $param)) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Option \"$flag\" requires an integer parameter, but was given \"$param\".",
+ $this->getUsageMessage());
+ }
+ break;
+ case 'string':
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Define legal options using the gnu-style format.
+ *
+ * @param string $rules
+ * @return void
+ */
+ protected function _addRulesModeGnu($rules)
+ {
+ $ruleArray = array();
+
+ /**
+ * Options may be single alphanumeric characters.
+ * Options may have a ':' which indicates a required string parameter.
+ * No long options or option aliases are supported in GNU style.
+ */
+ preg_match_all('/([a-zA-Z0-9]:?)/', $rules, $ruleArray);
+ foreach ($ruleArray[1] as $rule) {
+ $r = array();
+ $flag = substr($rule, 0, 1);
+ if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flag = strtolower($flag);
+ }
+ $r['alias'][] = $flag;
+ if (substr($rule, 1, 1) == ':') {
+ $r['param'] = 'required';
+ $r['paramType'] = 'string';
+ } else {
+ $r['param'] = 'none';
+ }
+ $this->_rules[$flag] = $r;
+ $this->_ruleMap[$flag] = $flag;
+ }
+ }
+
+ /**
+ * Define legal options using the Zend-style format.
+ *
+ * @param array $rules
+ * @throws Zend_Console_Getopt_Exception
+ * @return void
+ */
+ protected function _addRulesModeZend($rules)
+ {
+ foreach ($rules as $ruleCode => $helpMessage)
+ {
+ // this may have to translate the long parm type if there
+ // are any complaints that =string will not work (even though that use
+ // case is not documented)
+ if (in_array(substr($ruleCode, -2, 1), array('-', '='))) {
+ $flagList = substr($ruleCode, 0, -2);
+ $delimiter = substr($ruleCode, -2, 1);
+ $paramType = substr($ruleCode, -1);
+ } else {
+ $flagList = $ruleCode;
+ $delimiter = $paramType = null;
+ }
+ if ($this->_getoptConfig[self::CONFIG_IGNORECASE]) {
+ $flagList = strtolower($flagList);
+ }
+ $flags = explode('|', $flagList);
+ $rule = array();
+ $mainFlag = $flags[0];
+ foreach ($flags as $flag) {
+ if (empty($flag)) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Blank flag not allowed in rule \"$ruleCode\".");
+ }
+ if (strlen($flag) == 1) {
+ if (isset($this->_ruleMap[$flag])) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Option \"-$flag\" is being defined more than once.");
+ }
+ $this->_ruleMap[$flag] = $mainFlag;
+ $rule['alias'][] = $flag;
+ } else {
+ if (isset($this->_rules[$flag]) || isset($this->_ruleMap[$flag])) {
+ require_once 'Zend/Console/Getopt/Exception.php';
+ throw new Zend_Console_Getopt_Exception(
+ "Option \"--$flag\" is being defined more than once.");
+ }
+ $this->_ruleMap[$flag] = $mainFlag;
+ $rule['alias'][] = $flag;
+ }
+ }
+ if (isset($delimiter)) {
+ switch ($delimiter) {
+ case self::PARAM_REQUIRED:
+ $rule['param'] = 'required';
+ break;
+ case self::PARAM_OPTIONAL:
+ default:
+ $rule['param'] = 'optional';
+ }
+ switch (substr($paramType, 0, 1)) {
+ case self::TYPE_WORD:
+ $rule['paramType'] = 'word';
+ break;
+ case self::TYPE_INTEGER:
+ $rule['paramType'] = 'integer';
+ break;
+ case self::TYPE_STRING:
+ default:
+ $rule['paramType'] = 'string';
+ }
+ } else {
+ $rule['param'] = 'none';
+ }
+ $rule['help'] = $helpMessage;
+ $this->_rules[$mainFlag] = $rule;
+ }
+ }
+
+}
diff --git a/library/Zend/Console/Getopt/Exception.php b/library/Zend/Console/Getopt/Exception.php
new file mode 100644
index 0000000..cabb406
--- /dev/null
+++ b/library/Zend/Console/Getopt/Exception.php
@@ -0,0 +1,66 @@
+usage = $usage;
+ parent::__construct($message);
+ }
+
+ /**
+ * Returns the usage
+ *
+ * @return string
+ */
+ public function getUsageMessage()
+ {
+ return $this->usage;
+ }
+}
diff --git a/library/Zend/Controller/Action.php b/library/Zend/Controller/Action.php
new file mode 100644
index 0000000..a6c1785
--- /dev/null
+++ b/library/Zend/Controller/Action.php
@@ -0,0 +1,692 @@
+setRequest($request)
+ ->setResponse($response)
+ ->_setInvokeArgs($invokeArgs);
+ $this->_helper = new Zend_Controller_Action_HelperBroker($this);
+ $this->init();
+ }
+
+ /**
+ * Initialize object
+ *
+ * Called from {@link __construct()} as final step of object instantiation.
+ *
+ * @return void
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * Initialize View object
+ *
+ * Initializes {@link $view} if not otherwise a Zend_View_Interface.
+ *
+ * If {@link $view} is not otherwise set, instantiates a new Zend_View
+ * object, using the 'views' subdirectory at the same level as the
+ * controller directory for the current module as the base directory.
+ * It uses this to set the following:
+ * - script path = views/scripts/
+ * - helper path = views/helpers/
+ * - filter path = views/filters/
+ *
+ * @return Zend_View_Interface
+ * @throws Zend_Controller_Exception if base view directory does not exist
+ */
+ public function initView()
+ {
+ if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
+ return $this->view;
+ }
+
+ require_once 'Zend/View/Interface.php';
+ if (isset($this->view) && ($this->view instanceof Zend_View_Interface)) {
+ return $this->view;
+ }
+
+ $request = $this->getRequest();
+ $module = $request->getModuleName();
+ $dirs = $this->getFrontController()->getControllerDirectory();
+ if (empty($module) || !isset($dirs[$module])) {
+ $module = $this->getFrontController()->getDispatcher()->getDefaultModule();
+ }
+ $baseDir = dirname($dirs[$module]) . DIRECTORY_SEPARATOR . 'views';
+ if (!file_exists($baseDir) || !is_dir($baseDir)) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Missing base view directory ("' . $baseDir . '")');
+ }
+
+ require_once 'Zend/View.php';
+ $this->view = new Zend_View(array('basePath' => $baseDir));
+
+ return $this->view;
+ }
+
+ /**
+ * Render a view
+ *
+ * Renders a view. By default, views are found in the view script path as
+ * /.phtml. You may change the script suffix by
+ * resetting {@link $viewSuffix}. You may omit the controller directory
+ * prefix by specifying boolean true for $noController.
+ *
+ * By default, the rendered contents are appended to the response. You may
+ * specify the named body content segment to set by specifying a $name.
+ *
+ * @see Zend_Controller_Response_Abstract::appendBody()
+ * @param string|null $action Defaults to action registered in request object
+ * @param string|null $name Response object named path segment to use; defaults to null
+ * @param bool $noController Defaults to false; i.e. use controller name as subdir in which to search for view script
+ * @return void
+ */
+ public function render($action = null, $name = null, $noController = false)
+ {
+ if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
+ return $this->_helper->viewRenderer->render($action, $name, $noController);
+ }
+
+ $view = $this->initView();
+ $script = $this->getViewScript($action, $noController);
+
+ $this->getResponse()->appendBody(
+ $view->render($script),
+ $name
+ );
+ }
+
+ /**
+ * Render a given view script
+ *
+ * Similar to {@link render()}, this method renders a view script. Unlike render(),
+ * however, it does not autodetermine the view script via {@link getViewScript()},
+ * but instead renders the script passed to it. Use this if you know the
+ * exact view script name and path you wish to use, or if using paths that do not
+ * conform to the spec defined with getViewScript().
+ *
+ * By default, the rendered contents are appended to the response. You may
+ * specify the named body content segment to set by specifying a $name.
+ *
+ * @param string $script
+ * @param string $name
+ * @return void
+ */
+ public function renderScript($script, $name = null)
+ {
+ if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
+ return $this->_helper->viewRenderer->renderScript($script, $name);
+ }
+
+ $view = $this->initView();
+ $this->getResponse()->appendBody(
+ $view->render($script),
+ $name
+ );
+ }
+
+ /**
+ * Construct view script path
+ *
+ * Used by render() to determine the path to the view script.
+ *
+ * @param string $action Defaults to action registered in request object
+ * @param bool $noController Defaults to false; i.e. use controller name as subdir in which to search for view script
+ * @return string
+ * @throws Zend_Controller_Exception with bad $action
+ */
+ public function getViewScript($action = null, $noController = null)
+ {
+ if (!$this->getInvokeArg('noViewRenderer') && $this->_helper->hasHelper('viewRenderer')) {
+ $viewRenderer = $this->_helper->getHelper('viewRenderer');
+ if (null !== $noController) {
+ $viewRenderer->setNoController($noController);
+ }
+ return $viewRenderer->getViewScript($action);
+ }
+
+ $request = $this->getRequest();
+ if (null === $action) {
+ $action = $request->getActionName();
+ } elseif (!is_string($action)) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Invalid action specifier for view render');
+ }
+
+ if (null === $this->_delimiters) {
+ $dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
+ $wordDelimiters = $dispatcher->getWordDelimiter();
+ $pathDelimiters = $dispatcher->getPathDelimiter();
+ $this->_delimiters = array_unique(array_merge($wordDelimiters, (array) $pathDelimiters));
+ }
+
+ $action = str_replace($this->_delimiters, '-', $action);
+ $script = $action . '.' . $this->viewSuffix;
+
+ if (!$noController) {
+ $controller = $request->getControllerName();
+ $controller = str_replace($this->_delimiters, '-', $controller);
+ $script = $controller . DIRECTORY_SEPARATOR . $script;
+ }
+
+ return $script;
+ }
+
+ /**
+ * Return the Request object
+ *
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function getRequest()
+ {
+ return $this->_request;
+ }
+
+ /**
+ * Set the Request object
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return Zend_Controller_Action
+ */
+ public function setRequest(Zend_Controller_Request_Abstract $request)
+ {
+ $this->_request = $request;
+ return $this;
+ }
+
+ /**
+ * Return the Response object
+ *
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+
+ /**
+ * Set the Response object
+ *
+ * @param Zend_Controller_Response_Abstract $response
+ * @return Zend_Controller_Action
+ */
+ public function setResponse(Zend_Controller_Response_Abstract $response)
+ {
+ $this->_response = $response;
+ return $this;
+ }
+
+ /**
+ * Set invocation arguments
+ *
+ * @param array $args
+ * @return Zend_Controller_Action
+ */
+ protected function _setInvokeArgs(array $args = array())
+ {
+ $this->_invokeArgs = $args;
+ return $this;
+ }
+
+ /**
+ * Return the array of constructor arguments (minus the Request object)
+ *
+ * @return array
+ */
+ public function getInvokeArgs()
+ {
+ return $this->_invokeArgs;
+ }
+
+ /**
+ * Return a single invocation argument
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function getInvokeArg($key)
+ {
+ if (isset($this->_invokeArgs[$key])) {
+ return $this->_invokeArgs[$key];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a helper by name
+ *
+ * @param string $helperName
+ * @return Zend_Controller_Action_Helper_Abstract
+ */
+ public function getHelper($helperName)
+ {
+ return $this->_helper->{$helperName};
+ }
+
+ /**
+ * Get a clone of a helper by name
+ *
+ * @param string $helperName
+ * @return Zend_Controller_Action_Helper_Abstract
+ */
+ public function getHelperCopy($helperName)
+ {
+ return clone $this->_helper->{$helperName};
+ }
+
+ /**
+ * Set the front controller instance
+ *
+ * @param Zend_Controller_Front $front
+ * @return Zend_Controller_Action
+ */
+ public function setFrontController(Zend_Controller_Front $front)
+ {
+ $this->_frontController = $front;
+ return $this;
+ }
+
+ /**
+ * Retrieve Front Controller
+ *
+ * @return Zend_Controller_Front
+ */
+ public function getFrontController()
+ {
+ // Used cache version if found
+ if (null !== $this->_frontController) {
+ return $this->_frontController;
+ }
+
+ // Grab singleton instance, if class has been loaded
+ if (class_exists('Zend_Controller_Front')) {
+ $this->_frontController = Zend_Controller_Front::getInstance();
+ return $this->_frontController;
+ }
+
+ // Throw exception in all other cases
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Front controller class has not been loaded');
+ }
+
+ /**
+ * Pre-dispatch routines
+ *
+ * Called before action method. If using class with
+ * {@link Zend_Controller_Front}, it may modify the
+ * {@link $_request Request object} and reset its dispatched flag in order
+ * to skip processing the current action.
+ *
+ * @return void
+ */
+ public function preDispatch()
+ {
+ }
+
+ /**
+ * Post-dispatch routines
+ *
+ * Called after action method execution. If using class with
+ * {@link Zend_Controller_Front}, it may modify the
+ * {@link $_request Request object} and reset its dispatched flag in order
+ * to process an additional action.
+ *
+ * Common usages for postDispatch() include rendering content in a sitewide
+ * template, link url correction, setting headers, etc.
+ *
+ * @return void
+ */
+ public function postDispatch()
+ {
+ }
+
+ /**
+ * Proxy for undefined methods. Default behavior is to throw an
+ * exception on undefined methods, however this function can be
+ * overridden to implement magic (dynamic) actions, or provide run-time
+ * dispatching.
+ *
+ * @param string $methodName
+ * @param array $args
+ * @return void
+ * @throws Zend_Controller_Action_Exception
+ */
+ public function __call($methodName, $args)
+ {
+ require_once 'Zend/Controller/Action/Exception.php';
+ if ('Action' == substr($methodName, -6)) {
+ $action = substr($methodName, 0, strlen($methodName) - 6);
+ throw new Zend_Controller_Action_Exception(sprintf('Action "%s" does not exist and was not trapped in __call()', $action), 404);
+ }
+
+ throw new Zend_Controller_Action_Exception(sprintf('Method "%s" does not exist and was not trapped in __call()', $methodName), 500);
+ }
+
+ /**
+ * Dispatch the requested action
+ *
+ * @param string $action Method name of action
+ * @return void
+ */
+ public function dispatch($action)
+ {
+ // Notify helpers of action preDispatch state
+ $this->_helper->notifyPreDispatch();
+
+ $this->preDispatch();
+ if ($this->getRequest()->isDispatched()) {
+ if (null === $this->_classMethods) {
+ $this->_classMethods = get_class_methods($this);
+ }
+
+ // If pre-dispatch hooks introduced a redirect then stop dispatch
+ // @see ZF-7496
+ if (!($this->getResponse()->isRedirect())) {
+ // preDispatch() didn't change the action, so we can continue
+ if ($this->getInvokeArg('useCaseSensitiveActions') || in_array($action, $this->_classMethods)) {
+ if ($this->getInvokeArg('useCaseSensitiveActions')) {
+ trigger_error('Using case sensitive actions without word separators is deprecated; please do not rely on this "feature"');
+ }
+ $this->$action();
+ } else {
+ $this->__call($action, array());
+ }
+ }
+ $this->postDispatch();
+ }
+
+ // whats actually important here is that this action controller is
+ // shutting down, regardless of dispatching; notify the helpers of this
+ // state
+ $this->_helper->notifyPostDispatch();
+ }
+
+ /**
+ * Call the action specified in the request object, and return a response
+ *
+ * Not used in the Action Controller implementation, but left for usage in
+ * Page Controller implementations. Dispatches a method based on the
+ * request.
+ *
+ * Returns a Zend_Controller_Response_Abstract object, instantiating one
+ * prior to execution if none exists in the controller.
+ *
+ * {@link preDispatch()} is called prior to the action,
+ * {@link postDispatch()} is called following it.
+ *
+ * @param null|Zend_Controller_Request_Abstract $request Optional request
+ * object to use
+ * @param null|Zend_Controller_Response_Abstract $response Optional response
+ * object to use
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function run(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null)
+ {
+ if (null !== $request) {
+ $this->setRequest($request);
+ } else {
+ $request = $this->getRequest();
+ }
+
+ if (null !== $response) {
+ $this->setResponse($response);
+ }
+
+ $action = $request->getActionName();
+ if (empty($action)) {
+ $action = 'index';
+ }
+ $action = $action . 'Action';
+
+ $request->setDispatched(true);
+ $this->dispatch($action);
+
+ return $this->getResponse();
+ }
+
+ /**
+ * Gets a parameter from the {@link $_request Request object}. If the
+ * parameter does not exist, NULL will be returned.
+ *
+ * If the parameter does not exist and $default is set, then
+ * $default will be returned instead of NULL.
+ *
+ * @param string $paramName
+ * @param mixed $default
+ * @return mixed
+ */
+ protected function _getParam($paramName, $default = null)
+ {
+ $value = $this->getRequest()->getParam($paramName);
+ if ((null === $value || '' === $value) && (null !== $default)) {
+ $value = $default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Set a parameter in the {@link $_request Request object}.
+ *
+ * @param string $paramName
+ * @param mixed $value
+ * @return Zend_Controller_Action
+ */
+ protected function _setParam($paramName, $value)
+ {
+ $this->getRequest()->setParam($paramName, $value);
+
+ return $this;
+ }
+
+ /**
+ * Determine whether a given parameter exists in the
+ * {@link $_request Request object}.
+ *
+ * @param string $paramName
+ * @return boolean
+ */
+ protected function _hasParam($paramName)
+ {
+ return null !== $this->getRequest()->getParam($paramName);
+ }
+
+ /**
+ * Return all parameters in the {@link $_request Request object}
+ * as an associative array.
+ *
+ * @return array
+ */
+ protected function _getAllParams()
+ {
+ return $this->getRequest()->getParams();
+ }
+
+
+ /**
+ * Forward to another controller/action.
+ *
+ * It is important to supply the unformatted names, i.e. "article"
+ * rather than "ArticleController". The dispatcher will do the
+ * appropriate formatting when the request is received.
+ *
+ * If only an action name is provided, forwards to that action in this
+ * controller.
+ *
+ * If an action and controller are specified, forwards to that action and
+ * controller in this module.
+ *
+ * Specifying an action, controller, and module is the most specific way to
+ * forward.
+ *
+ * A fourth argument, $params, will be used to set the request parameters.
+ * If either the controller or module are unnecessary for forwarding,
+ * simply pass null values for them before specifying the parameters.
+ *
+ * @param string $action
+ * @param string $controller
+ * @param string $module
+ * @param array $params
+ * @return void
+ */
+ final protected function _forward($action, $controller = null, $module = null, array $params = null)
+ {
+ $request = $this->getRequest();
+
+ if (null !== $params) {
+ $request->setParams($params);
+ }
+
+ if (null !== $controller) {
+ $request->setControllerName($controller);
+
+ // Module should only be reset if controller has been specified
+ if (null !== $module) {
+ $request->setModuleName($module);
+ }
+ }
+
+ $request->setActionName($action)
+ ->setDispatched(false);
+ }
+
+ /**
+ * Redirect to another URL
+ *
+ * Proxies to {@link Zend_Controller_Action_Helper_Redirector::gotoUrl()}.
+ *
+ * @param string $url
+ * @param array $options Options to be used when redirecting
+ * @return void
+ */
+ protected function _redirect($url, array $options = array())
+ {
+ $this->_helper->redirector->gotoUrl($url, $options);
+ }
+}
diff --git a/library/Zend/Controller/Action/Exception.php b/library/Zend/Controller/Action/Exception.php
new file mode 100644
index 0000000..c444b23
--- /dev/null
+++ b/library/Zend/Controller/Action/Exception.php
@@ -0,0 +1,38 @@
+_actionController = $actionController;
+ return $this;
+ }
+
+ /**
+ * Retrieve current action controller
+ *
+ * @return Zend_Controller_Action
+ */
+ public function getActionController()
+ {
+ return $this->_actionController;
+ }
+
+ /**
+ * Retrieve front controller instance
+ *
+ * @return Zend_Controller_Front
+ */
+ public function getFrontController()
+ {
+ return Zend_Controller_Front::getInstance();
+ }
+
+ /**
+ * Hook into action controller initialization
+ *
+ * @return void
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * Hook into action controller preDispatch() workflow
+ *
+ * @return void
+ */
+ public function preDispatch()
+ {
+ }
+
+ /**
+ * Hook into action controller postDispatch() workflow
+ *
+ * @return void
+ */
+ public function postDispatch()
+ {
+ }
+
+ /**
+ * getRequest() -
+ *
+ * @return Zend_Controller_Request_Abstract $request
+ */
+ public function getRequest()
+ {
+ $controller = $this->getActionController();
+ if (null === $controller) {
+ $controller = $this->getFrontController();
+ }
+
+ return $controller->getRequest();
+ }
+
+ /**
+ * getResponse() -
+ *
+ * @return Zend_Controller_Response_Abstract $response
+ */
+ public function getResponse()
+ {
+ $controller = $this->getActionController();
+ if (null === $controller) {
+ $controller = $this->getFrontController();
+ }
+
+ return $controller->getResponse();
+ }
+
+ /**
+ * getName()
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ $fullClassName = get_class($this);
+ if (strpos($fullClassName, '_') !== false) {
+ $helperName = strrchr($fullClassName, '_');
+ return ltrim($helperName, '_');
+ } elseif (strpos($fullClassName, '\\') !== false) {
+ $helperName = strrchr($fullClassName, '\\');
+ return ltrim($helperName, '\\');
+ } else {
+ return $fullClassName;
+ }
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/ActionStack.php b/library/Zend/Controller/Action/Helper/ActionStack.php
new file mode 100644
index 0000000..eceb6f2
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/ActionStack.php
@@ -0,0 +1,138 @@
+hasPlugin('Zend_Controller_Plugin_ActionStack')) {
+ /**
+ * @see Zend_Controller_Plugin_ActionStack
+ */
+ require_once 'Zend/Controller/Plugin/ActionStack.php';
+ $this->_actionStack = new Zend_Controller_Plugin_ActionStack();
+ $front->registerPlugin($this->_actionStack, 97);
+ } else {
+ $this->_actionStack = $front->getPlugin('Zend_Controller_Plugin_ActionStack');
+ }
+ }
+
+ /**
+ * Push onto the stack
+ *
+ * @param Zend_Controller_Request_Abstract $next
+ * @return Zend_Controller_Action_Helper_ActionStack Provides a fluent interface
+ */
+ public function pushStack(Zend_Controller_Request_Abstract $next)
+ {
+ $this->_actionStack->pushStack($next);
+ return $this;
+ }
+
+ /**
+ * Push a new action onto the stack
+ *
+ * @param string $action
+ * @param string $controller
+ * @param string $module
+ * @param array $params
+ * @throws Zend_Controller_Action_Exception
+ * @return Zend_Controller_Action_Helper_ActionStack
+ */
+ public function actionToStack($action, $controller = null, $module = null, array $params = array())
+ {
+ if ($action instanceof Zend_Controller_Request_Abstract) {
+ return $this->pushStack($action);
+ } elseif (!is_string($action)) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('ActionStack requires either a request object or minimally a string action');
+ }
+
+ $request = $this->getRequest();
+
+ if ($request instanceof Zend_Controller_Request_Abstract === false){
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Request object not set yet');
+ }
+
+ $controller = (null === $controller) ? $request->getControllerName() : $controller;
+ $module = (null === $module) ? $request->getModuleName() : $module;
+
+ /**
+ * @see Zend_Controller_Request_Simple
+ */
+ require_once 'Zend/Controller/Request/Simple.php';
+ $newRequest = new Zend_Controller_Request_Simple($action, $controller, $module, $params);
+
+ return $this->pushStack($newRequest);
+ }
+
+ /**
+ * Perform helper when called as $this->_helper->actionStack() from an action controller
+ *
+ * Proxies to {@link simple()}
+ *
+ * @param string $action
+ * @param string $controller
+ * @param string $module
+ * @param array $params
+ * @return boolean
+ */
+ public function direct($action, $controller = null, $module = null, array $params = array())
+ {
+ return $this->actionToStack($action, $controller, $module, $params);
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/AjaxContext.php b/library/Zend/Controller/Action/Helper/AjaxContext.php
new file mode 100644
index 0000000..33606d5
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/AjaxContext.php
@@ -0,0 +1,80 @@
+addContext('html', array('suffix' => 'ajax'));
+ }
+
+ /**
+ * Initialize AJAX context switching
+ *
+ * Checks for XHR requests; if detected, attempts to perform context switch.
+ *
+ * @param string $format
+ * @return void
+ */
+ public function initContext($format = null)
+ {
+ $this->_currentContext = null;
+
+ $request = $this->getRequest();
+ if (!method_exists($request, 'isXmlHttpRequest') ||
+ !$this->getRequest()->isXmlHttpRequest())
+ {
+ return;
+ }
+
+ return parent::initContext($format);
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/AutoComplete/Abstract.php b/library/Zend/Controller/Action/Helper/AutoComplete/Abstract.php
new file mode 100644
index 0000000..09510f8
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/AutoComplete/Abstract.php
@@ -0,0 +1,149 @@
+disableLayout();
+ }
+
+ Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->setNoRender(true);
+
+ return $this;
+ }
+
+ /**
+ * Encode data to JSON
+ *
+ * @param mixed $data
+ * @param bool $keepLayouts
+ * @throws Zend_Controller_Action_Exception
+ * @return string
+ */
+ public function encodeJson($data, $keepLayouts = false)
+ {
+ if ($this->validateData($data)) {
+ return Zend_Controller_Action_HelperBroker::getStaticHelper('Json')->encodeJson($data, $keepLayouts);
+ }
+
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Invalid data passed for autocompletion');
+ }
+
+ /**
+ * Send autocompletion data
+ *
+ * Calls prepareAutoCompletion, populates response body with this
+ * information, and sends response.
+ *
+ * @param mixed $data
+ * @param bool $keepLayouts
+ * @return string|void
+ */
+ public function sendAutoCompletion($data, $keepLayouts = false)
+ {
+ $data = $this->prepareAutoCompletion($data, $keepLayouts);
+
+ $response = $this->getResponse();
+ $response->setBody($data);
+
+ if (!$this->suppressExit) {
+ $response->sendResponse();
+ exit;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Strategy pattern: allow calling helper as broker method
+ *
+ * Prepares autocompletion data and, if $sendNow is true, immediately sends
+ * response.
+ *
+ * @param mixed $data
+ * @param bool $sendNow
+ * @param bool $keepLayouts
+ * @return string|void
+ */
+ public function direct($data, $sendNow = true, $keepLayouts = false)
+ {
+ if ($sendNow) {
+ return $this->sendAutoCompletion($data, $keepLayouts);
+ }
+
+ return $this->prepareAutoCompletion($data, $keepLayouts);
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/AutoCompleteDojo.php b/library/Zend/Controller/Action/Helper/AutoCompleteDojo.php
new file mode 100644
index 0000000..bda6ed9
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/AutoCompleteDojo.php
@@ -0,0 +1,87 @@
+ $value) {
+ $items[] = array('label' => $value, 'name' => $value);
+ }
+ $data = new Zend_Dojo_Data('name', $items);
+ }
+
+ if (!$keepLayouts) {
+ require_once 'Zend/Controller/Action/HelperBroker.php';
+ Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->setNoRender(true);
+
+ require_once 'Zend/Layout.php';
+ $layout = Zend_Layout::getMvcInstance();
+ if ($layout instanceof Zend_Layout) {
+ $layout->disableLayout();
+ }
+ }
+
+ $response = Zend_Controller_Front::getInstance()->getResponse();
+ $response->setHeader('Content-Type', 'application/json');
+
+ return $data->toJson();
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/AutoCompleteScriptaculous.php b/library/Zend/Controller/Action/Helper/AutoCompleteScriptaculous.php
new file mode 100644
index 0000000..1fb78bd
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/AutoCompleteScriptaculous.php
@@ -0,0 +1,82 @@
+validateData($data)) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Invalid data passed for autocompletion');
+ }
+
+ $data = (array) $data;
+ $data = '- ' . implode('
- ', $data) . '
';
+
+ if (!$keepLayouts) {
+ $this->disableLayouts();
+ }
+
+ return $data;
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/Cache.php b/library/Zend/Controller/Action/Helper/Cache.php
new file mode 100644
index 0000000..e0a5d47
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/Cache.php
@@ -0,0 +1,279 @@
+getRequest()->getControllerName();
+ $actions = array_unique($actions);
+ if (!isset($this->_caching[$controller])) {
+ $this->_caching[$controller] = array();
+ }
+ if (!empty($tags)) {
+ $tags = array_unique($tags);
+ if (!isset($this->_tags[$controller])) {
+ $this->_tags[$controller] = array();
+ }
+ }
+ foreach ($actions as $action) {
+ $this->_caching[$controller][] = $action;
+ if (!empty($tags)) {
+ $this->_tags[$controller][$action] = array();
+ foreach ($tags as $tag) {
+ $this->_tags[$controller][$action][] = $tag;
+ }
+ }
+ }
+ if ($extension) {
+ if (!isset($this->_extensions[$controller])) {
+ $this->_extensions[$controller] = array();
+ }
+ foreach ($actions as $action) {
+ $this->_extensions[$controller][$action] = $extension;
+ }
+ }
+ }
+
+ /**
+ * Remove a specific page cache static file based on its
+ * relative URL from the application's public directory.
+ * The file extension is not required here; usually matches
+ * the original REQUEST_URI that was cached.
+ *
+ * @param string $relativeUrl
+ * @param bool $recursive
+ * @return mixed
+ */
+ public function removePage($relativeUrl, $recursive = false)
+ {
+ $cache = $this->getCache(Zend_Cache_Manager::PAGECACHE);
+ if ($recursive) {
+ $backend = $cache->getBackend();
+ if (($backend instanceof Zend_Cache_Backend)
+ && method_exists($backend, 'removeRecursively')
+ ) {
+ return $backend->removeRecursively($relativeUrl);
+ }
+ }
+
+ return $cache->remove($relativeUrl);
+ }
+
+ /**
+ * Remove a specific page cache static file based on its
+ * relative URL from the application's public directory.
+ * The file extension is not required here; usually matches
+ * the original REQUEST_URI that was cached.
+ *
+ * @param array $tags
+ * @return mixed
+ */
+ public function removePagesTagged(array $tags)
+ {
+ return $this->getCache(Zend_Cache_Manager::PAGECACHE)
+ ->clean(Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, $tags);
+ }
+
+ /**
+ * Commence page caching for any cacheable actions
+ *
+ * @return void
+ */
+ public function preDispatch()
+ {
+ $controller = $this->getRequest()->getControllerName();
+ $action = $this->getRequest()->getActionName();
+ $stats = ob_get_status(true);
+ foreach ($stats as $status) {
+ if ($status['name'] == 'Zend_Cache_Frontend_Page::_flush'
+ || $status['name'] == 'Zend_Cache_Frontend_Capture::_flush') {
+ $obStarted = true;
+ }
+ }
+ if (!isset($obStarted) && isset($this->_caching[$controller]) &&
+ in_array($action, $this->_caching[$controller])) {
+ $reqUri = $this->getRequest()->getRequestUri();
+ $tags = array();
+ if (isset($this->_tags[$controller][$action])
+ && !empty($this->_tags[$controller][$action])) {
+ $tags = array_unique($this->_tags[$controller][$action]);
+ }
+ $extension = null;
+ if (isset($this->_extensions[$controller][$action])) {
+ $extension = $this->_extensions[$controller][$action];
+ }
+ $this->getCache(Zend_Cache_Manager::PAGECACHE)
+ ->start($this->_encodeCacheId($reqUri), $tags, $extension);
+ }
+ }
+
+ /**
+ * Encode a Cache ID as hexadecimal. This is a workaround because Backend ID validation
+ * is trapped in the Frontend classes. Will try to get this reversed for ZF 2.0
+ * because it's a major annoyance to have IDs so restricted!
+ *
+ * @return string
+ * @param string $requestUri
+ */
+ protected function _encodeCacheId($requestUri)
+ {
+ return bin2hex($requestUri);
+ }
+
+ /**
+ * Set an instance of the Cache Manager for this helper
+ *
+ * @param Zend_Cache_Manager $manager
+ * @return void
+ */
+ public function setManager(Zend_Cache_Manager $manager)
+ {
+ $this->_manager = $manager;
+ return $this;
+ }
+
+ /**
+ * Get the Cache Manager instance or instantiate the object if not
+ * exists. Attempts to load from bootstrap if available.
+ *
+ * @return Zend_Cache_Manager
+ */
+ public function getManager()
+ {
+ if ($this->_manager !== null) {
+ return $this->_manager;
+ }
+ $front = Zend_Controller_Front::getInstance();
+ if ($front->getParam('bootstrap')
+ && $front->getParam('bootstrap')->getResource('CacheManager')) {
+ return $front->getParam('bootstrap')
+ ->getResource('CacheManager');
+ }
+ $this->_manager = new Zend_Cache_Manager;
+ return $this->_manager;
+ }
+
+ /**
+ * Return a list of actions for the current Controller marked for
+ * caching
+ *
+ * @return array
+ */
+ public function getCacheableActions()
+ {
+ return $this->_caching;
+ }
+
+ /**
+ * Return a list of tags set for all cacheable actions
+ *
+ * @return array
+ */
+ public function getCacheableTags()
+ {
+ return $this->_tags;
+ }
+
+ /**
+ * Proxy non-matched methods back to Zend_Cache_Manager where
+ * appropriate
+ *
+ * @param string $method
+ * @param array $args
+ * @return mixed
+ */
+ public function __call($method, $args)
+ {
+ if (method_exists($this->getManager(), $method)) {
+ return call_user_func_array(
+ array($this->getManager(), $method), $args
+ );
+ }
+ throw new Zend_Controller_Action_Exception('Method does not exist:'
+ . $method);
+ }
+
+}
diff --git a/library/Zend/Controller/Action/Helper/ContextSwitch.php b/library/Zend/Controller/Action/Helper/ContextSwitch.php
new file mode 100644
index 0000000..f618b29
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/ContextSwitch.php
@@ -0,0 +1,1394 @@
+setConfig($options);
+ } elseif (is_array($options)) {
+ $this->setOptions($options);
+ }
+
+ if (empty($this->_contexts)) {
+ $this->addContexts(array(
+ 'json' => array(
+ 'suffix' => 'json',
+ 'headers' => array('Content-Type' => 'application/json'),
+ 'callbacks' => array(
+ 'init' => 'initJsonContext',
+ 'post' => 'postJsonContext'
+ )
+ ),
+ 'xml' => array(
+ 'suffix' => 'xml',
+ 'headers' => array('Content-Type' => 'application/xml'),
+ )
+ ));
+ }
+
+ $this->init();
+ }
+
+ /**
+ * Initialize at start of action controller
+ *
+ * Reset the view script suffix to the original state, or store the
+ * original state.
+ *
+ * @return void
+ */
+ public function init()
+ {
+ if (null === $this->_viewSuffixOrig) {
+ $this->_viewSuffixOrig = $this->_getViewRenderer()->getViewSuffix();
+ } else {
+ $this->_getViewRenderer()->setViewSuffix($this->_viewSuffixOrig);
+ }
+ }
+
+ /**
+ * Configure object from array of options
+ *
+ * @param array $options
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setOptions(array $options)
+ {
+ if (isset($options['contexts'])) {
+ $this->setContexts($options['contexts']);
+ unset($options['contexts']);
+ }
+
+ foreach ($options as $key => $value) {
+ $method = 'set' . ucfirst($key);
+ if (in_array($method, $this->_unconfigurable)) {
+ continue;
+ }
+
+ if (in_array($method, $this->_specialConfig)) {
+ $method = '_' . $method;
+ }
+
+ if (method_exists($this, $method)) {
+ $this->$method($value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Set object state from config object
+ *
+ * @param Zend_Config $config
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ return $this->setOptions($config->toArray());
+ }
+
+ /**
+ * Strategy pattern: return object
+ *
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function direct()
+ {
+ return $this;
+ }
+
+ /**
+ * Initialize context detection and switching
+ *
+ * @param mixed $format
+ * @throws Zend_Controller_Action_Exception
+ * @return void
+ */
+ public function initContext($format = null)
+ {
+ $this->_currentContext = null;
+
+ $controller = $this->getActionController();
+ $request = $this->getRequest();
+ $action = $request->getActionName();
+
+ // Return if no context switching enabled, or no context switching
+ // enabled for this action
+ $contexts = $this->getActionContexts($action);
+ if (empty($contexts)) {
+ return;
+ }
+
+ // Return if no context parameter provided
+ if (!$context = $request->getParam($this->getContextParam())) {
+ if ($format === null) {
+ return;
+ }
+ $context = $format;
+ $format = null;
+ }
+
+ // Check if context allowed by action controller
+ if (!$this->hasActionContext($action, $context)) {
+ return;
+ }
+
+ // Return if invalid context parameter provided and no format or invalid
+ // format provided
+ if (!$this->hasContext($context)) {
+ if (empty($format) || !$this->hasContext($format)) {
+
+ return;
+ }
+ }
+
+ // Use provided format if passed
+ if (!empty($format) && $this->hasContext($format)) {
+ $context = $format;
+ }
+
+ $suffix = $this->getSuffix($context);
+
+ $this->_getViewRenderer()->setViewSuffix($suffix);
+
+ $headers = $this->getHeaders($context);
+ if (!empty($headers)) {
+ $response = $this->getResponse();
+ foreach ($headers as $header => $content) {
+ $response->setHeader($header, $content);
+ }
+ }
+
+ if ($this->getAutoDisableLayout()) {
+ /**
+ * @see Zend_Layout
+ */
+ require_once 'Zend/Layout.php';
+ $layout = Zend_Layout::getMvcInstance();
+ if (null !== $layout) {
+ $layout->disableLayout();
+ }
+ }
+
+ if (null !== ($callback = $this->getCallback($context, self::TRIGGER_INIT))) {
+ if (is_string($callback) && method_exists($this, $callback)) {
+ $this->$callback();
+ } elseif (is_string($callback) && function_exists($callback)) {
+ $callback();
+ } elseif (is_array($callback)) {
+ call_user_func($callback);
+ } else {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Invalid context callback registered for context "%s"', $context));
+ }
+ }
+
+ $this->_currentContext = $context;
+ }
+
+ /**
+ * JSON context extra initialization
+ *
+ * Turns off viewRenderer auto-rendering
+ *
+ * @return void
+ */
+ public function initJsonContext()
+ {
+ if (!$this->getAutoJsonSerialization()) {
+ return;
+ }
+
+ $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
+ $view = $viewRenderer->view;
+ if ($view instanceof Zend_View_Interface) {
+ $viewRenderer->setNoRender(true);
+ }
+ }
+
+ /**
+ * Should JSON contexts auto-serialize?
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setAutoJsonSerialization($flag)
+ {
+ $this->_autoJsonSerialization = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Get JSON context auto-serialization flag
+ *
+ * @return boolean
+ */
+ public function getAutoJsonSerialization()
+ {
+ return $this->_autoJsonSerialization;
+ }
+
+ /**
+ * Set suffix from array
+ *
+ * @param array $spec
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ protected function _setSuffix(array $spec)
+ {
+ foreach ($spec as $context => $suffixInfo) {
+ if (!is_string($context)) {
+ $context = null;
+ }
+
+ if (is_string($suffixInfo)) {
+ $this->setSuffix($context, $suffixInfo);
+ continue;
+ } elseif (is_array($suffixInfo)) {
+ if (isset($suffixInfo['suffix'])) {
+ $suffix = $suffixInfo['suffix'];
+ $prependViewRendererSuffix = true;
+
+ if ((null === $context) && isset($suffixInfo['context'])) {
+ $context = $suffixInfo['context'];
+ }
+
+ if (isset($suffixInfo['prependViewRendererSuffix'])) {
+ $prependViewRendererSuffix = $suffixInfo['prependViewRendererSuffix'];
+ }
+
+ $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
+ continue;
+ }
+
+ $count = count($suffixInfo);
+ switch (true) {
+ case (($count < 2) && (null === $context)):
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Invalid suffix information provided in config');
+ case ($count < 2):
+ $suffix = array_shift($suffixInfo);
+ $this->setSuffix($context, $suffix);
+ break;
+ case (($count < 3) && (null === $context)):
+ $context = array_shift($suffixInfo);
+ $suffix = array_shift($suffixInfo);
+ $this->setSuffix($context, $suffix);
+ break;
+ case (($count == 3) && (null === $context)):
+ $context = array_shift($suffixInfo);
+ $suffix = array_shift($suffixInfo);
+ $prependViewRendererSuffix = array_shift($suffixInfo);
+ $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
+ break;
+ case ($count >= 2):
+ $suffix = array_shift($suffixInfo);
+ $prependViewRendererSuffix = array_shift($suffixInfo);
+ $this->setSuffix($context, $suffix, $prependViewRendererSuffix);
+ break;
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Customize view script suffix to use when switching context.
+ *
+ * Passing an empty suffix value to the setters disables the view script
+ * suffix change.
+ *
+ * @param string $context Context type for which to set suffix
+ * @param string $suffix Suffix to use
+ * @param boolean $prependViewRendererSuffix Whether or not to prepend the new suffix to the viewrenderer suffix
+ * @throws Zend_Controller_Action_Exception
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setSuffix($context, $suffix, $prependViewRendererSuffix = true)
+ {
+ if (!isset($this->_contexts[$context])) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Cannot set suffix; invalid context type "%s"', $context));
+ }
+
+ if (empty($suffix)) {
+ $suffix = '';
+ }
+
+ if (is_array($suffix)) {
+ if (isset($suffix['prependViewRendererSuffix'])) {
+ $prependViewRendererSuffix = $suffix['prependViewRendererSuffix'];
+ }
+ if (isset($suffix['suffix'])) {
+ $suffix = $suffix['suffix'];
+ } else {
+ $suffix = '';
+ }
+ }
+
+ $suffix = (string) $suffix;
+
+ if ($prependViewRendererSuffix) {
+ if (empty($suffix)) {
+ $suffix = $this->_getViewRenderer()->getViewSuffix();
+ } else {
+ $suffix .= '.' . $this->_getViewRenderer()->getViewSuffix();
+ }
+ }
+
+ $this->_contexts[$context]['suffix'] = $suffix;
+ return $this;
+ }
+
+ /**
+ * Retrieve suffix for given context type
+ *
+ * @param string $type Context type
+ * @throws Zend_Controller_Action_Exception
+ * @return string
+ */
+ public function getSuffix($type)
+ {
+ if (!isset($this->_contexts[$type])) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Cannot retrieve suffix; invalid context type "%s"', $type));
+ }
+
+ return $this->_contexts[$type]['suffix'];
+ }
+
+ /**
+ * Does the given context exist?
+ *
+ * @param string $context
+ * @param boolean $throwException
+ * @throws Zend_Controller_Action_Exception if context does not exist and throwException is true
+ * @return bool
+ */
+ public function hasContext($context, $throwException = false)
+ {
+ if (is_string($context)) {
+ if (isset($this->_contexts[$context])) {
+ return true;
+ }
+ } elseif (is_array($context)) {
+ $error = false;
+ foreach ($context as $test) {
+ if (!isset($this->_contexts[$test])) {
+ $error = (string) $test;
+ break;
+ }
+ }
+ if (false === $error) {
+ return true;
+ }
+ $context = $error;
+ } elseif (true === $context) {
+ return true;
+ }
+
+ if ($throwException) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Context "%s" does not exist', $context));
+ }
+
+ return false;
+ }
+
+ /**
+ * Add header to context
+ *
+ * @param string $context
+ * @param string $header
+ * @param string $content
+ * @throws Zend_Controller_Action_Exception
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function addHeader($context, $header, $content)
+ {
+ $context = (string) $context;
+ $this->hasContext($context, true);
+
+ $header = (string) $header;
+ $content = (string) $content;
+
+ if (isset($this->_contexts[$context]['headers'][$header])) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Cannot add "%s" header to context "%s": already exists', $header, $context));
+ }
+
+ $this->_contexts[$context]['headers'][$header] = $content;
+ return $this;
+ }
+
+ /**
+ * Customize response header to use when switching context
+ *
+ * Passing an empty header value to the setters disables the response
+ * header.
+ *
+ * @param string $type Context type for which to set suffix
+ * @param string $header Header to set
+ * @param string $content Header content
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setHeader($context, $header, $content)
+ {
+ $this->hasContext($context, true);
+ $context = (string) $context;
+ $header = (string) $header;
+ $content = (string) $content;
+
+ $this->_contexts[$context]['headers'][$header] = $content;
+ return $this;
+ }
+
+ /**
+ * Add multiple headers at once for a given context
+ *
+ * @param string $context
+ * @param array $headers
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function addHeaders($context, array $headers)
+ {
+ foreach ($headers as $header => $content) {
+ $this->addHeader($context, $header, $content);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set headers from context => headers pairs
+ *
+ * @param array $options
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ protected function _setHeaders(array $options)
+ {
+ foreach ($options as $context => $headers) {
+ if (!is_array($headers)) {
+ continue;
+ }
+ $this->setHeaders($context, $headers);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set multiple headers at once for a given context
+ *
+ * @param string $context
+ * @param array $headers
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setHeaders($context, array $headers)
+ {
+ $this->clearHeaders($context);
+ foreach ($headers as $header => $content) {
+ $this->setHeader($context, $header, $content);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve context header
+ *
+ * Returns the value of a given header for a given context type
+ *
+ * @param string $context
+ * @param string $header
+ * @return string|null
+ */
+ public function getHeader($context, $header)
+ {
+ $this->hasContext($context, true);
+ $context = (string) $context;
+ $header = (string) $header;
+ if (isset($this->_contexts[$context]['headers'][$header])) {
+ return $this->_contexts[$context]['headers'][$header];
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve context headers
+ *
+ * Returns all headers for a context as key/value pairs
+ *
+ * @param string $context
+ * @return array
+ */
+ public function getHeaders($context)
+ {
+ $this->hasContext($context, true);
+ $context = (string) $context;
+ return $this->_contexts[$context]['headers'];
+ }
+
+ /**
+ * Remove a single header from a context
+ *
+ * @param string $context
+ * @param string $header
+ * @return boolean
+ */
+ public function removeHeader($context, $header)
+ {
+ $this->hasContext($context, true);
+ $context = (string) $context;
+ $header = (string) $header;
+ if (isset($this->_contexts[$context]['headers'][$header])) {
+ unset($this->_contexts[$context]['headers'][$header]);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Clear all headers for a given context
+ *
+ * @param string $context
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function clearHeaders($context)
+ {
+ $this->hasContext($context, true);
+ $context = (string) $context;
+ $this->_contexts[$context]['headers'] = array();
+ return $this;
+ }
+
+ /**
+ * Validate trigger and return in normalized form
+ *
+ * @param string $trigger
+ * @throws Zend_Controller_Action_Exception
+ * @return string
+ */
+ protected function _validateTrigger($trigger)
+ {
+ $trigger = strtoupper($trigger);
+ if ('TRIGGER_' !== substr($trigger, 0, 8)) {
+ $trigger = 'TRIGGER_' . $trigger;
+ }
+
+ if (!in_array($trigger, array(self::TRIGGER_INIT, self::TRIGGER_POST))) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Invalid trigger "%s"', $trigger));
+ }
+
+ return $trigger;
+ }
+
+ /**
+ * Set a callback for a given context and trigger
+ *
+ * @param string $context
+ * @param string $trigger
+ * @param string|array $callback
+ * @throws Zend_Controller_Action_Exception
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setCallback($context, $trigger, $callback)
+ {
+ $this->hasContext($context, true);
+ $trigger = $this->_validateTrigger($trigger);
+
+ if (!is_string($callback)) {
+ if (!is_array($callback) || (2 != count($callback))) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Invalid callback specified');
+ }
+ }
+
+ $this->_contexts[$context]['callbacks'][$trigger] = $callback;
+ return $this;
+ }
+
+ /**
+ * Set callbacks from array of context => callbacks pairs
+ *
+ * @param array $options
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ protected function _setCallbacks(array $options)
+ {
+ foreach ($options as $context => $callbacks) {
+ if (!is_array($callbacks)) {
+ continue;
+ }
+
+ $this->setCallbacks($context, $callbacks);
+ }
+ return $this;
+ }
+
+ /**
+ * Set callbacks for a given context
+ *
+ * Callbacks should be in trigger/callback pairs.
+ *
+ * @param string $context
+ * @param array $callbacks
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setCallbacks($context, array $callbacks)
+ {
+ $this->hasContext($context, true);
+ $context = (string) $context;
+ if (!isset($this->_contexts[$context]['callbacks'])) {
+ $this->_contexts[$context]['callbacks'] = array();
+ }
+
+ foreach ($callbacks as $trigger => $callback) {
+ $this->setCallback($context, $trigger, $callback);
+ }
+ return $this;
+ }
+
+ /**
+ * Get a single callback for a given context and trigger
+ *
+ * @param string $context
+ * @param string $trigger
+ * @return string|array|null
+ */
+ public function getCallback($context, $trigger)
+ {
+ $this->hasContext($context, true);
+ $trigger = $this->_validateTrigger($trigger);
+ if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
+ return $this->_contexts[$context]['callbacks'][$trigger];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get all callbacks for a given context
+ *
+ * @param string $context
+ * @return array
+ */
+ public function getCallbacks($context)
+ {
+ $this->hasContext($context, true);
+ return $this->_contexts[$context]['callbacks'];
+ }
+
+ /**
+ * Clear a callback for a given context and trigger
+ *
+ * @param string $context
+ * @param string $trigger
+ * @return boolean
+ */
+ public function removeCallback($context, $trigger)
+ {
+ $this->hasContext($context, true);
+ $trigger = $this->_validateTrigger($trigger);
+ if (isset($this->_contexts[$context]['callbacks'][$trigger])) {
+ unset($this->_contexts[$context]['callbacks'][$trigger]);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Clear all callbacks for a given context
+ *
+ * @param string $context
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function clearCallbacks($context)
+ {
+ $this->hasContext($context, true);
+ $this->_contexts[$context]['callbacks'] = array();
+ return $this;
+ }
+
+ /**
+ * Set name of parameter to use when determining context format
+ *
+ * @param string $name
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setContextParam($name)
+ {
+ $this->_contextParam = (string) $name;
+ return $this;
+ }
+
+ /**
+ * Return context format request parameter name
+ *
+ * @return string
+ */
+ public function getContextParam()
+ {
+ return $this->_contextParam;
+ }
+
+ /**
+ * Indicate default context to use when no context format provided
+ *
+ * @param string $type
+ * @throws Zend_Controller_Action_Exception
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setDefaultContext($type)
+ {
+ if (!isset($this->_contexts[$type])) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Cannot set default context; invalid context type "%s"', $type));
+ }
+
+ $this->_defaultContext = $type;
+ return $this;
+ }
+
+ /**
+ * Return default context
+ *
+ * @return string
+ */
+ public function getDefaultContext()
+ {
+ return $this->_defaultContext;
+ }
+
+ /**
+ * Set flag indicating if layout should be disabled
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setAutoDisableLayout($flag)
+ {
+ $this->_disableLayout = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Retrieve auto layout disable flag
+ *
+ * @return boolean
+ */
+ public function getAutoDisableLayout()
+ {
+ return $this->_disableLayout;
+ }
+
+ /**
+ * Add new context
+ *
+ * @param string $context Context type
+ * @param array $spec Context specification
+ * @throws Zend_Controller_Action_Exception
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function addContext($context, array $spec)
+ {
+ if ($this->hasContext($context)) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Cannot add context "%s"; already exists', $context));
+ }
+ $context = (string) $context;
+
+ $this->_contexts[$context] = array();
+
+ $this->setSuffix($context, (isset($spec['suffix']) ? $spec['suffix'] : ''))
+ ->setHeaders($context, (isset($spec['headers']) ? $spec['headers'] : array()))
+ ->setCallbacks($context, (isset($spec['callbacks']) ? $spec['callbacks'] : array()));
+ return $this;
+ }
+
+ /**
+ * Overwrite existing context
+ *
+ * @param string $context Context type
+ * @param array $spec Context specification
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setContext($context, array $spec)
+ {
+ $this->removeContext($context);
+ return $this->addContext($context, $spec);
+ }
+
+ /**
+ * Add multiple contexts
+ *
+ * @param array $contexts
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function addContexts(array $contexts)
+ {
+ foreach ($contexts as $context => $spec) {
+ $this->addContext($context, $spec);
+ }
+ return $this;
+ }
+
+ /**
+ * Set multiple contexts, after first removing all
+ *
+ * @param array $contexts
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setContexts(array $contexts)
+ {
+ $this->clearContexts();
+ foreach ($contexts as $context => $spec) {
+ $this->addContext($context, $spec);
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve context specification
+ *
+ * @param string $context
+ * @return array|null
+ */
+ public function getContext($context)
+ {
+ if ($this->hasContext($context)) {
+ return $this->_contexts[(string) $context];
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve context definitions
+ *
+ * @return array
+ */
+ public function getContexts()
+ {
+ return $this->_contexts;
+ }
+
+ /**
+ * Remove a context
+ *
+ * @param string $context
+ * @return boolean
+ */
+ public function removeContext($context)
+ {
+ if ($this->hasContext($context)) {
+ unset($this->_contexts[(string) $context]);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove all contexts
+ *
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function clearContexts()
+ {
+ $this->_contexts = array();
+ return $this;
+ }
+
+ /**
+ * Return current context, if any
+ *
+ * @return null|string
+ */
+ public function getCurrentContext()
+ {
+ return $this->_currentContext;
+ }
+
+ /**
+ * Post dispatch processing
+ *
+ * Execute postDispatch callback for current context, if available
+ *
+ * @throws Zend_Controller_Action_Exception
+ * @return void
+ */
+ public function postDispatch()
+ {
+ $context = $this->getCurrentContext();
+ if (null !== $context) {
+ if (null !== ($callback = $this->getCallback($context, self::TRIGGER_POST))) {
+ if (is_string($callback) && method_exists($this, $callback)) {
+ $this->$callback();
+ } elseif (is_string($callback) && function_exists($callback)) {
+ $callback();
+ } elseif (is_array($callback)) {
+ call_user_func($callback);
+ } else {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Invalid postDispatch context callback registered for context "%s"', $context));
+ }
+ }
+ }
+ }
+
+ /**
+ * JSON post processing
+ *
+ * JSON serialize view variables to response body
+ *
+ * @return void
+ */
+ public function postJsonContext()
+ {
+ if (!$this->getAutoJsonSerialization()) {
+ return;
+ }
+
+ $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
+ $view = $viewRenderer->view;
+ if ($view instanceof Zend_View_Interface) {
+ /**
+ * @see Zend_Json
+ */
+ if(method_exists($view, 'getVars')) {
+ require_once 'Zend/Json.php';
+ $vars = Zend_Json::encode($view->getVars());
+ $this->getResponse()->setBody($vars);
+ } else {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('View does not implement the getVars() method needed to encode the view into JSON');
+ }
+ }
+ }
+
+ /**
+ * Add one or more contexts to an action
+ *
+ * @param string $action
+ * @param string|array $context
+ * @return Zend_Controller_Action_Helper_ContextSwitch|void Provides a fluent interface
+ */
+ public function addActionContext($action, $context)
+ {
+ $this->hasContext($context, true);
+ $controller = $this->getActionController();
+ if (null === $controller) {
+ return;
+ }
+ $action = (string) $action;
+ $contextKey = $this->_contextKey;
+
+ if (!isset($controller->$contextKey)) {
+ $controller->$contextKey = array();
+ }
+
+ if (true === $context) {
+ $contexts = $this->getContexts();
+ $controller->{$contextKey}[$action] = array_keys($contexts);
+ return $this;
+ }
+
+ $context = (array) $context;
+ if (!isset($controller->{$contextKey}[$action])) {
+ $controller->{$contextKey}[$action] = $context;
+ } else {
+ $controller->{$contextKey}[$action] = array_merge(
+ $controller->{$contextKey}[$action],
+ $context
+ );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set a context as available for a given controller action
+ *
+ * @param string $action
+ * @param string|array $context
+ * @return Zend_Controller_Action_Helper_ContextSwitch|void Provides a fluent interface
+ */
+ public function setActionContext($action, $context)
+ {
+ $this->hasContext($context, true);
+ $controller = $this->getActionController();
+ if (null === $controller) {
+ return;
+ }
+ $action = (string) $action;
+ $contextKey = $this->_contextKey;
+
+ if (!isset($controller->$contextKey)) {
+ $controller->$contextKey = array();
+ }
+
+ if (true === $context) {
+ $contexts = $this->getContexts();
+ $controller->{$contextKey}[$action] = array_keys($contexts);
+ } else {
+ $controller->{$contextKey}[$action] = (array) $context;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add multiple action/context pairs at once
+ *
+ * @param array $contexts
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function addActionContexts(array $contexts)
+ {
+ foreach ($contexts as $action => $context) {
+ $this->addActionContext($action, $context);
+ }
+ return $this;
+ }
+
+ /**
+ * Overwrite and set multiple action contexts at once
+ *
+ * @param array $contexts
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function setActionContexts(array $contexts)
+ {
+ foreach ($contexts as $action => $context) {
+ $this->setActionContext($action, $context);
+ }
+ return $this;
+ }
+
+ /**
+ * Does a particular controller action have the given context(s)?
+ *
+ * @param string $action
+ * @param string|array $context
+ * @throws Zend_Controller_Action_Exception
+ * @return boolean
+ */
+ public function hasActionContext($action, $context)
+ {
+ $this->hasContext($context, true);
+ $controller = $this->getActionController();
+ if (null === $controller) {
+ return false;
+ }
+ $action = (string) $action;
+ $contextKey = $this->_contextKey;
+
+ if (!isset($controller->{$contextKey})) {
+ return false;
+ }
+
+ $allContexts = $controller->{$contextKey};
+
+ if (!is_array($allContexts)) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception("Invalid contexts found for controller");
+ }
+
+ if (!isset($allContexts[$action])) {
+ return false;
+ }
+
+ if (true === $allContexts[$action]) {
+ return true;
+ }
+
+ $contexts = $allContexts[$action];
+
+ if (!is_array($contexts)) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf("Invalid contexts found for action '%s'", $action));
+ }
+
+ if (is_string($context) && in_array($context, $contexts)) {
+ return true;
+ } elseif (is_array($context)) {
+ $found = true;
+ foreach ($context as $test) {
+ if (!in_array($test, $contexts)) {
+ $found = false;
+ break;
+ }
+ }
+ return $found;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get contexts for a given action or all actions in the controller
+ *
+ * @param string $action
+ * @return array
+ */
+ public function getActionContexts($action = null)
+ {
+ $controller = $this->getActionController();
+ if (null === $controller) {
+ return array();
+ }
+ $action = (string) $action;
+ $contextKey = $this->_contextKey;
+
+ if (!isset($controller->$contextKey)) {
+ return array();
+ }
+
+ if (null !== $action) {
+ if (isset($controller->{$contextKey}[$action])) {
+ return $controller->{$contextKey}[$action];
+ } else {
+ return array();
+ }
+ }
+
+ return $controller->$contextKey;
+ }
+
+ /**
+ * Remove one or more contexts for a given controller action
+ *
+ * @param string $action
+ * @param string|array $context
+ * @return boolean
+ */
+ public function removeActionContext($action, $context)
+ {
+ if ($this->hasActionContext($action, $context)) {
+ $controller = $this->getActionController();
+ $contextKey = $this->_contextKey;
+ $action = (string) $action;
+ $contexts = $controller->$contextKey;
+ $actionContexts = $contexts[$action];
+ $contexts = (array) $context;
+ foreach ($contexts as $context) {
+ $index = array_search($context, $actionContexts);
+ if (false !== $index) {
+ unset($controller->{$contextKey}[$action][$index]);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Clear all contexts for a given controller action or all actions
+ *
+ * @param string $action
+ * @return Zend_Controller_Action_Helper_ContextSwitch Provides a fluent interface
+ */
+ public function clearActionContexts($action = null)
+ {
+ $controller = $this->getActionController();
+ $contextKey = $this->_contextKey;
+
+ if (!isset($controller->$contextKey) || empty($controller->$contextKey)) {
+ return $this;
+ }
+
+ if (null === $action) {
+ $controller->$contextKey = array();
+ return $this;
+ }
+
+ $action = (string) $action;
+ if (isset($controller->{$contextKey}[$action])) {
+ unset($controller->{$contextKey}[$action]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve ViewRenderer
+ *
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ protected function _getViewRenderer()
+ {
+ if (null === $this->_viewRenderer) {
+ $this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
+ }
+
+ return $this->_viewRenderer;
+ }
+}
+
diff --git a/library/Zend/Controller/Action/Helper/FlashMessenger.php b/library/Zend/Controller/Action/Helper/FlashMessenger.php
new file mode 100644
index 0000000..6b60189
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/FlashMessenger.php
@@ -0,0 +1,266 @@
+getName());
+ foreach (self::$_session as $namespace => $messages) {
+ self::$_messages[$namespace] = $messages;
+ unset(self::$_session->{$namespace});
+ }
+ }
+ }
+
+ /**
+ * postDispatch() - runs after action is dispatched, in this
+ * case, it is resetting the namespace in case we have forwarded to a different
+ * action, Flashmessage will be 'clean' (default namespace)
+ *
+ * @return Zend_Controller_Action_Helper_FlashMessenger Provides a fluent interface
+ */
+ public function postDispatch()
+ {
+ $this->resetNamespace();
+ return $this;
+ }
+
+ /**
+ * setNamespace() - change the namespace messages are added to, useful for
+ * per action controller messaging between requests
+ *
+ * @param string $namespace
+ * @return Zend_Controller_Action_Helper_FlashMessenger Provides a fluent interface
+ */
+ public function setNamespace($namespace = 'default')
+ {
+ $this->_namespace = $namespace;
+ return $this;
+ }
+
+ /**
+ * resetNamespace() - reset the namespace to the default
+ *
+ * @return Zend_Controller_Action_Helper_FlashMessenger Provides a fluent interface
+ */
+ public function resetNamespace()
+ {
+ $this->setNamespace();
+ return $this;
+ }
+
+ /**
+ * addMessage() - Add a message to flash message
+ *
+ * @param string $message
+ * @return Zend_Controller_Action_Helper_FlashMessenger Provides a fluent interface
+ */
+ public function addMessage($message)
+ {
+ if (self::$_messageAdded === false) {
+ self::$_session->setExpirationHops(1, null, true);
+ }
+
+ if (!is_array(self::$_session->{$this->_namespace})) {
+ self::$_session->{$this->_namespace} = array();
+ }
+
+ self::$_session->{$this->_namespace}[] = $message;
+
+ return $this;
+ }
+
+ /**
+ * hasMessages() - Wether a specific namespace has messages
+ *
+ * @return boolean
+ */
+ public function hasMessages()
+ {
+ return isset(self::$_messages[$this->_namespace]);
+ }
+
+ /**
+ * getMessages() - Get messages from a specific namespace
+ *
+ * @return array
+ */
+ public function getMessages()
+ {
+ if ($this->hasMessages()) {
+ return self::$_messages[$this->_namespace];
+ }
+
+ return array();
+ }
+
+ /**
+ * Clear all messages from the previous request & current namespace
+ *
+ * @return boolean True if messages were cleared, false if none existed
+ */
+ public function clearMessages()
+ {
+ if ($this->hasMessages()) {
+ unset(self::$_messages[$this->_namespace]);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * hasCurrentMessages() - check to see if messages have been added to current
+ * namespace within this request
+ *
+ * @return boolean
+ */
+ public function hasCurrentMessages()
+ {
+ return isset(self::$_session->{$this->_namespace});
+ }
+
+ /**
+ * getCurrentMessages() - get messages that have been added to the current
+ * namespace within this request
+ *
+ * @return array
+ */
+ public function getCurrentMessages()
+ {
+ if ($this->hasCurrentMessages()) {
+ return self::$_session->{$this->_namespace};
+ }
+
+ return array();
+ }
+
+ /**
+ * clear messages from the current request & current namespace
+ *
+ * @return boolean
+ */
+ public function clearCurrentMessages()
+ {
+ if ($this->hasCurrentMessages()) {
+ unset(self::$_session->{$this->_namespace});
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * getIterator() - complete the IteratorAggregate interface, for iterating
+ *
+ * @return ArrayObject
+ */
+ public function getIterator()
+ {
+ if ($this->hasMessages()) {
+ return new ArrayObject($this->getMessages());
+ }
+
+ return new ArrayObject();
+ }
+
+ /**
+ * count() - Complete the countable interface
+ *
+ * @return int
+ */
+ public function count()
+ {
+ if ($this->hasMessages()) {
+ return count($this->getMessages());
+ }
+
+ return 0;
+ }
+
+ /**
+ * Strategy pattern: proxy to addMessage()
+ *
+ * @param string $message
+ * @return void
+ */
+ public function direct($message)
+ {
+ return $this->addMessage($message);
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/Json.php b/library/Zend/Controller/Action/Helper/Json.php
new file mode 100644
index 0000000..d2f32c5
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/Json.php
@@ -0,0 +1,130 @@
+true|false
+ * if $keepLayouts and parmas for Zend_Json::encode are required
+ * then, the array can contains a 'keepLayout'=>true|false
+ * that will not be passed to Zend_Json::encode method but will be passed
+ * to Zend_View_Helper_Json
+ * @throws Zend_Controller_Action_Helper_Json
+ * @return string
+ */
+ public function encodeJson($data, $keepLayouts = false)
+ {
+ /**
+ * @see Zend_View_Helper_Json
+ */
+ require_once 'Zend/View/Helper/Json.php';
+ $jsonHelper = new Zend_View_Helper_Json();
+ $data = $jsonHelper->json($data, $keepLayouts);
+
+ if (!$keepLayouts) {
+ /**
+ * @see Zend_Controller_Action_HelperBroker
+ */
+ require_once 'Zend/Controller/Action/HelperBroker.php';
+ Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->setNoRender(true);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Encode JSON response and immediately send
+ *
+ * @param mixed $data
+ * @param boolean|array $keepLayouts
+ * NOTE: if boolean, establish $keepLayouts to true|false
+ * if array, admit params for Zend_Json::encode as enableJsonExprFinder=>true|false
+ * if $keepLayouts and parmas for Zend_Json::encode are required
+ * then, the array can contains a 'keepLayout'=>true|false
+ * that will not be passed to Zend_Json::encode method but will be passed
+ * to Zend_View_Helper_Json
+ * @return string|void
+ */
+ public function sendJson($data, $keepLayouts = false)
+ {
+ $data = $this->encodeJson($data, $keepLayouts);
+ $response = $this->getResponse();
+ $response->setBody($data);
+
+ if (!$this->suppressExit) {
+ $response->sendResponse();
+ exit;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Strategy pattern: call helper as helper broker method
+ *
+ * Allows encoding JSON. If $sendNow is true, immediately sends JSON
+ * response.
+ *
+ * @param mixed $data
+ * @param boolean $sendNow
+ * @param boolean $keepLayouts
+ * @return string|void
+ */
+ public function direct($data, $sendNow = true, $keepLayouts = false)
+ {
+ if ($sendNow) {
+ return $this->sendJson($data, $keepLayouts);
+ }
+ return $this->encodeJson($data, $keepLayouts);
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/Redirector.php b/library/Zend/Controller/Action/Helper/Redirector.php
new file mode 100644
index 0000000..efa3572
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/Redirector.php
@@ -0,0 +1,534 @@
+_code;
+ }
+
+ /**
+ * Validate HTTP status redirect code
+ *
+ * @param int $code
+ * @throws Zend_Controller_Action_Exception on invalid HTTP status code
+ * @return true
+ */
+ protected function _checkCode($code)
+ {
+ $code = (int)$code;
+ if ((300 > $code) || (307 < $code) || (304 == $code) || (306 == $code)) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Invalid redirect HTTP status code (' . $code . ')');
+ }
+
+ return true;
+ }
+
+ /**
+ * Retrieve HTTP status code for {@link _redirect()} behaviour
+ *
+ * @param int $code
+ * @return Zend_Controller_Action_Helper_Redirector Provides a fluent interface
+ */
+ public function setCode($code)
+ {
+ $this->_checkCode($code);
+ $this->_code = $code;
+ return $this;
+ }
+
+ /**
+ * Retrieve flag for whether or not {@link _redirect()} will exit when finished.
+ *
+ * @return boolean
+ */
+ public function getExit()
+ {
+ return $this->_exit;
+ }
+
+ /**
+ * Retrieve exit flag for {@link _redirect()} behaviour
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_Redirector Provides a fluent interface
+ */
+ public function setExit($flag)
+ {
+ $this->_exit = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Retrieve flag for whether or not {@link _redirect()} will prepend the
+ * base URL on relative URLs
+ *
+ * @return boolean
+ */
+ public function getPrependBase()
+ {
+ return $this->_prependBase;
+ }
+
+ /**
+ * Retrieve 'prepend base' flag for {@link _redirect()} behaviour
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_Redirector Provides a fluent interface
+ */
+ public function setPrependBase($flag)
+ {
+ $this->_prependBase = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Retrieve flag for whether or not {@link redirectAndExit()} shall close the session before
+ * exiting.
+ *
+ * @return boolean
+ */
+ public function getCloseSessionOnExit()
+ {
+ return $this->_closeSessionOnExit;
+ }
+
+ /**
+ * Set flag for whether or not {@link redirectAndExit()} shall close the session before exiting.
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_Redirector Provides a fluent interface
+ */
+ public function setCloseSessionOnExit($flag)
+ {
+ $this->_closeSessionOnExit = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Return use absolute URI flag
+ *
+ * @return boolean
+ */
+ public function getUseAbsoluteUri()
+ {
+ return $this->_useAbsoluteUri;
+ }
+
+ /**
+ * Set use absolute URI flag
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_Redirector Provides a fluent interface
+ */
+ public function setUseAbsoluteUri($flag = true)
+ {
+ $this->_useAbsoluteUri = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Set redirect in response object
+ *
+ * @return void
+ */
+ protected function _redirect($url)
+ {
+ if ($this->getUseAbsoluteUri() && !preg_match('#^(https?|ftp)://#', $url)) {
+ $host = (isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:'');
+ $proto = (isset($_SERVER['HTTPS'])&&$_SERVER['HTTPS']!=="off") ? 'https' : 'http';
+ $port = (isset($_SERVER['SERVER_PORT'])?$_SERVER['SERVER_PORT']:80);
+ $uri = $proto . '://' . $host;
+ if ((('http' == $proto) && (80 != $port)) || (('https' == $proto) && (443 != $port))) {
+ // do not append if HTTP_HOST already contains port
+ if (strrchr($host, ':') === false) {
+ $uri .= ':' . $port;
+ }
+ }
+ $url = $uri . '/' . ltrim($url, '/');
+ }
+ $this->_redirectUrl = $url;
+ $this->getResponse()->setRedirect($url, $this->getCode());
+ }
+
+ /**
+ * Retrieve currently set URL for redirect
+ *
+ * @return string
+ */
+ public function getRedirectUrl()
+ {
+ return $this->_redirectUrl;
+ }
+
+ /**
+ * Determine if the baseUrl should be prepended, and prepend if necessary
+ *
+ * @param string $url
+ * @return string
+ */
+ protected function _prependBase($url)
+ {
+ if ($this->getPrependBase()) {
+ $request = $this->getRequest();
+ if ($request instanceof Zend_Controller_Request_Http) {
+ $base = rtrim($request->getBaseUrl(), '/');
+ if (!empty($base) && ('/' != $base)) {
+ $url = $base . '/' . ltrim($url, '/');
+ } else {
+ $url = '/' . ltrim($url, '/');
+ }
+ }
+ }
+
+ return $url;
+ }
+
+ /**
+ * Set a redirect URL of the form /module/controller/action/params
+ *
+ * @param string $action
+ * @param string $controller
+ * @param string $module
+ * @param array $params
+ * @return void
+ */
+ public function setGotoSimple($action, $controller = null, $module = null, array $params = array())
+ {
+ $dispatcher = $this->getFrontController()->getDispatcher();
+ $request = $this->getRequest();
+ $curModule = $request->getModuleName();
+ $useDefaultController = false;
+
+ if (null === $controller && null !== $module) {
+ $useDefaultController = true;
+ }
+
+ if (null === $module) {
+ $module = $curModule;
+ }
+
+ if ($module == $dispatcher->getDefaultModule()) {
+ $module = '';
+ }
+
+ if (null === $controller && !$useDefaultController) {
+ $controller = $request->getControllerName();
+ if (empty($controller)) {
+ $controller = $dispatcher->getDefaultControllerName();
+ }
+ }
+
+ $params[$request->getModuleKey()] = $module;
+ $params[$request->getControllerKey()] = $controller;
+ $params[$request->getActionKey()] = $action;
+
+ $router = $this->getFrontController()->getRouter();
+ $url = $router->assemble($params, 'default', true);
+
+ $this->_redirect($url);
+ }
+
+ /**
+ * Build a URL based on a route
+ *
+ * @param array $urlOptions
+ * @param string $name Route name
+ * @param boolean $reset
+ * @param boolean $encode
+ * @return void
+ */
+ public function setGotoRoute(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
+ {
+ $router = $this->getFrontController()->getRouter();
+ $url = $router->assemble($urlOptions, $name, $reset, $encode);
+
+ $this->_redirect($url);
+ }
+
+ /**
+ * Set a redirect URL string
+ *
+ * By default, emits a 302 HTTP status header, prepends base URL as defined
+ * in request object if url is relative, and halts script execution by
+ * calling exit().
+ *
+ * $options is an optional associative array that can be used to control
+ * redirect behaviour. The available option keys are:
+ * - exit: boolean flag indicating whether or not to halt script execution when done
+ * - prependBase: boolean flag indicating whether or not to prepend the base URL when a relative URL is provided
+ * - code: integer HTTP status code to use with redirect. Should be between 300 and 307.
+ *
+ * _redirect() sets the Location header in the response object. If you set
+ * the exit flag to false, you can override this header later in code
+ * execution.
+ *
+ * If the exit flag is true (true by default), _redirect() will write and
+ * close the current session, if any.
+ *
+ * @param string $url
+ * @param array $options
+ * @return void
+ */
+ public function setGotoUrl($url, array $options = array())
+ {
+ // prevent header injections
+ $url = str_replace(array("\n", "\r"), '', $url);
+
+ if (null !== $options) {
+ if (isset($options['exit'])) {
+ $this->setExit(($options['exit']) ? true : false);
+ }
+ if (isset($options['prependBase'])) {
+ $this->setPrependBase(($options['prependBase']) ? true : false);
+ }
+ if (isset($options['code'])) {
+ $this->setCode($options['code']);
+ }
+ }
+
+ // If relative URL, decide if we should prepend base URL
+ if (!preg_match('|^[a-z]+://|', $url)) {
+ $url = $this->_prependBase($url);
+ }
+
+ $this->_redirect($url);
+ }
+
+ /**
+ * Perform a redirect to an action/controller/module with params
+ *
+ * @param string $action
+ * @param string $controller
+ * @param string $module
+ * @param array $params
+ * @return void
+ */
+ public function gotoSimple($action, $controller = null, $module = null, array $params = array())
+ {
+ $this->setGotoSimple($action, $controller, $module, $params);
+
+ if ($this->getExit()) {
+ $this->redirectAndExit();
+ }
+ }
+
+ /**
+ * Perform a redirect to an action/controller/module with params, forcing an immdiate exit
+ *
+ * @param mixed $action
+ * @param mixed $controller
+ * @param mixed $module
+ * @param array $params
+ * @return void
+ */
+ public function gotoSimpleAndExit($action, $controller = null, $module = null, array $params = array())
+ {
+ $this->setGotoSimple($action, $controller, $module, $params);
+ $this->redirectAndExit();
+ }
+
+ /**
+ * Redirect to a route-based URL
+ *
+ * Uses route's assemble method tobuild the URL; route is specified by $name;
+ * default route is used if none provided.
+ *
+ * @param array $urlOptions Array of key/value pairs used to assemble URL
+ * @param string $name
+ * @param boolean $reset
+ * @param boolean $encode
+ * @return void
+ */
+ public function gotoRoute(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
+ {
+ $this->setGotoRoute($urlOptions, $name, $reset, $encode);
+
+ if ($this->getExit()) {
+ $this->redirectAndExit();
+ }
+ }
+
+ /**
+ * Redirect to a route-based URL, and immediately exit
+ *
+ * Uses route's assemble method tobuild the URL; route is specified by $name;
+ * default route is used if none provided.
+ *
+ * @param array $urlOptions Array of key/value pairs used to assemble URL
+ * @param string $name
+ * @param boolean $reset
+ * @return void
+ */
+ public function gotoRouteAndExit(array $urlOptions = array(), $name = null, $reset = false)
+ {
+ $this->setGotoRoute($urlOptions, $name, $reset);
+ $this->redirectAndExit();
+ }
+
+ /**
+ * Perform a redirect to a url
+ *
+ * @param string $url
+ * @param array $options
+ * @return void
+ */
+ public function gotoUrl($url, array $options = array())
+ {
+ $this->setGotoUrl($url, $options);
+
+ if ($this->getExit()) {
+ $this->redirectAndExit();
+ }
+ }
+
+ /**
+ * Set a URL string for a redirect, perform redirect, and immediately exit
+ *
+ * @param string $url
+ * @param array $options
+ * @return void
+ */
+ public function gotoUrlAndExit($url, array $options = array())
+ {
+ $this->setGotoUrl($url, $options);
+ $this->redirectAndExit();
+ }
+
+ /**
+ * exit(): Perform exit for redirector
+ *
+ * @return void
+ */
+ public function redirectAndExit()
+ {
+ if ($this->getCloseSessionOnExit()) {
+ // Close session, if started
+ if (class_exists('Zend_Session', false) && Zend_Session::isStarted()) {
+ Zend_Session::writeClose();
+ } elseif (isset($_SESSION)) {
+ session_write_close();
+ }
+ }
+
+ $this->getResponse()->sendHeaders();
+ exit();
+ }
+
+ /**
+ * direct(): Perform helper when called as
+ * $this->_helper->redirector($action, $controller, $module, $params)
+ *
+ * @param string $action
+ * @param string $controller
+ * @param string $module
+ * @param array $params
+ * @return void
+ */
+ public function direct($action, $controller = null, $module = null, array $params = array())
+ {
+ $this->gotoSimple($action, $controller, $module, $params);
+ }
+
+ /**
+ * Overloading
+ *
+ * Overloading for old 'goto', 'setGoto', and 'gotoAndExit' methods
+ *
+ * @param string $method
+ * @param array $args
+ * @return mixed
+ * @throws Zend_Controller_Action_Exception for invalid methods
+ */
+ public function __call($method, $args)
+ {
+ $method = strtolower($method);
+ if ('goto' == $method) {
+ return call_user_func_array(array($this, 'gotoSimple'), $args);
+ }
+ if ('setgoto' == $method) {
+ return call_user_func_array(array($this, 'setGotoSimple'), $args);
+ }
+ if ('gotoandexit' == $method) {
+ return call_user_func_array(array($this, 'gotoSimpleAndExit'), $args);
+ }
+
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception(sprintf('Invalid method "%s" called on redirector', $method));
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/Url.php b/library/Zend/Controller/Action/Helper/Url.php
new file mode 100644
index 0000000..ab0908a
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/Url.php
@@ -0,0 +1,117 @@
+getRequest();
+
+ if (null === $controller) {
+ $controller = $request->getControllerName();
+ }
+
+ if (null === $module) {
+ $module = $request->getModuleName();
+ }
+
+ $url = $controller . '/' . $action;
+ if ($module != $this->getFrontController()->getDispatcher()->getDefaultModule()) {
+ $url = $module . '/' . $url;
+ }
+
+ if ('' !== ($baseUrl = $this->getFrontController()->getBaseUrl())) {
+ $url = $baseUrl . '/' . $url;
+ }
+
+ if (null !== $params) {
+ $paramPairs = array();
+ foreach ($params as $key => $value) {
+ $paramPairs[] = urlencode($key) . '/' . urlencode($value);
+ }
+ $paramString = implode('/', $paramPairs);
+ $url .= '/' . $paramString;
+ }
+
+ $url = '/' . ltrim($url, '/');
+
+ return $url;
+ }
+
+ /**
+ * Assembles a URL based on a given route
+ *
+ * This method will typically be used for more complex operations, as it
+ * ties into the route objects registered with the router.
+ *
+ * @param array $urlOptions Options passed to the assemble method of the Route object.
+ * @param mixed $name The name of a Route to use. If null it will use the current Route
+ * @param boolean $reset
+ * @param boolean $encode
+ * @return string Url for the link href attribute.
+ */
+ public function url($urlOptions = array(), $name = null, $reset = false, $encode = true)
+ {
+ $router = $this->getFrontController()->getRouter();
+ return $router->assemble($urlOptions, $name, $reset, $encode);
+ }
+
+ /**
+ * Perform helper when called as $this->_helper->url() from an action controller
+ *
+ * Proxies to {@link simple()}
+ *
+ * @param string $action
+ * @param string $controller
+ * @param string $module
+ * @param array $params
+ * @return string
+ */
+ public function direct($action, $controller = null, $module = null, array $params = null)
+ {
+ return $this->simple($action, $controller, $module, $params);
+ }
+}
diff --git a/library/Zend/Controller/Action/Helper/ViewRenderer.php b/library/Zend/Controller/Action/Helper/ViewRenderer.php
new file mode 100644
index 0000000..51059be
--- /dev/null
+++ b/library/Zend/Controller/Action/Helper/ViewRenderer.php
@@ -0,0 +1,992 @@
+
+ * // In your bootstrap:
+ * Zend_Controller_Action_HelperBroker::addHelper(new Zend_Controller_Action_Helper_ViewRenderer());
+ *
+ * // In your action controller methods:
+ * $viewHelper = $this->_helper->getHelper('view');
+ *
+ * // Don't use controller subdirectories
+ * $viewHelper->setNoController(true);
+ *
+ * // Specify a different script to render:
+ * $this->_helper->viewRenderer('form');
+ *
+ *
+ *
+ * @uses Zend_Controller_Action_Helper_Abstract
+ * @package Zend_Controller
+ * @subpackage Zend_Controller_Action_Helper
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Controller_Action_Helper_ViewRenderer extends Zend_Controller_Action_Helper_Abstract
+{
+ /**
+ * @var Zend_View_Interface
+ */
+ public $view;
+
+ /**
+ * Word delimiters
+ * @var array
+ */
+ protected $_delimiters;
+
+ /**
+ * @var Zend_Filter_Inflector
+ */
+ protected $_inflector;
+
+ /**
+ * Inflector target
+ * @var string
+ */
+ protected $_inflectorTarget = '';
+
+ /**
+ * Current module directory
+ * @var string
+ */
+ protected $_moduleDir = '';
+
+ /**
+ * Whether or not to autorender using controller name as subdirectory;
+ * global setting (not reset at next invocation)
+ * @var boolean
+ */
+ protected $_neverController = false;
+
+ /**
+ * Whether or not to autorender postDispatch; global setting (not reset at
+ * next invocation)
+ * @var boolean
+ */
+ protected $_neverRender = false;
+
+ /**
+ * Whether or not to use a controller name as a subdirectory when rendering
+ * @var boolean
+ */
+ protected $_noController = false;
+
+ /**
+ * Whether or not to autorender postDispatch; per controller/action setting (reset
+ * at next invocation)
+ * @var boolean
+ */
+ protected $_noRender = false;
+
+ /**
+ * Characters representing path delimiters in the controller
+ * @var string|array
+ */
+ protected $_pathDelimiters;
+
+ /**
+ * Which named segment of the response to utilize
+ * @var string
+ */
+ protected $_responseSegment = null;
+
+ /**
+ * Which action view script to render
+ * @var string
+ */
+ protected $_scriptAction = null;
+
+ /**
+ * View object basePath
+ * @var string
+ */
+ protected $_viewBasePathSpec = ':moduleDir/views';
+
+ /**
+ * View script path specification string
+ * @var string
+ */
+ protected $_viewScriptPathSpec = ':controller/:action.:suffix';
+
+ /**
+ * View script path specification string, minus controller segment
+ * @var string
+ */
+ protected $_viewScriptPathNoControllerSpec = ':action.:suffix';
+
+ /**
+ * View script suffix
+ * @var string
+ */
+ protected $_viewSuffix = 'phtml';
+
+ /**
+ * Constructor
+ *
+ * Optionally set view object and options.
+ *
+ * @param Zend_View_Interface $view
+ * @param array $options
+ * @return void
+ */
+ public function __construct(Zend_View_Interface $view = null, array $options = array())
+ {
+ if (null !== $view) {
+ $this->setView($view);
+ }
+
+ if (!empty($options)) {
+ $this->_setOptions($options);
+ }
+ }
+
+ /**
+ * Clone - also make sure the view is cloned.
+ *
+ * @return void
+ */
+ public function __clone()
+ {
+ if (isset($this->view) && $this->view instanceof Zend_View_Interface) {
+ $this->view = clone $this->view;
+
+ }
+ }
+
+ /**
+ * Set the view object
+ *
+ * @param Zend_View_Interface $view
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setView(Zend_View_Interface $view)
+ {
+ $this->view = $view;
+ return $this;
+ }
+
+ /**
+ * Get current module name
+ *
+ * @return string
+ */
+ public function getModule()
+ {
+ $request = $this->getRequest();
+ $module = $request->getModuleName();
+ if (null === $module) {
+ $module = $this->getFrontController()->getDispatcher()->getDefaultModule();
+ }
+
+ return $module;
+ }
+
+ /**
+ * Get module directory
+ *
+ * @throws Zend_Controller_Action_Exception
+ * @return string
+ */
+ public function getModuleDirectory()
+ {
+ $module = $this->getModule();
+ $moduleDir = $this->getFrontController()->getControllerDirectory($module);
+ if ((null === $moduleDir) || is_array($moduleDir)) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('ViewRenderer cannot locate module directory for module "' . $module . '"');
+ }
+ $this->_moduleDir = dirname($moduleDir);
+ return $this->_moduleDir;
+ }
+
+ /**
+ * Get inflector
+ *
+ * @return Zend_Filter_Inflector
+ */
+ public function getInflector()
+ {
+ if (null === $this->_inflector) {
+ /**
+ * @see Zend_Filter_Inflector
+ */
+ require_once 'Zend/Filter/Inflector.php';
+ /**
+ * @see Zend_Filter_PregReplace
+ */
+ require_once 'Zend/Filter/PregReplace.php';
+ /**
+ * @see Zend_Filter_Word_UnderscoreToSeparator
+ */
+ require_once 'Zend/Filter/Word/UnderscoreToSeparator.php';
+ $this->_inflector = new Zend_Filter_Inflector();
+ $this->_inflector->setStaticRuleReference('moduleDir', $this->_moduleDir) // moduleDir must be specified before the less specific 'module'
+ ->addRules(array(
+ ':module' => array('Word_CamelCaseToDash', 'StringToLower'),
+ ':controller' => array('Word_CamelCaseToDash', new Zend_Filter_Word_UnderscoreToSeparator('/'), 'StringToLower', new Zend_Filter_PregReplace('/\./', '-')),
+ ':action' => array('Word_CamelCaseToDash', new Zend_Filter_PregReplace('#[^a-z0-9' . preg_quote('/', '#') . ']+#i', '-'), 'StringToLower'),
+ ))
+ ->setStaticRuleReference('suffix', $this->_viewSuffix)
+ ->setTargetReference($this->_inflectorTarget);
+ }
+
+ // Ensure that module directory is current
+ $this->getModuleDirectory();
+
+ return $this->_inflector;
+ }
+
+ /**
+ * Set inflector
+ *
+ * @param Zend_Filter_Inflector $inflector
+ * @param boolean $reference Whether the moduleDir, target, and suffix should be set as references to ViewRenderer properties
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setInflector(Zend_Filter_Inflector $inflector, $reference = false)
+ {
+ $this->_inflector = $inflector;
+ if ($reference) {
+ $this->_inflector->setStaticRuleReference('suffix', $this->_viewSuffix)
+ ->setStaticRuleReference('moduleDir', $this->_moduleDir)
+ ->setTargetReference($this->_inflectorTarget);
+ }
+ return $this;
+ }
+
+ /**
+ * Set inflector target
+ *
+ * @param string $target
+ * @return void
+ */
+ protected function _setInflectorTarget($target)
+ {
+ $this->_inflectorTarget = (string) $target;
+ }
+
+ /**
+ * Set internal module directory representation
+ *
+ * @param string $dir
+ * @return void
+ */
+ protected function _setModuleDir($dir)
+ {
+ $this->_moduleDir = (string) $dir;
+ }
+
+ /**
+ * Get internal module directory representation
+ *
+ * @return string
+ */
+ protected function _getModuleDir()
+ {
+ return $this->_moduleDir;
+ }
+
+ /**
+ * Generate a class prefix for helper and filter classes
+ *
+ * @return string
+ */
+ protected function _generateDefaultPrefix()
+ {
+ $default = 'Zend_View';
+ if (null === $this->_actionController) {
+ return $default;
+ }
+
+ $class = get_class($this->_actionController);
+
+ if (!strstr($class, '_')) {
+ return $default;
+ }
+
+ $module = $this->getModule();
+ if ('default' == $module) {
+ return $default;
+ }
+
+ $prefix = substr($class, 0, strpos($class, '_')) . '_View';
+
+ return $prefix;
+ }
+
+ /**
+ * Retrieve base path based on location of current action controller
+ *
+ * @return string
+ */
+ protected function _getBasePath()
+ {
+ if (null === $this->_actionController) {
+ return './views';
+ }
+
+ $inflector = $this->getInflector();
+ $this->_setInflectorTarget($this->getViewBasePathSpec());
+
+ $dispatcher = $this->getFrontController()->getDispatcher();
+ $request = $this->getRequest();
+
+ $parts = array(
+ 'module' => (($moduleName = $request->getModuleName()) != '') ? $dispatcher->formatModuleName($moduleName) : $moduleName,
+ 'controller' => $request->getControllerName(),
+ 'action' => $dispatcher->formatActionName($request->getActionName())
+ );
+
+ $path = $inflector->filter($parts);
+ return $path;
+ }
+
+ /**
+ * Set options
+ *
+ * @param array $options
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ protected function _setOptions(array $options)
+ {
+ foreach ($options as $key => $value)
+ {
+ switch ($key) {
+ case 'neverRender':
+ case 'neverController':
+ case 'noController':
+ case 'noRender':
+ $property = '_' . $key;
+ $this->{$property} = ($value) ? true : false;
+ break;
+ case 'responseSegment':
+ case 'scriptAction':
+ case 'viewBasePathSpec':
+ case 'viewScriptPathSpec':
+ case 'viewScriptPathNoControllerSpec':
+ case 'viewSuffix':
+ $property = '_' . $key;
+ $this->{$property} = (string) $value;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Initialize the view object
+ *
+ * $options may contain the following keys:
+ * - neverRender - flag dis/enabling postDispatch() autorender (affects all subsequent calls)
+ * - noController - flag indicating whether or not to look for view scripts in subdirectories named after the controller
+ * - noRender - flag indicating whether or not to autorender postDispatch()
+ * - responseSegment - which named response segment to render a view script to
+ * - scriptAction - what action script to render
+ * - viewBasePathSpec - specification to use for determining view base path
+ * - viewScriptPathSpec - specification to use for determining view script paths
+ * - viewScriptPathNoControllerSpec - specification to use for determining view script paths when noController flag is set
+ * - viewSuffix - what view script filename suffix to use
+ *
+ * @param string $path
+ * @param string $prefix
+ * @param array $options
+ * @throws Zend_Controller_Action_Exception
+ * @return void
+ */
+ public function initView($path = null, $prefix = null, array $options = array())
+ {
+ if (null === $this->view) {
+ $this->setView(new Zend_View());
+ }
+
+ // Reset some flags every time
+ $options['noController'] = (isset($options['noController'])) ? $options['noController'] : false;
+ $options['noRender'] = (isset($options['noRender'])) ? $options['noRender'] : false;
+ $this->_scriptAction = null;
+ $this->_responseSegment = null;
+
+ // Set options first; may be used to determine other initializations
+ $this->_setOptions($options);
+
+ // Get base view path
+ if (empty($path)) {
+ $path = $this->_getBasePath();
+ if (empty($path)) {
+ /**
+ * @see Zend_Controller_Action_Exception
+ */
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('ViewRenderer initialization failed: retrieved view base path is empty');
+ }
+ }
+
+ if (null === $prefix) {
+ $prefix = $this->_generateDefaultPrefix();
+ }
+
+ // Determine if this path has already been registered
+ $currentPaths = $this->view->getScriptPaths();
+ $path = str_replace(array('/', '\\'), '/', $path);
+ $pathExists = false;
+ foreach ($currentPaths as $tmpPath) {
+ $tmpPath = str_replace(array('/', '\\'), '/', $tmpPath);
+ if (strstr($tmpPath, $path)) {
+ $pathExists = true;
+ break;
+ }
+ }
+ if (!$pathExists) {
+ $this->view->addBasePath($path, $prefix);
+ }
+
+ // Register view with action controller (unless already registered)
+ if ((null !== $this->_actionController) && (null === $this->_actionController->view)) {
+ $this->_actionController->view = $this->view;
+ $this->_actionController->viewSuffix = $this->_viewSuffix;
+ }
+ }
+
+ /**
+ * init - initialize view
+ *
+ * @return void
+ */
+ public function init()
+ {
+ if ($this->getFrontController()->getParam('noViewRenderer')) {
+ return;
+ }
+
+ $this->initView();
+ }
+
+ /**
+ * Set view basePath specification
+ *
+ * Specification can contain one or more of the following:
+ * - :moduleDir - current module directory
+ * - :controller - name of current controller in the request
+ * - :action - name of current action in the request
+ * - :module - name of current module in the request
+ *
+ * @param string $path
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setViewBasePathSpec($path)
+ {
+ $this->_viewBasePathSpec = (string) $path;
+ return $this;
+ }
+
+ /**
+ * Retrieve the current view basePath specification string
+ *
+ * @return string
+ */
+ public function getViewBasePathSpec()
+ {
+ return $this->_viewBasePathSpec;
+ }
+
+ /**
+ * Set view script path specification
+ *
+ * Specification can contain one or more of the following:
+ * - :moduleDir - current module directory
+ * - :controller - name of current controller in the request
+ * - :action - name of current action in the request
+ * - :module - name of current module in the request
+ *
+ * @param string $path
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setViewScriptPathSpec($path)
+ {
+ $this->_viewScriptPathSpec = (string) $path;
+ return $this;
+ }
+
+ /**
+ * Retrieve the current view script path specification string
+ *
+ * @return string
+ */
+ public function getViewScriptPathSpec()
+ {
+ return $this->_viewScriptPathSpec;
+ }
+
+ /**
+ * Set view script path specification (no controller variant)
+ *
+ * Specification can contain one or more of the following:
+ * - :moduleDir - current module directory
+ * - :controller - name of current controller in the request
+ * - :action - name of current action in the request
+ * - :module - name of current module in the request
+ *
+ * :controller will likely be ignored in this variant.
+ *
+ * @param string $path
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setViewScriptPathNoControllerSpec($path)
+ {
+ $this->_viewScriptPathNoControllerSpec = (string) $path;
+ return $this;
+ }
+
+ /**
+ * Retrieve the current view script path specification string (no controller variant)
+ *
+ * @return string
+ */
+ public function getViewScriptPathNoControllerSpec()
+ {
+ return $this->_viewScriptPathNoControllerSpec;
+ }
+
+ /**
+ * Get a view script based on an action and/or other variables
+ *
+ * Uses values found in current request if no values passed in $vars.
+ *
+ * If {@link $_noController} is set, uses {@link $_viewScriptPathNoControllerSpec};
+ * otherwise, uses {@link $_viewScriptPathSpec}.
+ *
+ * @param string $action
+ * @param array $vars
+ * @return string
+ */
+ public function getViewScript($action = null, array $vars = array())
+ {
+ $request = $this->getRequest();
+ if ((null === $action) && (!isset($vars['action']))) {
+ $action = $this->getScriptAction();
+ if (null === $action) {
+ $action = $request->getActionName();
+ }
+ $vars['action'] = $action;
+ } elseif (null !== $action) {
+ $vars['action'] = $action;
+ }
+
+ $replacePattern = array('/[^a-z0-9]+$/i', '/^[^a-z0-9]+/i');
+ $vars['action'] = preg_replace($replacePattern, '', $vars['action']);
+
+ $inflector = $this->getInflector();
+ if ($this->getNoController() || $this->getNeverController()) {
+ $this->_setInflectorTarget($this->getViewScriptPathNoControllerSpec());
+ } else {
+ $this->_setInflectorTarget($this->getViewScriptPathSpec());
+ }
+ return $this->_translateSpec($vars);
+ }
+
+ /**
+ * Set the neverRender flag (i.e., globally dis/enable autorendering)
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setNeverRender($flag = true)
+ {
+ $this->_neverRender = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Retrieve neverRender flag value
+ *
+ * @return boolean
+ */
+ public function getNeverRender()
+ {
+ return $this->_neverRender;
+ }
+
+ /**
+ * Set the noRender flag (i.e., whether or not to autorender)
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setNoRender($flag = true)
+ {
+ $this->_noRender = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Retrieve noRender flag value
+ *
+ * @return boolean
+ */
+ public function getNoRender()
+ {
+ return $this->_noRender;
+ }
+
+ /**
+ * Set the view script to use
+ *
+ * @param string $name
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setScriptAction($name)
+ {
+ $this->_scriptAction = (string) $name;
+ return $this;
+ }
+
+ /**
+ * Retrieve view script name
+ *
+ * @return string
+ */
+ public function getScriptAction()
+ {
+ return $this->_scriptAction;
+ }
+
+ /**
+ * Set the response segment name
+ *
+ * @param string $name
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setResponseSegment($name)
+ {
+ if (null === $name) {
+ $this->_responseSegment = null;
+ } else {
+ $this->_responseSegment = (string) $name;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve named response segment name
+ *
+ * @return string
+ */
+ public function getResponseSegment()
+ {
+ return $this->_responseSegment;
+ }
+
+ /**
+ * Set the noController flag (i.e., whether or not to render into controller subdirectories)
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setNoController($flag = true)
+ {
+ $this->_noController = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Retrieve noController flag value
+ *
+ * @return boolean
+ */
+ public function getNoController()
+ {
+ return $this->_noController;
+ }
+
+ /**
+ * Set the neverController flag (i.e., whether or not to render into controller subdirectories)
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setNeverController($flag = true)
+ {
+ $this->_neverController = ($flag) ? true : false;
+ return $this;
+ }
+
+ /**
+ * Retrieve neverController flag value
+ *
+ * @return boolean
+ */
+ public function getNeverController()
+ {
+ return $this->_neverController;
+ }
+
+ /**
+ * Set view script suffix
+ *
+ * @param string $suffix
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setViewSuffix($suffix)
+ {
+ $this->_viewSuffix = (string) $suffix;
+ return $this;
+ }
+
+ /**
+ * Get view script suffix
+ *
+ * @return string
+ */
+ public function getViewSuffix()
+ {
+ return $this->_viewSuffix;
+ }
+
+ /**
+ * Set options for rendering a view script
+ *
+ * @param string $action View script to render
+ * @param string $name Response named segment to render to
+ * @param boolean $noController Whether or not to render within a subdirectory named after the controller
+ * @return Zend_Controller_Action_Helper_ViewRenderer Provides a fluent interface
+ */
+ public function setRender($action = null, $name = null, $noController = null)
+ {
+ if (null !== $action) {
+ $this->setScriptAction($action);
+ }
+
+ if (null !== $name) {
+ $this->setResponseSegment($name);
+ }
+
+ if (null !== $noController) {
+ $this->setNoController($noController);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Inflect based on provided vars
+ *
+ * Allowed variables are:
+ * - :moduleDir - current module directory
+ * - :module - current module name
+ * - :controller - current controller name
+ * - :action - current action name
+ * - :suffix - view script file suffix
+ *
+ * @param array $vars
+ * @return string
+ */
+ protected function _translateSpec(array $vars = array())
+ {
+ $inflector = $this->getInflector();
+ $request = $this->getRequest();
+ $dispatcher = $this->getFrontController()->getDispatcher();
+ $module = $dispatcher->formatModuleName($request->getModuleName());
+ $controller = $request->getControllerName();
+ $action = $dispatcher->formatActionName($request->getActionName());
+
+ $params = compact('module', 'controller', 'action');
+ foreach ($vars as $key => $value) {
+ switch ($key) {
+ case 'module':
+ case 'controller':
+ case 'action':
+ case 'moduleDir':
+ case 'suffix':
+ $params[$key] = (string) $value;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (isset($params['suffix'])) {
+ $origSuffix = $this->getViewSuffix();
+ $this->setViewSuffix($params['suffix']);
+ }
+ if (isset($params['moduleDir'])) {
+ $origModuleDir = $this->_getModuleDir();
+ $this->_setModuleDir($params['moduleDir']);
+ }
+
+ $filtered = $inflector->filter($params);
+
+ if (isset($params['suffix'])) {
+ $this->setViewSuffix($origSuffix);
+ }
+ if (isset($params['moduleDir'])) {
+ $this->_setModuleDir($origModuleDir);
+ }
+
+ return $filtered;
+ }
+
+ /**
+ * Render a view script (optionally to a named response segment)
+ *
+ * Sets the noRender flag to true when called.
+ *
+ * @param string $script
+ * @param string $name
+ * @return void
+ */
+ public function renderScript($script, $name = null)
+ {
+ if (null === $name) {
+ $name = $this->getResponseSegment();
+ }
+
+ $this->getResponse()->appendBody(
+ $this->view->render($script),
+ $name
+ );
+
+ $this->setNoRender();
+ }
+
+ /**
+ * Render a view based on path specifications
+ *
+ * Renders a view based on the view script path specifications.
+ *
+ * @param string $action
+ * @param string $name
+ * @param boolean $noController
+ * @return void
+ */
+ public function render($action = null, $name = null, $noController = null)
+ {
+ $this->setRender($action, $name, $noController);
+ $path = $this->getViewScript();
+ $this->renderScript($path, $name);
+ }
+
+ /**
+ * Render a script based on specification variables
+ *
+ * Pass an action, and one or more specification variables (view script suffix)
+ * to determine the view script path, and render that script.
+ *
+ * @param string $action
+ * @param array $vars
+ * @param string $name
+ * @return void
+ */
+ public function renderBySpec($action = null, array $vars = array(), $name = null)
+ {
+ if (null !== $name) {
+ $this->setResponseSegment($name);
+ }
+
+ $path = $this->getViewScript($action, $vars);
+
+ $this->renderScript($path);
+ }
+
+ /**
+ * postDispatch - auto render a view
+ *
+ * Only autorenders if:
+ * - _noRender is false
+ * - action controller is present
+ * - request has not been re-dispatched (i.e., _forward() has not been called)
+ * - response is not a redirect
+ *
+ * @return void
+ */
+ public function postDispatch()
+ {
+ if ($this->_shouldRender()) {
+ $this->render();
+ }
+ }
+
+ /**
+ * Should the ViewRenderer render a view script?
+ *
+ * @return boolean
+ */
+ protected function _shouldRender()
+ {
+ return (!$this->getFrontController()->getParam('noViewRenderer')
+ && !$this->_neverRender
+ && !$this->_noRender
+ && (null !== $this->_actionController)
+ && $this->getRequest()->isDispatched()
+ && !$this->getResponse()->isRedirect()
+ );
+ }
+
+ /**
+ * Use this helper as a method; proxies to setRender()
+ *
+ * @param string $action
+ * @param string $name
+ * @param boolean $noController
+ * @return void
+ */
+ public function direct($action = null, $name = null, $noController = null)
+ {
+ $this->setRender($action, $name, $noController);
+ }
+}
diff --git a/library/Zend/Controller/Action/HelperBroker.php b/library/Zend/Controller/Action/HelperBroker.php
new file mode 100644
index 0000000..7c98fd8
--- /dev/null
+++ b/library/Zend/Controller/Action/HelperBroker.php
@@ -0,0 +1,381 @@
+ 'Zend/Controller/Action/Helper/',
+ ));
+ }
+ return self::$_pluginLoader;
+ }
+
+ /**
+ * addPrefix() - Add repository of helpers by prefix
+ *
+ * @param string $prefix
+ */
+ static public function addPrefix($prefix)
+ {
+ $prefix = rtrim($prefix, '_');
+ $path = str_replace('_', DIRECTORY_SEPARATOR, $prefix);
+ self::getPluginLoader()->addPrefixPath($prefix, $path);
+ }
+
+ /**
+ * addPath() - Add path to repositories where Action_Helpers could be found.
+ *
+ * @param string $path
+ * @param string $prefix Optional; defaults to 'Zend_Controller_Action_Helper'
+ * @return void
+ */
+ static public function addPath($path, $prefix = 'Zend_Controller_Action_Helper')
+ {
+ self::getPluginLoader()->addPrefixPath($prefix, $path);
+ }
+
+ /**
+ * addHelper() - Add helper objects
+ *
+ * @param Zend_Controller_Action_Helper_Abstract $helper
+ * @return void
+ */
+ static public function addHelper(Zend_Controller_Action_Helper_Abstract $helper)
+ {
+ self::getStack()->push($helper);
+ return;
+ }
+
+ /**
+ * resetHelpers()
+ *
+ * @return void
+ */
+ static public function resetHelpers()
+ {
+ self::$_stack = null;
+ return;
+ }
+
+ /**
+ * Retrieve or initialize a helper statically
+ *
+ * Retrieves a helper object statically, loading on-demand if the helper
+ * does not already exist in the stack. Always returns a helper, unless
+ * the helper class cannot be found.
+ *
+ * @param string $name
+ * @return Zend_Controller_Action_Helper_Abstract
+ */
+ public static function getStaticHelper($name)
+ {
+ $name = self::_normalizeHelperName($name);
+ $stack = self::getStack();
+
+ if (!isset($stack->{$name})) {
+ self::_loadHelper($name);
+ }
+
+ return $stack->{$name};
+ }
+
+ /**
+ * getExistingHelper() - get helper by name
+ *
+ * Static method to retrieve helper object. Only retrieves helpers already
+ * initialized with the broker (either via addHelper() or on-demand loading
+ * via getHelper()).
+ *
+ * Throws an exception if the referenced helper does not exist in the
+ * stack; use {@link hasHelper()} to check if the helper is registered
+ * prior to retrieving it.
+ *
+ * @param string $name
+ * @return Zend_Controller_Action_Helper_Abstract
+ * @throws Zend_Controller_Action_Exception
+ */
+ public static function getExistingHelper($name)
+ {
+ $name = self::_normalizeHelperName($name);
+ $stack = self::getStack();
+
+ if (!isset($stack->{$name})) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Action helper "' . $name . '" has not been registered with the helper broker');
+ }
+
+ return $stack->{$name};
+ }
+
+ /**
+ * Return all registered helpers as helper => object pairs
+ *
+ * @return array
+ */
+ public static function getExistingHelpers()
+ {
+ return self::getStack()->getHelpersByName();
+ }
+
+ /**
+ * Is a particular helper loaded in the broker?
+ *
+ * @param string $name
+ * @return boolean
+ */
+ public static function hasHelper($name)
+ {
+ $name = self::_normalizeHelperName($name);
+ return isset(self::getStack()->{$name});
+ }
+
+ /**
+ * Remove a particular helper from the broker
+ *
+ * @param string $name
+ * @return boolean
+ */
+ public static function removeHelper($name)
+ {
+ $name = self::_normalizeHelperName($name);
+ $stack = self::getStack();
+ if (isset($stack->{$name})) {
+ unset($stack->{$name});
+ }
+
+ return false;
+ }
+
+ /**
+ * Lazy load the priority stack and return it
+ *
+ * @return Zend_Controller_Action_HelperBroker_PriorityStack
+ */
+ public static function getStack()
+ {
+ if (self::$_stack == null) {
+ self::$_stack = new Zend_Controller_Action_HelperBroker_PriorityStack();
+ }
+
+ return self::$_stack;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param Zend_Controller_Action $actionController
+ * @return void
+ */
+ public function __construct(Zend_Controller_Action $actionController)
+ {
+ $this->_actionController = $actionController;
+ foreach (self::getStack() as $helper) {
+ $helper->setActionController($actionController);
+ $helper->init();
+ }
+ }
+
+ /**
+ * notifyPreDispatch() - called by action controller dispatch method
+ *
+ * @return void
+ */
+ public function notifyPreDispatch()
+ {
+ foreach (self::getStack() as $helper) {
+ $helper->preDispatch();
+ }
+ }
+
+ /**
+ * notifyPostDispatch() - called by action controller dispatch method
+ *
+ * @return void
+ */
+ public function notifyPostDispatch()
+ {
+ foreach (self::getStack() as $helper) {
+ $helper->postDispatch();
+ }
+ }
+
+ /**
+ * getHelper() - get helper by name
+ *
+ * @param string $name
+ * @return Zend_Controller_Action_Helper_Abstract
+ */
+ public function getHelper($name)
+ {
+ $name = self::_normalizeHelperName($name);
+ $stack = self::getStack();
+
+ if (!isset($stack->{$name})) {
+ self::_loadHelper($name);
+ }
+
+ $helper = $stack->{$name};
+
+ $initialize = false;
+ if (null === ($actionController = $helper->getActionController())) {
+ $initialize = true;
+ } elseif ($actionController !== $this->_actionController) {
+ $initialize = true;
+ }
+
+ if ($initialize) {
+ $helper->setActionController($this->_actionController)
+ ->init();
+ }
+
+ return $helper;
+ }
+
+ /**
+ * Method overloading
+ *
+ * @param string $method
+ * @param array $args
+ * @return mixed
+ * @throws Zend_Controller_Action_Exception if helper does not have a direct() method
+ */
+ public function __call($method, $args)
+ {
+ $helper = $this->getHelper($method);
+ if (!method_exists($helper, 'direct')) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Helper "' . $method . '" does not support overloading via direct()');
+ }
+ return call_user_func_array(array($helper, 'direct'), $args);
+ }
+
+ /**
+ * Retrieve helper by name as object property
+ *
+ * @param string $name
+ * @return Zend_Controller_Action_Helper_Abstract
+ */
+ public function __get($name)
+ {
+ return $this->getHelper($name);
+ }
+
+ /**
+ * Normalize helper name for lookups
+ *
+ * @param string $name
+ * @return string
+ */
+ protected static function _normalizeHelperName($name)
+ {
+ if (strpos($name, '_') !== false) {
+ $name = str_replace(' ', '', ucwords(str_replace('_', ' ', $name)));
+ }
+
+ return ucfirst($name);
+ }
+
+ /**
+ * Load a helper
+ *
+ * @param string $name
+ * @return void
+ */
+ protected static function _loadHelper($name)
+ {
+ try {
+ $class = self::getPluginLoader()->load($name);
+ } catch (Zend_Loader_PluginLoader_Exception $e) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Action Helper by name ' . $name . ' not found', 0, $e);
+ }
+
+ $helper = new $class();
+
+ if (!$helper instanceof Zend_Controller_Action_Helper_Abstract) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('Helper name ' . $name . ' -> class ' . $class . ' is not of type Zend_Controller_Action_Helper_Abstract');
+ }
+
+ self::getStack()->push($helper);
+ }
+}
diff --git a/library/Zend/Controller/Action/HelperBroker/PriorityStack.php b/library/Zend/Controller/Action/HelperBroker/PriorityStack.php
new file mode 100644
index 0000000..13d0f0c
--- /dev/null
+++ b/library/Zend/Controller/Action/HelperBroker/PriorityStack.php
@@ -0,0 +1,280 @@
+_helpersByNameRef)) {
+ return false;
+ }
+
+ return $this->_helpersByNameRef[$helperName];
+ }
+
+ /**
+ * Magic property overloading for returning if helper is set by name
+ *
+ * @param string $helperName The helper name
+ * @return Zend_Controller_Action_Helper_Abstract
+ */
+ public function __isset($helperName)
+ {
+ return array_key_exists($helperName, $this->_helpersByNameRef);
+ }
+
+ /**
+ * Magic property overloading for unsetting if helper is exists by name
+ *
+ * @param string $helperName The helper name
+ * @return Zend_Controller_Action_Helper_Abstract
+ */
+ public function __unset($helperName)
+ {
+ return $this->offsetUnset($helperName);
+ }
+
+ /**
+ * push helper onto the stack
+ *
+ * @param Zend_Controller_Action_Helper_Abstract $helper
+ * @return Zend_Controller_Action_HelperBroker_PriorityStack
+ */
+ public function push(Zend_Controller_Action_Helper_Abstract $helper)
+ {
+ $this->offsetSet($this->getNextFreeHigherPriority(), $helper);
+ return $this;
+ }
+
+ /**
+ * Return something iterable
+ *
+ * @return array
+ */
+ public function getIterator()
+ {
+ return new ArrayObject($this->_helpersByPriority);
+ }
+
+ /**
+ * offsetExists()
+ *
+ * @param int|string $priorityOrHelperName
+ * @return Zend_Controller_Action_HelperBroker_PriorityStack
+ */
+ public function offsetExists($priorityOrHelperName)
+ {
+ if (is_string($priorityOrHelperName)) {
+ return array_key_exists($priorityOrHelperName, $this->_helpersByNameRef);
+ } else {
+ return array_key_exists($priorityOrHelperName, $this->_helpersByPriority);
+ }
+ }
+
+ /**
+ * offsetGet()
+ *
+ * @param int|string $priorityOrHelperName
+ * @return Zend_Controller_Action_HelperBroker_PriorityStack
+ */
+ public function offsetGet($priorityOrHelperName)
+ {
+ if (!$this->offsetExists($priorityOrHelperName)) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('A helper with priority ' . $priorityOrHelperName . ' does not exist.');
+ }
+
+ if (is_string($priorityOrHelperName)) {
+ return $this->_helpersByNameRef[$priorityOrHelperName];
+ } else {
+ return $this->_helpersByPriority[$priorityOrHelperName];
+ }
+ }
+
+ /**
+ * offsetSet()
+ *
+ * @param int $priority
+ * @param Zend_Controller_Action_Helper_Abstract $helper
+ * @return Zend_Controller_Action_HelperBroker_PriorityStack
+ */
+ public function offsetSet($priority, $helper)
+ {
+ $priority = (int) $priority;
+
+ if (!$helper instanceof Zend_Controller_Action_Helper_Abstract) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('$helper must extend Zend_Controller_Action_Helper_Abstract.');
+ }
+
+ if (array_key_exists($helper->getName(), $this->_helpersByNameRef)) {
+ // remove any object with the same name to retain BC compailitbility
+ // @todo At ZF 2.0 time throw an exception here.
+ $this->offsetUnset($helper->getName());
+ }
+
+ if (array_key_exists($priority, $this->_helpersByPriority)) {
+ $priority = $this->getNextFreeHigherPriority($priority); // ensures LIFO
+ trigger_error("A helper with the same priority already exists, reassigning to $priority", E_USER_WARNING);
+ }
+
+ $this->_helpersByPriority[$priority] = $helper;
+ $this->_helpersByNameRef[$helper->getName()] = $helper;
+
+ if ($priority == ($nextFreeDefault = $this->getNextFreeHigherPriority($this->_nextDefaultPriority))) {
+ $this->_nextDefaultPriority = $nextFreeDefault;
+ }
+
+ krsort($this->_helpersByPriority); // always make sure priority and LIFO are both enforced
+ return $this;
+ }
+
+ /**
+ * offsetUnset()
+ *
+ * @param int|string $priorityOrHelperName Priority integer or the helper name
+ * @return Zend_Controller_Action_HelperBroker_PriorityStack
+ */
+ public function offsetUnset($priorityOrHelperName)
+ {
+ if (!$this->offsetExists($priorityOrHelperName)) {
+ require_once 'Zend/Controller/Action/Exception.php';
+ throw new Zend_Controller_Action_Exception('A helper with priority or name ' . $priorityOrHelperName . ' does not exist.');
+ }
+
+ if (is_string($priorityOrHelperName)) {
+ $helperName = $priorityOrHelperName;
+ $helper = $this->_helpersByNameRef[$helperName];
+ $priority = array_search($helper, $this->_helpersByPriority, true);
+ } else {
+ $priority = $priorityOrHelperName;
+ $helperName = $this->_helpersByPriority[$priorityOrHelperName]->getName();
+ }
+
+ unset($this->_helpersByNameRef[$helperName]);
+ unset($this->_helpersByPriority[$priority]);
+ return $this;
+ }
+
+ /**
+ * return the count of helpers
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->_helpersByPriority);
+ }
+
+ /**
+ * Find the next free higher priority. If an index is given, it will
+ * find the next free highest priority after it.
+ *
+ * @param int $indexPriority OPTIONAL
+ * @return int
+ */
+ public function getNextFreeHigherPriority($indexPriority = null)
+ {
+ if ($indexPriority == null) {
+ $indexPriority = $this->_nextDefaultPriority;
+ }
+
+ $priorities = array_keys($this->_helpersByPriority);
+
+ while (in_array($indexPriority, $priorities)) {
+ $indexPriority++;
+ }
+
+ return $indexPriority;
+ }
+
+ /**
+ * Find the next free lower priority. If an index is given, it will
+ * find the next free lower priority before it.
+ *
+ * @param int $indexPriority
+ * @return int
+ */
+ public function getNextFreeLowerPriority($indexPriority = null)
+ {
+ if ($indexPriority == null) {
+ $indexPriority = $this->_nextDefaultPriority;
+ }
+
+ $priorities = array_keys($this->_helpersByPriority);
+
+ while (in_array($indexPriority, $priorities)) {
+ $indexPriority--;
+ }
+
+ return $indexPriority;
+ }
+
+ /**
+ * return the highest priority
+ *
+ * @return int
+ */
+ public function getHighestPriority()
+ {
+ return max(array_keys($this->_helpersByPriority));
+ }
+
+ /**
+ * return the lowest priority
+ *
+ * @return int
+ */
+ public function getLowestPriority()
+ {
+ return min(array_keys($this->_helpersByPriority));
+ }
+
+ /**
+ * return the helpers referenced by name
+ *
+ * @return array
+ */
+ public function getHelpersByName()
+ {
+ return $this->_helpersByNameRef;
+ }
+
+}
diff --git a/library/Zend/Controller/Action/Interface.php b/library/Zend/Controller/Action/Interface.php
new file mode 100644
index 0000000..5810c01
--- /dev/null
+++ b/library/Zend/Controller/Action/Interface.php
@@ -0,0 +1,69 @@
+setParams($params);
+ }
+
+ /**
+ * Formats a string into a controller name. This is used to take a raw
+ * controller name, such as one stored inside a Zend_Controller_Request_Abstract
+ * object, and reformat it to a proper class name that a class extending
+ * Zend_Controller_Action would use.
+ *
+ * @param string $unformatted
+ * @return string
+ */
+ public function formatControllerName($unformatted)
+ {
+ return ucfirst($this->_formatName($unformatted)) . 'Controller';
+ }
+
+ /**
+ * Formats a string into an action name. This is used to take a raw
+ * action name, such as one that would be stored inside a Zend_Controller_Request_Abstract
+ * object, and reformat into a proper method name that would be found
+ * inside a class extending Zend_Controller_Action.
+ *
+ * @param string $unformatted
+ * @return string
+ */
+ public function formatActionName($unformatted)
+ {
+ $formatted = $this->_formatName($unformatted, true);
+ return strtolower(substr($formatted, 0, 1)) . substr($formatted, 1) . 'Action';
+ }
+
+ /**
+ * Verify delimiter
+ *
+ * Verify a delimiter to use in controllers or actions. May be a single
+ * string or an array of strings.
+ *
+ * @param string|array $spec
+ * @return array
+ * @throws Zend_Controller_Dispatcher_Exception with invalid delimiters
+ */
+ public function _verifyDelimiter($spec)
+ {
+ if (is_string($spec)) {
+ return (array) $spec;
+ } elseif (is_array($spec)) {
+ $allStrings = true;
+ foreach ($spec as $delim) {
+ if (!is_string($delim)) {
+ $allStrings = false;
+ break;
+ }
+ }
+
+ if (!$allStrings) {
+ require_once 'Zend/Controller/Dispatcher/Exception.php';
+ throw new Zend_Controller_Dispatcher_Exception('Word delimiter array must contain only strings');
+ }
+
+ return $spec;
+ }
+
+ require_once 'Zend/Controller/Dispatcher/Exception.php';
+ throw new Zend_Controller_Dispatcher_Exception('Invalid word delimiter');
+ }
+
+ /**
+ * Retrieve the word delimiter character(s) used in
+ * controller or action names
+ *
+ * @return array
+ */
+ public function getWordDelimiter()
+ {
+ return $this->_wordDelimiter;
+ }
+
+ /**
+ * Set word delimiter
+ *
+ * Set the word delimiter to use in controllers and actions. May be a
+ * single string or an array of strings.
+ *
+ * @param string|array $spec
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setWordDelimiter($spec)
+ {
+ $spec = $this->_verifyDelimiter($spec);
+ $this->_wordDelimiter = $spec;
+
+ return $this;
+ }
+
+ /**
+ * Retrieve the path delimiter character(s) used in
+ * controller names
+ *
+ * @return array
+ */
+ public function getPathDelimiter()
+ {
+ return $this->_pathDelimiter;
+ }
+
+ /**
+ * Set path delimiter
+ *
+ * Set the path delimiter to use in controllers. May be a single string or
+ * an array of strings.
+ *
+ * @param string $spec
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setPathDelimiter($spec)
+ {
+ if (!is_string($spec)) {
+ require_once 'Zend/Controller/Dispatcher/Exception.php';
+ throw new Zend_Controller_Dispatcher_Exception('Invalid path delimiter');
+ }
+ $this->_pathDelimiter = $spec;
+
+ return $this;
+ }
+
+ /**
+ * Formats a string from a URI into a PHP-friendly name.
+ *
+ * By default, replaces words separated by the word separator character(s)
+ * with camelCaps. If $isAction is false, it also preserves replaces words
+ * separated by the path separation character with an underscore, making
+ * the following word Title cased. All non-alphanumeric characters are
+ * removed.
+ *
+ * @param string $unformatted
+ * @param boolean $isAction Defaults to false
+ * @return string
+ */
+ protected function _formatName($unformatted, $isAction = false)
+ {
+ // preserve directories
+ if (!$isAction) {
+ $segments = explode($this->getPathDelimiter(), $unformatted);
+ } else {
+ $segments = (array) $unformatted;
+ }
+
+ foreach ($segments as $key => $segment) {
+ $segment = str_replace($this->getWordDelimiter(), ' ', strtolower($segment));
+ $segment = preg_replace('/[^a-z0-9 ]/', '', $segment);
+ $segments[$key] = str_replace(' ', '', ucwords($segment));
+ }
+
+ return implode('_', $segments);
+ }
+
+ /**
+ * Retrieve front controller instance
+ *
+ * @return Zend_Controller_Front
+ */
+ public function getFrontController()
+ {
+ if (null === $this->_frontController) {
+ require_once 'Zend/Controller/Front.php';
+ $this->_frontController = Zend_Controller_Front::getInstance();
+ }
+
+ return $this->_frontController;
+ }
+
+ /**
+ * Set front controller instance
+ *
+ * @param Zend_Controller_Front $controller
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setFrontController(Zend_Controller_Front $controller)
+ {
+ $this->_frontController = $controller;
+ return $this;
+ }
+
+ /**
+ * Add or modify a parameter to use when instantiating an action controller
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setParam($name, $value)
+ {
+ $name = (string) $name;
+ $this->_invokeParams[$name] = $value;
+ return $this;
+ }
+
+ /**
+ * Set parameters to pass to action controller constructors
+ *
+ * @param array $params
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setParams(array $params)
+ {
+ $this->_invokeParams = array_merge($this->_invokeParams, $params);
+ return $this;
+ }
+
+ /**
+ * Retrieve a single parameter from the controller parameter stack
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function getParam($name)
+ {
+ if(isset($this->_invokeParams[$name])) {
+ return $this->_invokeParams[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve action controller instantiation parameters
+ *
+ * @return array
+ */
+ public function getParams()
+ {
+ return $this->_invokeParams;
+ }
+
+ /**
+ * Clear the controller parameter stack
+ *
+ * By default, clears all parameters. If a parameter name is given, clears
+ * only that parameter; if an array of parameter names is provided, clears
+ * each.
+ *
+ * @param null|string|array single key or array of keys for params to clear
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function clearParams($name = null)
+ {
+ if (null === $name) {
+ $this->_invokeParams = array();
+ } elseif (is_string($name) && isset($this->_invokeParams[$name])) {
+ unset($this->_invokeParams[$name]);
+ } elseif (is_array($name)) {
+ foreach ($name as $key) {
+ if (is_string($key) && isset($this->_invokeParams[$key])) {
+ unset($this->_invokeParams[$key]);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set response object to pass to action controllers
+ *
+ * @param Zend_Controller_Response_Abstract|null $response
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setResponse(Zend_Controller_Response_Abstract $response = null)
+ {
+ $this->_response = $response;
+ return $this;
+ }
+
+ /**
+ * Return the registered response object
+ *
+ * @return Zend_Controller_Response_Abstract|null
+ */
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+
+ /**
+ * Set the default controller (minus any formatting)
+ *
+ * @param string $controller
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setDefaultControllerName($controller)
+ {
+ $this->_defaultController = (string) $controller;
+ return $this;
+ }
+
+ /**
+ * Retrieve the default controller name (minus formatting)
+ *
+ * @return string
+ */
+ public function getDefaultControllerName()
+ {
+ return $this->_defaultController;
+ }
+
+ /**
+ * Set the default action (minus any formatting)
+ *
+ * @param string $action
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setDefaultAction($action)
+ {
+ $this->_defaultAction = (string) $action;
+ return $this;
+ }
+
+ /**
+ * Retrieve the default action name (minus formatting)
+ *
+ * @return string
+ */
+ public function getDefaultAction()
+ {
+ return $this->_defaultAction;
+ }
+
+ /**
+ * Set the default module
+ *
+ * @param string $module
+ * @return Zend_Controller_Dispatcher_Abstract
+ */
+ public function setDefaultModule($module)
+ {
+ $this->_defaultModule = (string) $module;
+ return $this;
+ }
+
+ /**
+ * Retrieve the default module
+ *
+ * @return string
+ */
+ public function getDefaultModule()
+ {
+ return $this->_defaultModule;
+ }
+}
diff --git a/library/Zend/Controller/Dispatcher/Exception.php b/library/Zend/Controller/Dispatcher/Exception.php
new file mode 100644
index 0000000..46f5345
--- /dev/null
+++ b/library/Zend/Controller/Dispatcher/Exception.php
@@ -0,0 +1,37 @@
+_curModule = $this->getDefaultModule();
+ }
+
+ /**
+ * Add a single path to the controller directory stack
+ *
+ * @param string $path
+ * @param string $module
+ * @return Zend_Controller_Dispatcher_Standard
+ */
+ public function addControllerDirectory($path, $module = null)
+ {
+ if (null === $module) {
+ $module = $this->_defaultModule;
+ }
+
+ $module = (string) $module;
+ $path = rtrim((string) $path, '/\\');
+
+ $this->_controllerDirectory[$module] = $path;
+ return $this;
+ }
+
+ /**
+ * Set controller directory
+ *
+ * @param array|string $directory
+ * @return Zend_Controller_Dispatcher_Standard
+ */
+ public function setControllerDirectory($directory, $module = null)
+ {
+ $this->_controllerDirectory = array();
+
+ if (is_string($directory)) {
+ $this->addControllerDirectory($directory, $module);
+ } elseif (is_array($directory)) {
+ foreach ((array) $directory as $module => $path) {
+ $this->addControllerDirectory($path, $module);
+ }
+ } else {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Controller directory spec must be either a string or an array');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return the currently set directories for Zend_Controller_Action class
+ * lookup
+ *
+ * If a module is specified, returns just that directory.
+ *
+ * @param string $module Module name
+ * @return array|string Returns array of all directories by default, single
+ * module directory if module argument provided
+ */
+ public function getControllerDirectory($module = null)
+ {
+ if (null === $module) {
+ return $this->_controllerDirectory;
+ }
+
+ $module = (string) $module;
+ if (array_key_exists($module, $this->_controllerDirectory)) {
+ return $this->_controllerDirectory[$module];
+ }
+
+ return null;
+ }
+
+ /**
+ * Remove a controller directory by module name
+ *
+ * @param string $module
+ * @return bool
+ */
+ public function removeControllerDirectory($module)
+ {
+ $module = (string) $module;
+ if (array_key_exists($module, $this->_controllerDirectory)) {
+ unset($this->_controllerDirectory[$module]);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Format the module name.
+ *
+ * @param string $unformatted
+ * @return string
+ */
+ public function formatModuleName($unformatted)
+ {
+ if (($this->_defaultModule == $unformatted) && !$this->getParam('prefixDefaultModule')) {
+ return $unformatted;
+ }
+
+ return ucfirst($this->_formatName($unformatted));
+ }
+
+ /**
+ * Format action class name
+ *
+ * @param string $moduleName Name of the current module
+ * @param string $className Name of the action class
+ * @return string Formatted class name
+ */
+ public function formatClassName($moduleName, $className)
+ {
+ return $this->formatModuleName($moduleName) . '_' . $className;
+ }
+
+ /**
+ * Convert a class name to a filename
+ *
+ * @param string $class
+ * @return string
+ */
+ public function classToFilename($class)
+ {
+ return str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
+ }
+
+ /**
+ * Returns TRUE if the Zend_Controller_Request_Abstract object can be
+ * dispatched to a controller.
+ *
+ * Use this method wisely. By default, the dispatcher will fall back to the
+ * default controller (either in the module specified or the global default)
+ * if a given controller does not exist. This method returning false does
+ * not necessarily indicate the dispatcher will not still dispatch the call.
+ *
+ * @param Zend_Controller_Request_Abstract $action
+ * @return boolean
+ */
+ public function isDispatchable(Zend_Controller_Request_Abstract $request)
+ {
+ $className = $this->getControllerClass($request);
+ if (!$className) {
+ return false;
+ }
+
+ $finalClass = $className;
+ if (($this->_defaultModule != $this->_curModule)
+ || $this->getParam('prefixDefaultModule'))
+ {
+ $finalClass = $this->formatClassName($this->_curModule, $className);
+ }
+ if (class_exists($finalClass, false)) {
+ return true;
+ }
+
+ $fileSpec = $this->classToFilename($className);
+ $dispatchDir = $this->getDispatchDirectory();
+ $test = $dispatchDir . DIRECTORY_SEPARATOR . $fileSpec;
+ return Zend_Loader::isReadable($test);
+ }
+
+ /**
+ * Dispatch to a controller/action
+ *
+ * By default, if a controller is not dispatchable, dispatch() will throw
+ * an exception. If you wish to use the default controller instead, set the
+ * param 'useDefaultControllerAlways' via {@link setParam()}.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @param Zend_Controller_Response_Abstract $response
+ * @return void
+ * @throws Zend_Controller_Dispatcher_Exception
+ */
+ public function dispatch(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response)
+ {
+ $this->setResponse($response);
+
+ /**
+ * Get controller class
+ */
+ if (!$this->isDispatchable($request)) {
+ $controller = $request->getControllerName();
+ if (!$this->getParam('useDefaultControllerAlways') && !empty($controller)) {
+ require_once 'Zend/Controller/Dispatcher/Exception.php';
+ throw new Zend_Controller_Dispatcher_Exception('Invalid controller specified (' . $request->getControllerName() . ')');
+ }
+
+ $className = $this->getDefaultControllerClass($request);
+ } else {
+ $className = $this->getControllerClass($request);
+ if (!$className) {
+ $className = $this->getDefaultControllerClass($request);
+ }
+ }
+
+ /**
+ * Load the controller class file
+ */
+ $className = $this->loadClass($className);
+
+ /**
+ * Instantiate controller with request, response, and invocation
+ * arguments; throw exception if it's not an action controller
+ */
+ $controller = new $className($request, $this->getResponse(), $this->getParams());
+ if (!($controller instanceof Zend_Controller_Action_Interface) &&
+ !($controller instanceof Zend_Controller_Action)) {
+ require_once 'Zend/Controller/Dispatcher/Exception.php';
+ throw new Zend_Controller_Dispatcher_Exception(
+ 'Controller "' . $className . '" is not an instance of Zend_Controller_Action_Interface'
+ );
+ }
+
+ /**
+ * Retrieve the action name
+ */
+ $action = $this->getActionMethod($request);
+
+ /**
+ * Dispatch the method call
+ */
+ $request->setDispatched(true);
+
+ // by default, buffer output
+ $disableOb = $this->getParam('disableOutputBuffering');
+ $obLevel = ob_get_level();
+ if (empty($disableOb)) {
+ ob_start();
+ }
+
+ try {
+ $controller->dispatch($action);
+ } catch (Exception $e) {
+ // Clean output buffer on error
+ $curObLevel = ob_get_level();
+ if ($curObLevel > $obLevel) {
+ do {
+ ob_get_clean();
+ $curObLevel = ob_get_level();
+ } while ($curObLevel > $obLevel);
+ }
+ throw $e;
+ }
+
+ if (empty($disableOb)) {
+ $content = ob_get_clean();
+ $response->appendBody($content);
+ }
+
+ // Destroy the page controller instance and reflection objects
+ $controller = null;
+ }
+
+ /**
+ * Load a controller class
+ *
+ * Attempts to load the controller class file from
+ * {@link getControllerDirectory()}. If the controller belongs to a
+ * module, looks for the module prefix to the controller class.
+ *
+ * @param string $className
+ * @return string Class name loaded
+ * @throws Zend_Controller_Dispatcher_Exception if class not loaded
+ */
+ public function loadClass($className)
+ {
+ $finalClass = $className;
+ if (($this->_defaultModule != $this->_curModule)
+ || $this->getParam('prefixDefaultModule'))
+ {
+ $finalClass = $this->formatClassName($this->_curModule, $className);
+ }
+ if (class_exists($finalClass, false)) {
+ return $finalClass;
+ }
+
+ $dispatchDir = $this->getDispatchDirectory();
+ $loadFile = $dispatchDir . DIRECTORY_SEPARATOR . $this->classToFilename($className);
+
+ if (Zend_Loader::isReadable($loadFile)) {
+ include_once $loadFile;
+ } else {
+ require_once 'Zend/Controller/Dispatcher/Exception.php';
+ throw new Zend_Controller_Dispatcher_Exception('Cannot load controller class "' . $className . '" from file "' . $loadFile . "'");
+ }
+
+ if (!class_exists($finalClass, false)) {
+ require_once 'Zend/Controller/Dispatcher/Exception.php';
+ throw new Zend_Controller_Dispatcher_Exception('Invalid controller class ("' . $finalClass . '")');
+ }
+
+ return $finalClass;
+ }
+
+ /**
+ * Get controller class name
+ *
+ * Try request first; if not found, try pulling from request parameter;
+ * if still not found, fallback to default
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return string|false Returns class name on success
+ */
+ public function getControllerClass(Zend_Controller_Request_Abstract $request)
+ {
+ $controllerName = $request->getControllerName();
+ if (empty($controllerName)) {
+ if (!$this->getParam('useDefaultControllerAlways')) {
+ return false;
+ }
+ $controllerName = $this->getDefaultControllerName();
+ $request->setControllerName($controllerName);
+ }
+
+ $className = $this->formatControllerName($controllerName);
+
+ $controllerDirs = $this->getControllerDirectory();
+ $module = $request->getModuleName();
+ if ($this->isValidModule($module)) {
+ $this->_curModule = $module;
+ $this->_curDirectory = $controllerDirs[$module];
+ } elseif ($this->isValidModule($this->_defaultModule)) {
+ $request->setModuleName($this->_defaultModule);
+ $this->_curModule = $this->_defaultModule;
+ $this->_curDirectory = $controllerDirs[$this->_defaultModule];
+ } else {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('No default module defined for this application');
+ }
+
+ return $className;
+ }
+
+ /**
+ * Determine if a given module is valid
+ *
+ * @param string $module
+ * @return bool
+ */
+ public function isValidModule($module)
+ {
+ if (!is_string($module)) {
+ return false;
+ }
+
+ $module = strtolower($module);
+ $controllerDir = $this->getControllerDirectory();
+ foreach (array_keys($controllerDir) as $moduleName) {
+ if ($module == strtolower($moduleName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieve default controller class
+ *
+ * Determines whether the default controller to use lies within the
+ * requested module, or if the global default should be used.
+ *
+ * By default, will only use the module default unless that controller does
+ * not exist; if this is the case, it falls back to the default controller
+ * in the default module.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return string
+ */
+ public function getDefaultControllerClass(Zend_Controller_Request_Abstract $request)
+ {
+ $controller = $this->getDefaultControllerName();
+ $default = $this->formatControllerName($controller);
+ $request->setControllerName($controller)
+ ->setActionName(null);
+
+ $module = $request->getModuleName();
+ $controllerDirs = $this->getControllerDirectory();
+ $this->_curModule = $this->_defaultModule;
+ $this->_curDirectory = $controllerDirs[$this->_defaultModule];
+ if ($this->isValidModule($module)) {
+ $found = false;
+ if (class_exists($default, false)) {
+ $found = true;
+ } else {
+ $moduleDir = $controllerDirs[$module];
+ $fileSpec = $moduleDir . DIRECTORY_SEPARATOR . $this->classToFilename($default);
+ if (Zend_Loader::isReadable($fileSpec)) {
+ $found = true;
+ $this->_curDirectory = $moduleDir;
+ }
+ }
+ if ($found) {
+ $request->setModuleName($module);
+ $this->_curModule = $this->formatModuleName($module);
+ }
+ } else {
+ $request->setModuleName($this->_defaultModule);
+ }
+
+ return $default;
+ }
+
+ /**
+ * Return the value of the currently selected dispatch directory (as set by
+ * {@link getController()})
+ *
+ * @return string
+ */
+ public function getDispatchDirectory()
+ {
+ return $this->_curDirectory;
+ }
+
+ /**
+ * Determine the action name
+ *
+ * First attempt to retrieve from request; then from request params
+ * using action key; default to default action
+ *
+ * Returns formatted action name
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return string
+ */
+ public function getActionMethod(Zend_Controller_Request_Abstract $request)
+ {
+ $action = $request->getActionName();
+ if (empty($action)) {
+ $action = $this->getDefaultAction();
+ $request->setActionName($action);
+ }
+
+ return $this->formatActionName($action);
+ }
+}
diff --git a/library/Zend/Controller/Exception.php b/library/Zend/Controller/Exception.php
new file mode 100644
index 0000000..43e981b
--- /dev/null
+++ b/library/Zend/Controller/Exception.php
@@ -0,0 +1,35 @@
+_plugins = new Zend_Controller_Plugin_Broker();
+ }
+
+ /**
+ * Enforce singleton; disallow cloning
+ *
+ * @return void
+ */
+ private function __clone()
+ {
+ }
+
+ /**
+ * Singleton instance
+ *
+ * @return Zend_Controller_Front
+ */
+ public static function getInstance()
+ {
+ if (null === self::$_instance) {
+ self::$_instance = new self();
+ }
+
+ return self::$_instance;
+ }
+
+ /**
+ * Resets all object properties of the singleton instance
+ *
+ * Primarily used for testing; could be used to chain front controllers.
+ *
+ * Also resets action helper broker, clearing all registered helpers.
+ *
+ * @return void
+ */
+ public function resetInstance()
+ {
+ $reflection = new ReflectionObject($this);
+ foreach ($reflection->getProperties() as $property) {
+ $name = $property->getName();
+ switch ($name) {
+ case '_instance':
+ break;
+ case '_controllerDir':
+ case '_invokeParams':
+ $this->{$name} = array();
+ break;
+ case '_plugins':
+ $this->{$name} = new Zend_Controller_Plugin_Broker();
+ break;
+ case '_throwExceptions':
+ case '_returnResponse':
+ $this->{$name} = false;
+ break;
+ case '_moduleControllerDirectoryName':
+ $this->{$name} = 'controllers';
+ break;
+ default:
+ $this->{$name} = null;
+ break;
+ }
+ }
+ Zend_Controller_Action_HelperBroker::resetHelpers();
+ }
+
+ /**
+ * Convenience feature, calls setControllerDirectory()->setRouter()->dispatch()
+ *
+ * In PHP 5.1.x, a call to a static method never populates $this -- so run()
+ * may actually be called after setting up your front controller.
+ *
+ * @param string|array $controllerDirectory Path to Zend_Controller_Action
+ * controller classes or array of such paths
+ * @return void
+ * @throws Zend_Controller_Exception if called from an object instance
+ */
+ public static function run($controllerDirectory)
+ {
+ self::getInstance()
+ ->setControllerDirectory($controllerDirectory)
+ ->dispatch();
+ }
+
+ /**
+ * Add a controller directory to the controller directory stack
+ *
+ * If $args is presented and is a string, uses it for the array key mapping
+ * to the directory specified.
+ *
+ * @param string $directory
+ * @param string $module Optional argument; module with which to associate directory. If none provided, assumes 'default'
+ * @return Zend_Controller_Front
+ * @throws Zend_Controller_Exception if directory not found or readable
+ */
+ public function addControllerDirectory($directory, $module = null)
+ {
+ $this->getDispatcher()->addControllerDirectory($directory, $module);
+ return $this;
+ }
+
+ /**
+ * Set controller directory
+ *
+ * Stores controller directory(ies) in dispatcher. May be an array of
+ * directories or a string containing a single directory.
+ *
+ * @param string|array $directory Path to Zend_Controller_Action controller
+ * classes or array of such paths
+ * @param string $module Optional module name to use with string $directory
+ * @return Zend_Controller_Front
+ */
+ public function setControllerDirectory($directory, $module = null)
+ {
+ $this->getDispatcher()->setControllerDirectory($directory, $module);
+ return $this;
+ }
+
+ /**
+ * Retrieve controller directory
+ *
+ * Retrieves:
+ * - Array of all controller directories if no $name passed
+ * - String path if $name passed and exists as a key in controller directory array
+ * - null if $name passed but does not exist in controller directory keys
+ *
+ * @param string $name Default null
+ * @return array|string|null
+ */
+ public function getControllerDirectory($name = null)
+ {
+ return $this->getDispatcher()->getControllerDirectory($name);
+ }
+
+ /**
+ * Remove a controller directory by module name
+ *
+ * @param string $module
+ * @return bool
+ */
+ public function removeControllerDirectory($module)
+ {
+ return $this->getDispatcher()->removeControllerDirectory($module);
+ }
+
+ /**
+ * Specify a directory as containing modules
+ *
+ * Iterates through the directory, adding any subdirectories as modules;
+ * the subdirectory within each module named after {@link $_moduleControllerDirectoryName}
+ * will be used as the controller directory path.
+ *
+ * @param string $path
+ * @return Zend_Controller_Front
+ */
+ public function addModuleDirectory($path)
+ {
+ try{
+ $dir = new DirectoryIterator($path);
+ } catch(Exception $e) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception("Directory $path not readable", 0, $e);
+ }
+ foreach ($dir as $file) {
+ if ($file->isDot() || !$file->isDir()) {
+ continue;
+ }
+
+ $module = $file->getFilename();
+
+ // Don't use SCCS directories as modules
+ if (preg_match('/^[^a-z]/i', $module) || ('CVS' == $module)) {
+ continue;
+ }
+
+ $moduleDir = $file->getPathname() . DIRECTORY_SEPARATOR . $this->getModuleControllerDirectoryName();
+ $this->addControllerDirectory($moduleDir, $module);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return the path to a module directory (but not the controllers directory within)
+ *
+ * @param string $module
+ * @return string|null
+ */
+ public function getModuleDirectory($module = null)
+ {
+ if (null === $module) {
+ $request = $this->getRequest();
+ if (null !== $request) {
+ $module = $this->getRequest()->getModuleName();
+ }
+ if (empty($module)) {
+ $module = $this->getDispatcher()->getDefaultModule();
+ }
+ }
+
+ $controllerDir = $this->getControllerDirectory($module);
+
+ if ((null === $controllerDir) || !is_string($controllerDir)) {
+ return null;
+ }
+
+ return dirname($controllerDir);
+ }
+
+ /**
+ * Set the directory name within a module containing controllers
+ *
+ * @param string $name
+ * @return Zend_Controller_Front
+ */
+ public function setModuleControllerDirectoryName($name = 'controllers')
+ {
+ $this->_moduleControllerDirectoryName = (string) $name;
+
+ return $this;
+ }
+
+ /**
+ * Return the directory name within a module containing controllers
+ *
+ * @return string
+ */
+ public function getModuleControllerDirectoryName()
+ {
+ return $this->_moduleControllerDirectoryName;
+ }
+
+ /**
+ * Set the default controller (unformatted string)
+ *
+ * @param string $controller
+ * @return Zend_Controller_Front
+ */
+ public function setDefaultControllerName($controller)
+ {
+ $dispatcher = $this->getDispatcher();
+ $dispatcher->setDefaultControllerName($controller);
+ return $this;
+ }
+
+ /**
+ * Retrieve the default controller (unformatted string)
+ *
+ * @return string
+ */
+ public function getDefaultControllerName()
+ {
+ return $this->getDispatcher()->getDefaultControllerName();
+ }
+
+ /**
+ * Set the default action (unformatted string)
+ *
+ * @param string $action
+ * @return Zend_Controller_Front
+ */
+ public function setDefaultAction($action)
+ {
+ $dispatcher = $this->getDispatcher();
+ $dispatcher->setDefaultAction($action);
+ return $this;
+ }
+
+ /**
+ * Retrieve the default action (unformatted string)
+ *
+ * @return string
+ */
+ public function getDefaultAction()
+ {
+ return $this->getDispatcher()->getDefaultAction();
+ }
+
+ /**
+ * Set the default module name
+ *
+ * @param string $module
+ * @return Zend_Controller_Front
+ */
+ public function setDefaultModule($module)
+ {
+ $dispatcher = $this->getDispatcher();
+ $dispatcher->setDefaultModule($module);
+ return $this;
+ }
+
+ /**
+ * Retrieve the default module
+ *
+ * @return string
+ */
+ public function getDefaultModule()
+ {
+ return $this->getDispatcher()->getDefaultModule();
+ }
+
+ /**
+ * Set request class/object
+ *
+ * Set the request object. The request holds the request environment.
+ *
+ * If a class name is provided, it will instantiate it
+ *
+ * @param string|Zend_Controller_Request_Abstract $request
+ * @throws Zend_Controller_Exception if invalid request class
+ * @return Zend_Controller_Front
+ */
+ public function setRequest($request)
+ {
+ if (is_string($request)) {
+ if (!class_exists($request)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($request);
+ }
+ $request = new $request();
+ }
+ if (!$request instanceof Zend_Controller_Request_Abstract) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Invalid request class');
+ }
+
+ $this->_request = $request;
+
+ return $this;
+ }
+
+ /**
+ * Return the request object.
+ *
+ * @return null|Zend_Controller_Request_Abstract
+ */
+ public function getRequest()
+ {
+ return $this->_request;
+ }
+
+ /**
+ * Set router class/object
+ *
+ * Set the router object. The router is responsible for mapping
+ * the request to a controller and action.
+ *
+ * If a class name is provided, instantiates router with any parameters
+ * registered via {@link setParam()} or {@link setParams()}.
+ *
+ * @param string|Zend_Controller_Router_Interface $router
+ * @throws Zend_Controller_Exception if invalid router class
+ * @return Zend_Controller_Front
+ */
+ public function setRouter($router)
+ {
+ if (is_string($router)) {
+ if (!class_exists($router)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($router);
+ }
+ $router = new $router();
+ }
+
+ if (!$router instanceof Zend_Controller_Router_Interface) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Invalid router class');
+ }
+
+ $router->setFrontController($this);
+ $this->_router = $router;
+
+ return $this;
+ }
+
+ /**
+ * Return the router object.
+ *
+ * Instantiates a Zend_Controller_Router_Rewrite object if no router currently set.
+ *
+ * @return Zend_Controller_Router_Interface
+ */
+ public function getRouter()
+ {
+ if (null == $this->_router) {
+ require_once 'Zend/Controller/Router/Rewrite.php';
+ $this->setRouter(new Zend_Controller_Router_Rewrite());
+ }
+
+ return $this->_router;
+ }
+
+ /**
+ * Set the base URL used for requests
+ *
+ * Use to set the base URL segment of the REQUEST_URI to use when
+ * determining PATH_INFO, etc. Examples:
+ * - /admin
+ * - /myapp
+ * - /subdir/index.php
+ *
+ * Note that the URL should not include the full URI. Do not use:
+ * - http://example.com/admin
+ * - http://example.com/myapp
+ * - http://example.com/subdir/index.php
+ *
+ * If a null value is passed, this can be used as well for autodiscovery (default).
+ *
+ * @param string $base
+ * @return Zend_Controller_Front
+ * @throws Zend_Controller_Exception for non-string $base
+ */
+ public function setBaseUrl($base = null)
+ {
+ if (!is_string($base) && (null !== $base)) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Rewrite base must be a string');
+ }
+
+ $this->_baseUrl = $base;
+
+ if ((null !== ($request = $this->getRequest())) && (method_exists($request, 'setBaseUrl'))) {
+ $request->setBaseUrl($base);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve the currently set base URL
+ *
+ * @return string
+ */
+ public function getBaseUrl()
+ {
+ $request = $this->getRequest();
+ if ((null !== $request) && method_exists($request, 'getBaseUrl')) {
+ return $request->getBaseUrl();
+ }
+
+ return $this->_baseUrl;
+ }
+
+ /**
+ * Set the dispatcher object. The dispatcher is responsible for
+ * taking a Zend_Controller_Dispatcher_Token object, instantiating the controller, and
+ * call the action method of the controller.
+ *
+ * @param Zend_Controller_Dispatcher_Interface $dispatcher
+ * @return Zend_Controller_Front
+ */
+ public function setDispatcher(Zend_Controller_Dispatcher_Interface $dispatcher)
+ {
+ $this->_dispatcher = $dispatcher;
+ return $this;
+ }
+
+ /**
+ * Return the dispatcher object.
+ *
+ * @return Zend_Controller_Dispatcher_Interface
+ */
+ public function getDispatcher()
+ {
+ /**
+ * Instantiate the default dispatcher if one was not set.
+ */
+ if (!$this->_dispatcher instanceof Zend_Controller_Dispatcher_Interface) {
+ require_once 'Zend/Controller/Dispatcher/Standard.php';
+ $this->_dispatcher = new Zend_Controller_Dispatcher_Standard();
+ }
+ return $this->_dispatcher;
+ }
+
+ /**
+ * Set response class/object
+ *
+ * Set the response object. The response is a container for action
+ * responses and headers. Usage is optional.
+ *
+ * If a class name is provided, instantiates a response object.
+ *
+ * @param string|Zend_Controller_Response_Abstract $response
+ * @throws Zend_Controller_Exception if invalid response class
+ * @return Zend_Controller_Front
+ */
+ public function setResponse($response)
+ {
+ if (is_string($response)) {
+ if (!class_exists($response)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($response);
+ }
+ $response = new $response();
+ }
+ if (!$response instanceof Zend_Controller_Response_Abstract) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Invalid response class');
+ }
+
+ $this->_response = $response;
+
+ return $this;
+ }
+
+ /**
+ * Return the response object.
+ *
+ * @return null|Zend_Controller_Response_Abstract
+ */
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+
+ /**
+ * Add or modify a parameter to use when instantiating an action controller
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return Zend_Controller_Front
+ */
+ public function setParam($name, $value)
+ {
+ $name = (string) $name;
+ $this->_invokeParams[$name] = $value;
+ return $this;
+ }
+
+ /**
+ * Set parameters to pass to action controller constructors
+ *
+ * @param array $params
+ * @return Zend_Controller_Front
+ */
+ public function setParams(array $params)
+ {
+ $this->_invokeParams = array_merge($this->_invokeParams, $params);
+ return $this;
+ }
+
+ /**
+ * Retrieve a single parameter from the controller parameter stack
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function getParam($name)
+ {
+ if(isset($this->_invokeParams[$name])) {
+ return $this->_invokeParams[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve action controller instantiation parameters
+ *
+ * @return array
+ */
+ public function getParams()
+ {
+ return $this->_invokeParams;
+ }
+
+ /**
+ * Clear the controller parameter stack
+ *
+ * By default, clears all parameters. If a parameter name is given, clears
+ * only that parameter; if an array of parameter names is provided, clears
+ * each.
+ *
+ * @param null|string|array single key or array of keys for params to clear
+ * @return Zend_Controller_Front
+ */
+ public function clearParams($name = null)
+ {
+ if (null === $name) {
+ $this->_invokeParams = array();
+ } elseif (is_string($name) && isset($this->_invokeParams[$name])) {
+ unset($this->_invokeParams[$name]);
+ } elseif (is_array($name)) {
+ foreach ($name as $key) {
+ if (is_string($key) && isset($this->_invokeParams[$key])) {
+ unset($this->_invokeParams[$key]);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Register a plugin.
+ *
+ * @param Zend_Controller_Plugin_Abstract $plugin
+ * @param int $stackIndex Optional; stack index for plugin
+ * @return Zend_Controller_Front
+ */
+ public function registerPlugin(Zend_Controller_Plugin_Abstract $plugin, $stackIndex = null)
+ {
+ $this->_plugins->registerPlugin($plugin, $stackIndex);
+ return $this;
+ }
+
+ /**
+ * Unregister a plugin.
+ *
+ * @param string|Zend_Controller_Plugin_Abstract $plugin Plugin class or object to unregister
+ * @return Zend_Controller_Front
+ */
+ public function unregisterPlugin($plugin)
+ {
+ $this->_plugins->unregisterPlugin($plugin);
+ return $this;
+ }
+
+ /**
+ * Is a particular plugin registered?
+ *
+ * @param string $class
+ * @return bool
+ */
+ public function hasPlugin($class)
+ {
+ return $this->_plugins->hasPlugin($class);
+ }
+
+ /**
+ * Retrieve a plugin or plugins by class
+ *
+ * @param string $class
+ * @return false|Zend_Controller_Plugin_Abstract|array
+ */
+ public function getPlugin($class)
+ {
+ return $this->_plugins->getPlugin($class);
+ }
+
+ /**
+ * Retrieve all plugins
+ *
+ * @return array
+ */
+ public function getPlugins()
+ {
+ return $this->_plugins->getPlugins();
+ }
+
+ /**
+ * Set the throwExceptions flag and retrieve current status
+ *
+ * Set whether exceptions encounted in the dispatch loop should be thrown
+ * or caught and trapped in the response object.
+ *
+ * Default behaviour is to trap them in the response object; call this
+ * method to have them thrown.
+ *
+ * Passing no value will return the current value of the flag; passing a
+ * boolean true or false value will set the flag and return the current
+ * object instance.
+ *
+ * @param boolean $flag Defaults to null (return flag state)
+ * @return boolean|Zend_Controller_Front Used as a setter, returns object; as a getter, returns boolean
+ */
+ public function throwExceptions($flag = null)
+ {
+ if ($flag !== null) {
+ $this->_throwExceptions = (bool) $flag;
+ return $this;
+ }
+
+ return $this->_throwExceptions;
+ }
+
+ /**
+ * Set whether {@link dispatch()} should return the response without first
+ * rendering output. By default, output is rendered and dispatch() returns
+ * nothing.
+ *
+ * @param boolean $flag
+ * @return boolean|Zend_Controller_Front Used as a setter, returns object; as a getter, returns boolean
+ */
+ public function returnResponse($flag = null)
+ {
+ if (true === $flag) {
+ $this->_returnResponse = true;
+ return $this;
+ } elseif (false === $flag) {
+ $this->_returnResponse = false;
+ return $this;
+ }
+
+ return $this->_returnResponse;
+ }
+
+ /**
+ * Dispatch an HTTP request to a controller/action.
+ *
+ * @param Zend_Controller_Request_Abstract|null $request
+ * @param Zend_Controller_Response_Abstract|null $response
+ * @return void|Zend_Controller_Response_Abstract Returns response object if returnResponse() is true
+ */
+ public function dispatch(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null)
+ {
+ if (!$this->getParam('noErrorHandler') && !$this->_plugins->hasPlugin('Zend_Controller_Plugin_ErrorHandler')) {
+ // Register with stack index of 100
+ require_once 'Zend/Controller/Plugin/ErrorHandler.php';
+ $this->_plugins->registerPlugin(new Zend_Controller_Plugin_ErrorHandler(), 100);
+ }
+
+ if (!$this->getParam('noViewRenderer') && !Zend_Controller_Action_HelperBroker::hasHelper('viewRenderer')) {
+ require_once 'Zend/Controller/Action/Helper/ViewRenderer.php';
+ Zend_Controller_Action_HelperBroker::getStack()->offsetSet(-80, new Zend_Controller_Action_Helper_ViewRenderer());
+ }
+
+ /**
+ * Instantiate default request object (HTTP version) if none provided
+ */
+ if (null !== $request) {
+ $this->setRequest($request);
+ } elseif ((null === $request) && (null === ($request = $this->getRequest()))) {
+ require_once 'Zend/Controller/Request/Http.php';
+ $request = new Zend_Controller_Request_Http();
+ $this->setRequest($request);
+ }
+
+ /**
+ * Set base URL of request object, if available
+ */
+ if (is_callable(array($this->_request, 'setBaseUrl'))) {
+ if (null !== $this->_baseUrl) {
+ $this->_request->setBaseUrl($this->_baseUrl);
+ }
+ }
+
+ /**
+ * Instantiate default response object (HTTP version) if none provided
+ */
+ if (null !== $response) {
+ $this->setResponse($response);
+ } elseif ((null === $this->_response) && (null === ($this->_response = $this->getResponse()))) {
+ require_once 'Zend/Controller/Response/Http.php';
+ $response = new Zend_Controller_Response_Http();
+ $this->setResponse($response);
+ }
+
+ /**
+ * Register request and response objects with plugin broker
+ */
+ $this->_plugins
+ ->setRequest($this->_request)
+ ->setResponse($this->_response);
+
+ /**
+ * Initialize router
+ */
+ $router = $this->getRouter();
+ $router->setParams($this->getParams());
+
+ /**
+ * Initialize dispatcher
+ */
+ $dispatcher = $this->getDispatcher();
+ $dispatcher->setParams($this->getParams())
+ ->setResponse($this->_response);
+
+ // Begin dispatch
+ try {
+ /**
+ * Route request to controller/action, if a router is provided
+ */
+
+ /**
+ * Notify plugins of router startup
+ */
+ $this->_plugins->routeStartup($this->_request);
+
+ try {
+ $router->route($this->_request);
+ } catch (Exception $e) {
+ if ($this->throwExceptions()) {
+ throw $e;
+ }
+
+ $this->_response->setException($e);
+ }
+
+ /**
+ * Notify plugins of router completion
+ */
+ $this->_plugins->routeShutdown($this->_request);
+
+ /**
+ * Notify plugins of dispatch loop startup
+ */
+ $this->_plugins->dispatchLoopStartup($this->_request);
+
+ /**
+ * Attempt to dispatch the controller/action. If the $this->_request
+ * indicates that it needs to be dispatched, move to the next
+ * action in the request.
+ */
+ do {
+ $this->_request->setDispatched(true);
+
+ /**
+ * Notify plugins of dispatch startup
+ */
+ $this->_plugins->preDispatch($this->_request);
+
+ /**
+ * Skip requested action if preDispatch() has reset it
+ */
+ if (!$this->_request->isDispatched()) {
+ continue;
+ }
+
+ /**
+ * Dispatch request
+ */
+ try {
+ $dispatcher->dispatch($this->_request, $this->_response);
+ } catch (Exception $e) {
+ if ($this->throwExceptions()) {
+ throw $e;
+ }
+ $this->_response->setException($e);
+ }
+
+ /**
+ * Notify plugins of dispatch completion
+ */
+ $this->_plugins->postDispatch($this->_request);
+ } while (!$this->_request->isDispatched());
+ } catch (Exception $e) {
+ if ($this->throwExceptions()) {
+ throw $e;
+ }
+
+ $this->_response->setException($e);
+ }
+
+ /**
+ * Notify plugins of dispatch loop completion
+ */
+ try {
+ $this->_plugins->dispatchLoopShutdown();
+ } catch (Exception $e) {
+ if ($this->throwExceptions()) {
+ throw $e;
+ }
+
+ $this->_response->setException($e);
+ }
+
+ if ($this->returnResponse()) {
+ return $this->_response;
+ }
+
+ $this->_response->sendResponse();
+ }
+}
diff --git a/library/Zend/Controller/Plugin/Abstract.php b/library/Zend/Controller/Plugin/Abstract.php
new file mode 100644
index 0000000..1e05193
--- /dev/null
+++ b/library/Zend/Controller/Plugin/Abstract.php
@@ -0,0 +1,151 @@
+_request = $request;
+ return $this;
+ }
+
+ /**
+ * Get request object
+ *
+ * @return Zend_Controller_Request_Abstract $request
+ */
+ public function getRequest()
+ {
+ return $this->_request;
+ }
+
+ /**
+ * Set response object
+ *
+ * @param Zend_Controller_Response_Abstract $response
+ * @return Zend_Controller_Plugin_Abstract
+ */
+ public function setResponse(Zend_Controller_Response_Abstract $response)
+ {
+ $this->_response = $response;
+ return $this;
+ }
+
+ /**
+ * Get response object
+ *
+ * @return Zend_Controller_Response_Abstract $response
+ */
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+
+ /**
+ * Called before Zend_Controller_Front begins evaluating the
+ * request against its routes.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function routeStartup(Zend_Controller_Request_Abstract $request)
+ {}
+
+ /**
+ * Called after Zend_Controller_Router exits.
+ *
+ * Called after Zend_Controller_Front exits from the router.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function routeShutdown(Zend_Controller_Request_Abstract $request)
+ {}
+
+ /**
+ * Called before Zend_Controller_Front enters its dispatch loop.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
+ {}
+
+ /**
+ * Called before an action is dispatched by Zend_Controller_Dispatcher.
+ *
+ * This callback allows for proxy or filter behavior. By altering the
+ * request and resetting its dispatched flag (via
+ * {@link Zend_Controller_Request_Abstract::setDispatched() setDispatched(false)}),
+ * the current action may be skipped.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function preDispatch(Zend_Controller_Request_Abstract $request)
+ {}
+
+ /**
+ * Called after an action is dispatched by Zend_Controller_Dispatcher.
+ *
+ * This callback allows for proxy or filter behavior. By altering the
+ * request and resetting its dispatched flag (via
+ * {@link Zend_Controller_Request_Abstract::setDispatched() setDispatched(false)}),
+ * a new action may be specified for dispatching.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function postDispatch(Zend_Controller_Request_Abstract $request)
+ {}
+
+ /**
+ * Called before Zend_Controller_Front exits its dispatch loop.
+ *
+ * @return void
+ */
+ public function dispatchLoopShutdown()
+ {}
+}
diff --git a/library/Zend/Controller/Plugin/ActionStack.php b/library/Zend/Controller/Plugin/ActionStack.php
new file mode 100644
index 0000000..39dd74e
--- /dev/null
+++ b/library/Zend/Controller/Plugin/ActionStack.php
@@ -0,0 +1,280 @@
+setRegistry($registry);
+
+ if (null !== $key) {
+ $this->setRegistryKey($key);
+ } else {
+ $key = $this->getRegistryKey();
+ }
+
+ $registry[$key] = array();
+ }
+
+ /**
+ * Set registry object
+ *
+ * @param Zend_Registry $registry
+ * @return Zend_Controller_Plugin_ActionStack
+ */
+ public function setRegistry(Zend_Registry $registry)
+ {
+ $this->_registry = $registry;
+ return $this;
+ }
+
+ /**
+ * Retrieve registry object
+ *
+ * @return Zend_Registry
+ */
+ public function getRegistry()
+ {
+ return $this->_registry;
+ }
+
+ /**
+ * Retrieve registry key
+ *
+ * @return string
+ */
+ public function getRegistryKey()
+ {
+ return $this->_registryKey;
+ }
+
+ /**
+ * Set registry key
+ *
+ * @param string $key
+ * @return Zend_Controller_Plugin_ActionStack
+ */
+ public function setRegistryKey($key)
+ {
+ $this->_registryKey = (string) $key;
+ return $this;
+ }
+
+ /**
+ * Set clearRequestParams flag
+ *
+ * @param bool $clearRequestParams
+ * @return Zend_Controller_Plugin_ActionStack
+ */
+ public function setClearRequestParams($clearRequestParams)
+ {
+ $this->_clearRequestParams = (bool) $clearRequestParams;
+ return $this;
+ }
+
+ /**
+ * Retrieve clearRequestParams flag
+ *
+ * @return bool
+ */
+ public function getClearRequestParams()
+ {
+ return $this->_clearRequestParams;
+ }
+
+ /**
+ * Retrieve action stack
+ *
+ * @return array
+ */
+ public function getStack()
+ {
+ $registry = $this->getRegistry();
+ $stack = $registry[$this->getRegistryKey()];
+ return $stack;
+ }
+
+ /**
+ * Save stack to registry
+ *
+ * @param array $stack
+ * @return Zend_Controller_Plugin_ActionStack
+ */
+ protected function _saveStack(array $stack)
+ {
+ $registry = $this->getRegistry();
+ $registry[$this->getRegistryKey()] = $stack;
+ return $this;
+ }
+
+ /**
+ * Push an item onto the stack
+ *
+ * @param Zend_Controller_Request_Abstract $next
+ * @return Zend_Controller_Plugin_ActionStack
+ */
+ public function pushStack(Zend_Controller_Request_Abstract $next)
+ {
+ $stack = $this->getStack();
+ array_push($stack, $next);
+ return $this->_saveStack($stack);
+ }
+
+ /**
+ * Pop an item off the action stack
+ *
+ * @return false|Zend_Controller_Request_Abstract
+ */
+ public function popStack()
+ {
+ $stack = $this->getStack();
+ if (0 == count($stack)) {
+ return false;
+ }
+
+ $next = array_pop($stack);
+ $this->_saveStack($stack);
+
+ if (!$next instanceof Zend_Controller_Request_Abstract) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('ArrayStack should only contain request objects');
+ }
+ $action = $next->getActionName();
+ if (empty($action)) {
+ return $this->popStack($stack);
+ }
+
+ $request = $this->getRequest();
+ $controller = $next->getControllerName();
+ if (empty($controller)) {
+ $next->setControllerName($request->getControllerName());
+ }
+
+ $module = $next->getModuleName();
+ if (empty($module)) {
+ $next->setModuleName($request->getModuleName());
+ }
+
+ return $next;
+ }
+
+ /**
+ * postDispatch() plugin hook -- check for actions in stack, and dispatch if any found
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function postDispatch(Zend_Controller_Request_Abstract $request)
+ {
+ // Don't move on to next request if this is already an attempt to
+ // forward
+ if (!$request->isDispatched()) {
+ return;
+ }
+
+ $this->setRequest($request);
+ $stack = $this->getStack();
+ if (empty($stack)) {
+ return;
+ }
+ $next = $this->popStack();
+ if (!$next) {
+ return;
+ }
+
+ $this->forward($next);
+ }
+
+ /**
+ * Forward request with next action
+ *
+ * @param array $next
+ * @return void
+ */
+ public function forward(Zend_Controller_Request_Abstract $next)
+ {
+ $request = $this->getRequest();
+ if ($this->getClearRequestParams()) {
+ $request->clearParams();
+ }
+
+ $request->setModuleName($next->getModuleName())
+ ->setControllerName($next->getControllerName())
+ ->setActionName($next->getActionName())
+ ->setParams($next->getParams())
+ ->setDispatched(false);
+ }
+}
diff --git a/library/Zend/Controller/Plugin/Broker.php b/library/Zend/Controller/Plugin/Broker.php
new file mode 100644
index 0000000..bb2a76a
--- /dev/null
+++ b/library/Zend/Controller/Plugin/Broker.php
@@ -0,0 +1,365 @@
+_plugins, true)) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Plugin already registered');
+ }
+
+ $stackIndex = (int) $stackIndex;
+
+ if ($stackIndex) {
+ if (isset($this->_plugins[$stackIndex])) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Plugin with stackIndex "' . $stackIndex . '" already registered');
+ }
+ $this->_plugins[$stackIndex] = $plugin;
+ } else {
+ $stackIndex = count($this->_plugins);
+ while (isset($this->_plugins[$stackIndex])) {
+ ++$stackIndex;
+ }
+ $this->_plugins[$stackIndex] = $plugin;
+ }
+
+ $request = $this->getRequest();
+ if ($request) {
+ $this->_plugins[$stackIndex]->setRequest($request);
+ }
+ $response = $this->getResponse();
+ if ($response) {
+ $this->_plugins[$stackIndex]->setResponse($response);
+ }
+
+ ksort($this->_plugins);
+
+ return $this;
+ }
+
+ /**
+ * Unregister a plugin.
+ *
+ * @param string|Zend_Controller_Plugin_Abstract $plugin Plugin object or class name
+ * @return Zend_Controller_Plugin_Broker
+ */
+ public function unregisterPlugin($plugin)
+ {
+ if ($plugin instanceof Zend_Controller_Plugin_Abstract) {
+ // Given a plugin object, find it in the array
+ $key = array_search($plugin, $this->_plugins, true);
+ if (false === $key) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Plugin never registered.');
+ }
+ unset($this->_plugins[$key]);
+ } elseif (is_string($plugin)) {
+ // Given a plugin class, find all plugins of that class and unset them
+ foreach ($this->_plugins as $key => $_plugin) {
+ $type = get_class($_plugin);
+ if ($plugin == $type) {
+ unset($this->_plugins[$key]);
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Is a plugin of a particular class registered?
+ *
+ * @param string $class
+ * @return bool
+ */
+ public function hasPlugin($class)
+ {
+ foreach ($this->_plugins as $plugin) {
+ $type = get_class($plugin);
+ if ($class == $type) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieve a plugin or plugins by class
+ *
+ * @param string $class Class name of plugin(s) desired
+ * @return false|Zend_Controller_Plugin_Abstract|array Returns false if none found, plugin if only one found, and array of plugins if multiple plugins of same class found
+ */
+ public function getPlugin($class)
+ {
+ $found = array();
+ foreach ($this->_plugins as $plugin) {
+ $type = get_class($plugin);
+ if ($class == $type) {
+ $found[] = $plugin;
+ }
+ }
+
+ switch (count($found)) {
+ case 0:
+ return false;
+ case 1:
+ return $found[0];
+ default:
+ return $found;
+ }
+ }
+
+ /**
+ * Retrieve all plugins
+ *
+ * @return array
+ */
+ public function getPlugins()
+ {
+ return $this->_plugins;
+ }
+
+ /**
+ * Set request object, and register with each plugin
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return Zend_Controller_Plugin_Broker
+ */
+ public function setRequest(Zend_Controller_Request_Abstract $request)
+ {
+ $this->_request = $request;
+
+ foreach ($this->_plugins as $plugin) {
+ $plugin->setRequest($request);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get request object
+ *
+ * @return Zend_Controller_Request_Abstract $request
+ */
+ public function getRequest()
+ {
+ return $this->_request;
+ }
+
+ /**
+ * Set response object
+ *
+ * @param Zend_Controller_Response_Abstract $response
+ * @return Zend_Controller_Plugin_Broker
+ */
+ public function setResponse(Zend_Controller_Response_Abstract $response)
+ {
+ $this->_response = $response;
+
+ foreach ($this->_plugins as $plugin) {
+ $plugin->setResponse($response);
+ }
+
+
+ return $this;
+ }
+
+ /**
+ * Get response object
+ *
+ * @return Zend_Controller_Response_Abstract $response
+ */
+ public function getResponse()
+ {
+ return $this->_response;
+ }
+
+
+ /**
+ * Called before Zend_Controller_Front begins evaluating the
+ * request against its routes.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function routeStartup(Zend_Controller_Request_Abstract $request)
+ {
+ foreach ($this->_plugins as $plugin) {
+ try {
+ $plugin->routeStartup($request);
+ } catch (Exception $e) {
+ if (Zend_Controller_Front::getInstance()->throwExceptions()) {
+ throw new Zend_Controller_Exception($e->getMessage() . $e->getTraceAsString(), $e->getCode(), $e);
+ } else {
+ $this->getResponse()->setException($e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Called before Zend_Controller_Front exits its iterations over
+ * the route set.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function routeShutdown(Zend_Controller_Request_Abstract $request)
+ {
+ foreach ($this->_plugins as $plugin) {
+ try {
+ $plugin->routeShutdown($request);
+ } catch (Exception $e) {
+ if (Zend_Controller_Front::getInstance()->throwExceptions()) {
+ throw new Zend_Controller_Exception($e->getMessage() . $e->getTraceAsString(), $e->getCode(), $e);
+ } else {
+ $this->getResponse()->setException($e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Called before Zend_Controller_Front enters its dispatch loop.
+ *
+ * During the dispatch loop, Zend_Controller_Front keeps a
+ * Zend_Controller_Request_Abstract object, and uses
+ * Zend_Controller_Dispatcher to dispatch the
+ * Zend_Controller_Request_Abstract object to controllers/actions.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
+ {
+ foreach ($this->_plugins as $plugin) {
+ try {
+ $plugin->dispatchLoopStartup($request);
+ } catch (Exception $e) {
+ if (Zend_Controller_Front::getInstance()->throwExceptions()) {
+ throw new Zend_Controller_Exception($e->getMessage() . $e->getTraceAsString(), $e->getCode(), $e);
+ } else {
+ $this->getResponse()->setException($e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Called before an action is dispatched by Zend_Controller_Dispatcher.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function preDispatch(Zend_Controller_Request_Abstract $request)
+ {
+ foreach ($this->_plugins as $plugin) {
+ try {
+ $plugin->preDispatch($request);
+ } catch (Exception $e) {
+ if (Zend_Controller_Front::getInstance()->throwExceptions()) {
+ throw new Zend_Controller_Exception($e->getMessage() . $e->getTraceAsString(), $e->getCode(), $e);
+ } else {
+ $this->getResponse()->setException($e);
+ // skip rendering of normal dispatch give the error handler a try
+ $this->getRequest()->setDispatched(false);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Called after an action is dispatched by Zend_Controller_Dispatcher.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function postDispatch(Zend_Controller_Request_Abstract $request)
+ {
+ foreach ($this->_plugins as $plugin) {
+ try {
+ $plugin->postDispatch($request);
+ } catch (Exception $e) {
+ if (Zend_Controller_Front::getInstance()->throwExceptions()) {
+ throw new Zend_Controller_Exception($e->getMessage() . $e->getTraceAsString(), $e->getCode(), $e);
+ } else {
+ $this->getResponse()->setException($e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Called before Zend_Controller_Front exits its dispatch loop.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ public function dispatchLoopShutdown()
+ {
+ foreach ($this->_plugins as $plugin) {
+ try {
+ $plugin->dispatchLoopShutdown();
+ } catch (Exception $e) {
+ if (Zend_Controller_Front::getInstance()->throwExceptions()) {
+ throw new Zend_Controller_Exception($e->getMessage() . $e->getTraceAsString(), $e->getCode(), $e);
+ } else {
+ $this->getResponse()->setException($e);
+ }
+ }
+ }
+ }
+}
diff --git a/library/Zend/Controller/Plugin/ErrorHandler.php b/library/Zend/Controller/Plugin/ErrorHandler.php
new file mode 100644
index 0000000..2f462f6
--- /dev/null
+++ b/library/Zend/Controller/Plugin/ErrorHandler.php
@@ -0,0 +1,300 @@
+setErrorHandler($options);
+ }
+
+ /**
+ * setErrorHandler() - setup the error handling options
+ *
+ * @param array $options
+ * @return Zend_Controller_Plugin_ErrorHandler
+ */
+ public function setErrorHandler(Array $options = array())
+ {
+ if (isset($options['module'])) {
+ $this->setErrorHandlerModule($options['module']);
+ }
+ if (isset($options['controller'])) {
+ $this->setErrorHandlerController($options['controller']);
+ }
+ if (isset($options['action'])) {
+ $this->setErrorHandlerAction($options['action']);
+ }
+ return $this;
+ }
+
+ /**
+ * Set the module name for the error handler
+ *
+ * @param string $module
+ * @return Zend_Controller_Plugin_ErrorHandler
+ */
+ public function setErrorHandlerModule($module)
+ {
+ $this->_errorModule = (string) $module;
+ return $this;
+ }
+
+ /**
+ * Retrieve the current error handler module
+ *
+ * @return string
+ */
+ public function getErrorHandlerModule()
+ {
+ if (null === $this->_errorModule) {
+ $this->_errorModule = Zend_Controller_Front::getInstance()->getDispatcher()->getDefaultModule();
+ }
+ return $this->_errorModule;
+ }
+
+ /**
+ * Set the controller name for the error handler
+ *
+ * @param string $controller
+ * @return Zend_Controller_Plugin_ErrorHandler
+ */
+ public function setErrorHandlerController($controller)
+ {
+ $this->_errorController = (string) $controller;
+ return $this;
+ }
+
+ /**
+ * Retrieve the current error handler controller
+ *
+ * @return string
+ */
+ public function getErrorHandlerController()
+ {
+ return $this->_errorController;
+ }
+
+ /**
+ * Set the action name for the error handler
+ *
+ * @param string $action
+ * @return Zend_Controller_Plugin_ErrorHandler
+ */
+ public function setErrorHandlerAction($action)
+ {
+ $this->_errorAction = (string) $action;
+ return $this;
+ }
+
+ /**
+ * Retrieve the current error handler action
+ *
+ * @return string
+ */
+ public function getErrorHandlerAction()
+ {
+ return $this->_errorAction;
+ }
+
+ /**
+ * Route shutdown hook -- Ccheck for router exceptions
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ */
+ public function routeShutdown(Zend_Controller_Request_Abstract $request)
+ {
+ $this->_handleError($request);
+ }
+
+ /**
+ * Pre dispatch hook -- check for exceptions and dispatch error handler if
+ * necessary
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ */
+ public function preDispatch(Zend_Controller_Request_Abstract $request)
+ {
+ $this->_handleError($request);
+ }
+
+ /**
+ * Post dispatch hook -- check for exceptions and dispatch error handler if
+ * necessary
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ */
+ public function postDispatch(Zend_Controller_Request_Abstract $request)
+ {
+ $this->_handleError($request);
+ }
+
+ /**
+ * Handle errors and exceptions
+ *
+ * If the 'noErrorHandler' front controller flag has been set,
+ * returns early.
+ *
+ * @param Zend_Controller_Request_Abstract $request
+ * @return void
+ */
+ protected function _handleError(Zend_Controller_Request_Abstract $request)
+ {
+ $frontController = Zend_Controller_Front::getInstance();
+ if ($frontController->getParam('noErrorHandler')) {
+ return;
+ }
+
+ $response = $this->getResponse();
+
+ if ($this->_isInsideErrorHandlerLoop) {
+ $exceptions = $response->getException();
+ if (count($exceptions) > $this->_exceptionCountAtFirstEncounter) {
+ // Exception thrown by error handler; tell the front controller to throw it
+ $frontController->throwExceptions(true);
+ throw array_pop($exceptions);
+ }
+ }
+
+ // check for an exception AND allow the error handler controller the option to forward
+ if (($response->isException()) && (!$this->_isInsideErrorHandlerLoop)) {
+ $this->_isInsideErrorHandlerLoop = true;
+
+ // Get exception information
+ $error = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS);
+ $exceptions = $response->getException();
+ $exception = $exceptions[0];
+ $exceptionType = get_class($exception);
+ $error->exception = $exception;
+ switch ($exceptionType) {
+ case 'Zend_Controller_Router_Exception':
+ if (404 == $exception->getCode()) {
+ $error->type = self::EXCEPTION_NO_ROUTE;
+ } else {
+ $error->type = self::EXCEPTION_OTHER;
+ }
+ break;
+ case 'Zend_Controller_Dispatcher_Exception':
+ $error->type = self::EXCEPTION_NO_CONTROLLER;
+ break;
+ case 'Zend_Controller_Action_Exception':
+ if (404 == $exception->getCode()) {
+ $error->type = self::EXCEPTION_NO_ACTION;
+ } else {
+ $error->type = self::EXCEPTION_OTHER;
+ }
+ break;
+ default:
+ $error->type = self::EXCEPTION_OTHER;
+ break;
+ }
+
+ // Keep a copy of the original request
+ $error->request = clone $request;
+
+ // get a count of the number of exceptions encountered
+ $this->_exceptionCountAtFirstEncounter = count($exceptions);
+
+ // Forward to the error handler
+ $request->setParam('error_handler', $error)
+ ->setModuleName($this->getErrorHandlerModule())
+ ->setControllerName($this->getErrorHandlerController())
+ ->setActionName($this->getErrorHandlerAction())
+ ->setDispatched(false);
+ }
+ }
+}
diff --git a/library/Zend/Controller/Plugin/PutHandler.php b/library/Zend/Controller/Plugin/PutHandler.php
new file mode 100644
index 0000000..1f749a1
--- /dev/null
+++ b/library/Zend/Controller/Plugin/PutHandler.php
@@ -0,0 +1,60 @@
+_request->isPut()) {
+ $putParams = array();
+ parse_str($this->_request->getRawBody(), $putParams);
+ $request->setParams($putParams);
+ }
+ }
+}
diff --git a/library/Zend/Controller/Request/Abstract.php b/library/Zend/Controller/Request/Abstract.php
new file mode 100644
index 0000000..9a8e057
--- /dev/null
+++ b/library/Zend/Controller/Request/Abstract.php
@@ -0,0 +1,356 @@
+_module) {
+ $this->_module = $this->getParam($this->getModuleKey());
+ }
+
+ return $this->_module;
+ }
+
+ /**
+ * Set the module name to use
+ *
+ * @param string $value
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setModuleName($value)
+ {
+ $this->_module = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve the controller name
+ *
+ * @return string
+ */
+ public function getControllerName()
+ {
+ if (null === $this->_controller) {
+ $this->_controller = $this->getParam($this->getControllerKey());
+ }
+
+ return $this->_controller;
+ }
+
+ /**
+ * Set the controller name to use
+ *
+ * @param string $value
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setControllerName($value)
+ {
+ $this->_controller = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve the action name
+ *
+ * @return string
+ */
+ public function getActionName()
+ {
+ if (null === $this->_action) {
+ $this->_action = $this->getParam($this->getActionKey());
+ }
+
+ return $this->_action;
+ }
+
+ /**
+ * Set the action name
+ *
+ * @param string $value
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setActionName($value)
+ {
+ $this->_action = $value;
+ /**
+ * @see ZF-3465
+ */
+ if (null === $value) {
+ $this->setParam($this->getActionKey(), $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve the module key
+ *
+ * @return string
+ */
+ public function getModuleKey()
+ {
+ return $this->_moduleKey;
+ }
+
+ /**
+ * Set the module key
+ *
+ * @param string $key
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setModuleKey($key)
+ {
+ $this->_moduleKey = (string) $key;
+ return $this;
+ }
+
+ /**
+ * Retrieve the controller key
+ *
+ * @return string
+ */
+ public function getControllerKey()
+ {
+ return $this->_controllerKey;
+ }
+
+ /**
+ * Set the controller key
+ *
+ * @param string $key
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setControllerKey($key)
+ {
+ $this->_controllerKey = (string) $key;
+ return $this;
+ }
+
+ /**
+ * Retrieve the action key
+ *
+ * @return string
+ */
+ public function getActionKey()
+ {
+ return $this->_actionKey;
+ }
+
+ /**
+ * Set the action key
+ *
+ * @param string $key
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setActionKey($key)
+ {
+ $this->_actionKey = (string) $key;
+ return $this;
+ }
+
+ /**
+ * Get an action parameter
+ *
+ * @param string $key
+ * @param mixed $default Default value to use if key not found
+ * @return mixed
+ */
+ public function getParam($key, $default = null)
+ {
+ $key = (string) $key;
+ if (isset($this->_params[$key])) {
+ return $this->_params[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Retrieve only user params (i.e, any param specific to the object and not the environment)
+ *
+ * @return array
+ */
+ public function getUserParams()
+ {
+ return $this->_params;
+ }
+
+ /**
+ * Retrieve a single user param (i.e, a param specific to the object and not the environment)
+ *
+ * @param string $key
+ * @param string $default Default value to use if key not found
+ * @return mixed
+ */
+ public function getUserParam($key, $default = null)
+ {
+ if (isset($this->_params[$key])) {
+ return $this->_params[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Set an action parameter
+ *
+ * A $value of null will unset the $key if it exists
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setParam($key, $value)
+ {
+ $key = (string) $key;
+
+ if ((null === $value) && isset($this->_params[$key])) {
+ unset($this->_params[$key]);
+ } elseif (null !== $value) {
+ $this->_params[$key] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get all action parameters
+ *
+ * @return array
+ */
+ public function getParams()
+ {
+ return $this->_params;
+ }
+
+ /**
+ * Set action parameters en masse; does not overwrite
+ *
+ * Null values will unset the associated key.
+ *
+ * @param array $array
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setParams(array $array)
+ {
+ $this->_params = $this->_params + (array) $array;
+
+ foreach ($this->_params as $key => $value) {
+ if (null === $value) {
+ unset($this->_params[$key]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Unset all user parameters
+ *
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function clearParams()
+ {
+ $this->_params = array();
+ return $this;
+ }
+
+ /**
+ * Set flag indicating whether or not request has been dispatched
+ *
+ * @param boolean $flag
+ * @return Zend_Controller_Request_Abstract
+ */
+ public function setDispatched($flag = true)
+ {
+ $this->_dispatched = $flag ? true : false;
+ return $this;
+ }
+
+ /**
+ * Determine if the request has been dispatched
+ *
+ * @return boolean
+ */
+ public function isDispatched()
+ {
+ return $this->_dispatched;
+ }
+}
diff --git a/library/Zend/Controller/Request/Apache404.php b/library/Zend/Controller/Request/Apache404.php
new file mode 100644
index 0000000..11a5f64
--- /dev/null
+++ b/library/Zend/Controller/Request/Apache404.php
@@ -0,0 +1,82 @@
+_requestUri = $requestUri;
+ return $this;
+ }
+}
diff --git a/library/Zend/Controller/Request/Exception.php b/library/Zend/Controller/Request/Exception.php
new file mode 100644
index 0000000..217b49b
--- /dev/null
+++ b/library/Zend/Controller/Request/Exception.php
@@ -0,0 +1,37 @@
+valid()) {
+ $path = $uri->getPath();
+ $query = $uri->getQuery();
+ if (!empty($query)) {
+ $path .= '?' . $query;
+ }
+
+ $this->setRequestUri($path);
+ } else {
+ require_once 'Zend/Controller/Request/Exception.php';
+ throw new Zend_Controller_Request_Exception('Invalid URI provided to constructor');
+ }
+ } else {
+ $this->setRequestUri();
+ }
+ }
+
+ /**
+ * Access values contained in the superglobals as public members
+ * Order of precedence: 1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV
+ *
+ * @see http://msdn.microsoft.com/en-us/library/system.web.httprequest.item.aspx
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ switch (true) {
+ case isset($this->_params[$key]):
+ return $this->_params[$key];
+ case isset($_GET[$key]):
+ return $_GET[$key];
+ case isset($_POST[$key]):
+ return $_POST[$key];
+ case isset($_COOKIE[$key]):
+ return $_COOKIE[$key];
+ case ($key == 'REQUEST_URI'):
+ return $this->getRequestUri();
+ case ($key == 'PATH_INFO'):
+ return $this->getPathInfo();
+ case isset($_SERVER[$key]):
+ return $_SERVER[$key];
+ case isset($_ENV[$key]):
+ return $_ENV[$key];
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Alias to __get
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ return $this->__get($key);
+ }
+
+ /**
+ * Set values
+ *
+ * In order to follow {@link __get()}, which operates on a number of
+ * superglobals, setting values through overloading is not allowed and will
+ * raise an exception. Use setParam() instead.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ * @throws Zend_Controller_Request_Exception
+ */
+ public function __set($key, $value)
+ {
+ require_once 'Zend/Controller/Request/Exception.php';
+ throw new Zend_Controller_Request_Exception('Setting values in superglobals not allowed; please use setParam()');
+ }
+
+ /**
+ * Alias to __set()
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ */
+ public function set($key, $value)
+ {
+ return $this->__set($key, $value);
+ }
+
+ /**
+ * Check to see if a property is set
+ *
+ * @param string $key
+ * @return boolean
+ */
+ public function __isset($key)
+ {
+ switch (true) {
+ case isset($this->_params[$key]):
+ return true;
+ case isset($_GET[$key]):
+ return true;
+ case isset($_POST[$key]):
+ return true;
+ case isset($_COOKIE[$key]):
+ return true;
+ case isset($_SERVER[$key]):
+ return true;
+ case isset($_ENV[$key]):
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Alias to __isset()
+ *
+ * @param string $key
+ * @return boolean
+ */
+ public function has($key)
+ {
+ return $this->__isset($key);
+ }
+
+ /**
+ * Set GET values
+ *
+ * @param string|array $spec
+ * @param null|mixed $value
+ * @return Zend_Controller_Request_Http
+ */
+ public function setQuery($spec, $value = null)
+ {
+ if ((null === $value) && !is_array($spec)) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Invalid value passed to setQuery(); must be either array of values or key/value pair');
+ }
+ if ((null === $value) && is_array($spec)) {
+ foreach ($spec as $key => $value) {
+ $this->setQuery($key, $value);
+ }
+ return $this;
+ }
+ $_GET[(string) $spec] = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve a member of the $_GET superglobal
+ *
+ * If no $key is passed, returns the entire $_GET array.
+ *
+ * @todo How to retrieve from nested arrays
+ * @param string $key
+ * @param mixed $default Default value to use if key not found
+ * @return mixed Returns null if key does not exist
+ */
+ public function getQuery($key = null, $default = null)
+ {
+ if (null === $key) {
+ return $_GET;
+ }
+
+ return (isset($_GET[$key])) ? $_GET[$key] : $default;
+ }
+
+ /**
+ * Set POST values
+ *
+ * @param string|array $spec
+ * @param null|mixed $value
+ * @return Zend_Controller_Request_Http
+ */
+ public function setPost($spec, $value = null)
+ {
+ if ((null === $value) && !is_array($spec)) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Invalid value passed to setPost(); must be either array of values or key/value pair');
+ }
+ if ((null === $value) && is_array($spec)) {
+ foreach ($spec as $key => $value) {
+ $this->setPost($key, $value);
+ }
+ return $this;
+ }
+ $_POST[(string) $spec] = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve a member of the $_POST superglobal
+ *
+ * If no $key is passed, returns the entire $_POST array.
+ *
+ * @todo How to retrieve from nested arrays
+ * @param string $key
+ * @param mixed $default Default value to use if key not found
+ * @return mixed Returns null if key does not exist
+ */
+ public function getPost($key = null, $default = null)
+ {
+ if (null === $key) {
+ return $_POST;
+ }
+
+ return (isset($_POST[$key])) ? $_POST[$key] : $default;
+ }
+
+ /**
+ * Retrieve a member of the $_COOKIE superglobal
+ *
+ * If no $key is passed, returns the entire $_COOKIE array.
+ *
+ * @todo How to retrieve from nested arrays
+ * @param string $key
+ * @param mixed $default Default value to use if key not found
+ * @return mixed Returns null if key does not exist
+ */
+ public function getCookie($key = null, $default = null)
+ {
+ if (null === $key) {
+ return $_COOKIE;
+ }
+
+ return (isset($_COOKIE[$key])) ? $_COOKIE[$key] : $default;
+ }
+
+ /**
+ * Retrieve a member of the $_SERVER superglobal
+ *
+ * If no $key is passed, returns the entire $_SERVER array.
+ *
+ * @param string $key
+ * @param mixed $default Default value to use if key not found
+ * @return mixed Returns null if key does not exist
+ */
+ public function getServer($key = null, $default = null)
+ {
+ if (null === $key) {
+ return $_SERVER;
+ }
+
+ return (isset($_SERVER[$key])) ? $_SERVER[$key] : $default;
+ }
+
+ /**
+ * Retrieve a member of the $_ENV superglobal
+ *
+ * If no $key is passed, returns the entire $_ENV array.
+ *
+ * @param string $key
+ * @param mixed $default Default value to use if key not found
+ * @return mixed Returns null if key does not exist
+ */
+ public function getEnv($key = null, $default = null)
+ {
+ if (null === $key) {
+ return $_ENV;
+ }
+
+ return (isset($_ENV[$key])) ? $_ENV[$key] : $default;
+ }
+
+ /**
+ * Set the REQUEST_URI on which the instance operates
+ *
+ * If no request URI is passed, uses the value in $_SERVER['REQUEST_URI'],
+ * $_SERVER['HTTP_X_REWRITE_URL'], or $_SERVER['ORIG_PATH_INFO'] + $_SERVER['QUERY_STRING'].
+ *
+ * @param string $requestUri
+ * @return Zend_Controller_Request_Http
+ */
+ public function setRequestUri($requestUri = null)
+ {
+ if ($requestUri === null) {
+ if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch
+ $requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
+ } elseif (
+ // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem)
+ isset($_SERVER['IIS_WasUrlRewritten'])
+ && $_SERVER['IIS_WasUrlRewritten'] == '1'
+ && isset($_SERVER['UNENCODED_URL'])
+ && $_SERVER['UNENCODED_URL'] != ''
+ ) {
+ $requestUri = $_SERVER['UNENCODED_URL'];
+ } elseif (isset($_SERVER['REQUEST_URI'])) {
+ $requestUri = $_SERVER['REQUEST_URI'];
+ // Http proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path
+ $schemeAndHttpHost = $this->getScheme() . '://' . $this->getHttpHost();
+ if (strpos($requestUri, $schemeAndHttpHost) === 0) {
+ $requestUri = substr($requestUri, strlen($schemeAndHttpHost));
+ }
+ } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI
+ $requestUri = $_SERVER['ORIG_PATH_INFO'];
+ if (!empty($_SERVER['QUERY_STRING'])) {
+ $requestUri .= '?' . $_SERVER['QUERY_STRING'];
+ }
+ } else {
+ return $this;
+ }
+ } elseif (!is_string($requestUri)) {
+ return $this;
+ } else {
+ // Set GET items, if available
+ if (false !== ($pos = strpos($requestUri, '?'))) {
+ // Get key => value pairs and set $_GET
+ $query = substr($requestUri, $pos + 1);
+ parse_str($query, $vars);
+ $this->setQuery($vars);
+ }
+ }
+
+ $this->_requestUri = $requestUri;
+ return $this;
+ }
+
+ /**
+ * Returns the REQUEST_URI taking into account
+ * platform differences between Apache and IIS
+ *
+ * @return string
+ */
+ public function getRequestUri()
+ {
+ if (empty($this->_requestUri)) {
+ $this->setRequestUri();
+ }
+
+ return $this->_requestUri;
+ }
+
+ /**
+ * Set the base URL of the request; i.e., the segment leading to the script name
+ *
+ * E.g.:
+ * - /admin
+ * - /myapp
+ * - /subdir/index.php
+ *
+ * Do not use the full URI when providing the base. The following are
+ * examples of what not to use:
+ * - http://example.com/admin (should be just /admin)
+ * - http://example.com/subdir/index.php (should be just /subdir/index.php)
+ *
+ * If no $baseUrl is provided, attempts to determine the base URL from the
+ * environment, using SCRIPT_FILENAME, SCRIPT_NAME, PHP_SELF, and
+ * ORIG_SCRIPT_NAME in its determination.
+ *
+ * @param mixed $baseUrl
+ * @return Zend_Controller_Request_Http
+ */
+ public function setBaseUrl($baseUrl = null)
+ {
+ if ((null !== $baseUrl) && !is_string($baseUrl)) {
+ return $this;
+ }
+
+ if ($baseUrl === null) {
+ $filename = (isset($_SERVER['SCRIPT_FILENAME'])) ? basename($_SERVER['SCRIPT_FILENAME']) : '';
+
+ if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $filename) {
+ $baseUrl = $_SERVER['SCRIPT_NAME'];
+ } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $filename) {
+ $baseUrl = $_SERVER['PHP_SELF'];
+ } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
+ $baseUrl = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
+ } else {
+ // Backtrack up the script_filename to find the portion matching
+ // php_self
+ $path = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '';
+ $file = isset($_SERVER['SCRIPT_FILENAME']) ? $_SERVER['SCRIPT_FILENAME'] : '';
+ $segs = explode('/', trim($file, '/'));
+ $segs = array_reverse($segs);
+ $index = 0;
+ $last = count($segs);
+ $baseUrl = '';
+ do {
+ $seg = $segs[$index];
+ $baseUrl = '/' . $seg . $baseUrl;
+ ++$index;
+ } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
+ }
+
+ // Does the baseUrl have anything in common with the request_uri?
+ $requestUri = $this->getRequestUri();
+
+ if (0 === strpos($requestUri, $baseUrl)) {
+ // full $baseUrl matches
+ $this->_baseUrl = $baseUrl;
+ return $this;
+ }
+
+ if (0 === strpos($requestUri, dirname($baseUrl))) {
+ // directory portion of $baseUrl matches
+ $this->_baseUrl = rtrim(dirname($baseUrl), '/');
+ return $this;
+ }
+
+ $truncatedRequestUri = $requestUri;
+ if (($pos = strpos($requestUri, '?')) !== false) {
+ $truncatedRequestUri = substr($requestUri, 0, $pos);
+ }
+
+ $basename = basename($baseUrl);
+ if (empty($basename) || !strpos($truncatedRequestUri, $basename)) {
+ // no match whatsoever; set it blank
+ $this->_baseUrl = '';
+ return $this;
+ }
+
+ // If using mod_rewrite or ISAPI_Rewrite strip the script filename
+ // out of baseUrl. $pos !== 0 makes sure it is not matching a value
+ // from PATH_INFO or QUERY_STRING
+ if ((strlen($requestUri) >= strlen($baseUrl))
+ && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0)))
+ {
+ $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
+ }
+ }
+
+ $this->_baseUrl = rtrim($baseUrl, '/');
+ return $this;
+ }
+
+ /**
+ * Everything in REQUEST_URI before PATH_INFO
+ *
+ *
+ * @return string
+ */
+ public function getBaseUrl($raw = false)
+ {
+ if (null === $this->_baseUrl) {
+ $this->setBaseUrl();
+ }
+
+ return (($raw == false) ? urldecode($this->_baseUrl) : $this->_baseUrl);
+ }
+
+ /**
+ * Set the base path for the URL
+ *
+ * @param string|null $basePath
+ * @return Zend_Controller_Request_Http
+ */
+ public function setBasePath($basePath = null)
+ {
+ if ($basePath === null) {
+ $filename = (isset($_SERVER['SCRIPT_FILENAME']))
+ ? basename($_SERVER['SCRIPT_FILENAME'])
+ : '';
+
+ $baseUrl = $this->getBaseUrl();
+ if (empty($baseUrl)) {
+ $this->_basePath = '';
+ return $this;
+ }
+
+ if (basename($baseUrl) === $filename) {
+ $basePath = dirname($baseUrl);
+ } else {
+ $basePath = $baseUrl;
+ }
+ }
+
+ if (substr(PHP_OS, 0, 3) === 'WIN') {
+ $basePath = str_replace('\\', '/', $basePath);
+ }
+
+ $this->_basePath = rtrim($basePath, '/');
+ return $this;
+ }
+
+ /**
+ * Everything in REQUEST_URI before PATH_INFO not including the filename
+ *
+ *
+ * @return string
+ */
+ public function getBasePath()
+ {
+ if (null === $this->_basePath) {
+ $this->setBasePath();
+ }
+
+ return $this->_basePath;
+ }
+
+ /**
+ * Set the PATH_INFO string
+ *
+ * @param string|null $pathInfo
+ * @return Zend_Controller_Request_Http
+ */
+ public function setPathInfo($pathInfo = null)
+ {
+ if ($pathInfo === null) {
+ $baseUrl = $this->getBaseUrl(); // this actually calls setBaseUrl() & setRequestUri()
+ $baseUrlRaw = $this->getBaseUrl(false);
+ $baseUrlEncoded = urlencode($baseUrlRaw);
+
+ if (null === ($requestUri = $this->getRequestUri())) {
+ return $this;
+ }
+
+ // Remove the query string from REQUEST_URI
+ if ($pos = strpos($requestUri, '?')) {
+ $requestUri = substr($requestUri, 0, $pos);
+ }
+
+ if (!empty($baseUrl) || !empty($baseUrlRaw)) {
+ if (strpos($requestUri, $baseUrl) === 0) {
+ $pathInfo = substr($requestUri, strlen($baseUrl));
+ } elseif (strpos($requestUri, $baseUrlRaw) === 0) {
+ $pathInfo = substr($requestUri, strlen($baseUrlRaw));
+ } elseif (strpos($requestUri, $baseUrlEncoded) === 0) {
+ $pathInfo = substr($requestUri, strlen($baseUrlEncoded));
+ } else {
+ $pathInfo = $requestUri;
+ }
+ } else {
+ $pathInfo = $requestUri;
+ }
+
+ }
+
+ $this->_pathInfo = (string) $pathInfo;
+ return $this;
+ }
+
+ /**
+ * Returns everything between the BaseUrl and QueryString.
+ * This value is calculated instead of reading PATH_INFO
+ * directly from $_SERVER due to cross-platform differences.
+ *
+ * @return string
+ */
+ public function getPathInfo()
+ {
+ if (empty($this->_pathInfo)) {
+ $this->setPathInfo();
+ }
+
+ return $this->_pathInfo;
+ }
+
+ /**
+ * Set allowed parameter sources
+ *
+ * Can be empty array, or contain one or more of '_GET' or '_POST'.
+ *
+ * @param array $paramSoures
+ * @return Zend_Controller_Request_Http
+ */
+ public function setParamSources(array $paramSources = array())
+ {
+ $this->_paramSources = $paramSources;
+ return $this;
+ }
+
+ /**
+ * Get list of allowed parameter sources
+ *
+ * @return array
+ */
+ public function getParamSources()
+ {
+ return $this->_paramSources;
+ }
+
+ /**
+ * Set a userland parameter
+ *
+ * Uses $key to set a userland parameter. If $key is an alias, the actual
+ * key will be retrieved and used to set the parameter.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return Zend_Controller_Request_Http
+ */
+ public function setParam($key, $value)
+ {
+ $key = (null !== ($alias = $this->getAlias($key))) ? $alias : $key;
+ parent::setParam($key, $value);
+ return $this;
+ }
+
+ /**
+ * Retrieve a parameter
+ *
+ * Retrieves a parameter from the instance. Priority is in the order of
+ * userland parameters (see {@link setParam()}), $_GET, $_POST. If a
+ * parameter matching the $key is not found, null is returned.
+ *
+ * If the $key is an alias, the actual key aliased will be used.
+ *
+ * @param mixed $key
+ * @param mixed $default Default value to use if key not found
+ * @return mixed
+ */
+ public function getParam($key, $default = null)
+ {
+ $keyName = (null !== ($alias = $this->getAlias($key))) ? $alias : $key;
+
+ $paramSources = $this->getParamSources();
+ if (isset($this->_params[$keyName])) {
+ return $this->_params[$keyName];
+ } elseif (in_array('_GET', $paramSources) && (isset($_GET[$keyName]))) {
+ return $_GET[$keyName];
+ } elseif (in_array('_POST', $paramSources) && (isset($_POST[$keyName]))) {
+ return $_POST[$keyName];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Retrieve an array of parameters
+ *
+ * Retrieves a merged array of parameters, with precedence of userland
+ * params (see {@link setParam()}), $_GET, $_POST (i.e., values in the
+ * userland params will take precedence over all others).
+ *
+ * @return array
+ */
+ public function getParams()
+ {
+ $return = $this->_params;
+ $paramSources = $this->getParamSources();
+ if (in_array('_GET', $paramSources)
+ && isset($_GET)
+ && is_array($_GET)
+ ) {
+ $return += $_GET;
+ }
+ if (in_array('_POST', $paramSources)
+ && isset($_POST)
+ && is_array($_POST)
+ ) {
+ $return += $_POST;
+ }
+ return $return;
+ }
+
+ /**
+ * Set parameters
+ *
+ * Set one or more parameters. Parameters are set as userland parameters,
+ * using the keys specified in the array.
+ *
+ * @param array $params
+ * @return Zend_Controller_Request_Http
+ */
+ public function setParams(array $params)
+ {
+ foreach ($params as $key => $value) {
+ $this->setParam($key, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Set a key alias
+ *
+ * Set an alias used for key lookups. $name specifies the alias, $target
+ * specifies the actual key to use.
+ *
+ * @param string $name
+ * @param string $target
+ * @return Zend_Controller_Request_Http
+ */
+ public function setAlias($name, $target)
+ {
+ $this->_aliases[$name] = $target;
+ return $this;
+ }
+
+ /**
+ * Retrieve an alias
+ *
+ * Retrieve the actual key represented by the alias $name.
+ *
+ * @param string $name
+ * @return string|null Returns null when no alias exists
+ */
+ public function getAlias($name)
+ {
+ if (isset($this->_aliases[$name])) {
+ return $this->_aliases[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve the list of all aliases
+ *
+ * @return array
+ */
+ public function getAliases()
+ {
+ return $this->_aliases;
+ }
+
+ /**
+ * Return the method by which the request was made
+ *
+ * @return string
+ */
+ public function getMethod()
+ {
+ return $this->getServer('REQUEST_METHOD');
+ }
+
+ /**
+ * Was the request made by POST?
+ *
+ * @return boolean
+ */
+ public function isPost()
+ {
+ if ('POST' == $this->getMethod()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Was the request made by GET?
+ *
+ * @return boolean
+ */
+ public function isGet()
+ {
+ if ('GET' == $this->getMethod()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Was the request made by PUT?
+ *
+ * @return boolean
+ */
+ public function isPut()
+ {
+ if ('PUT' == $this->getMethod()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Was the request made by DELETE?
+ *
+ * @return boolean
+ */
+ public function isDelete()
+ {
+ if ('DELETE' == $this->getMethod()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Was the request made by HEAD?
+ *
+ * @return boolean
+ */
+ public function isHead()
+ {
+ if ('HEAD' == $this->getMethod()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Was the request made by OPTIONS?
+ *
+ * @return boolean
+ */
+ public function isOptions()
+ {
+ if ('OPTIONS' == $this->getMethod()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Is the request a Javascript XMLHttpRequest?
+ *
+ * Should work with Prototype/Script.aculo.us, possibly others.
+ *
+ * @return boolean
+ */
+ public function isXmlHttpRequest()
+ {
+ return ($this->getHeader('X_REQUESTED_WITH') == 'XMLHttpRequest');
+ }
+
+ /**
+ * Is this a Flash request?
+ *
+ * @return boolean
+ */
+ public function isFlashRequest()
+ {
+ $header = strtolower($this->getHeader('USER_AGENT'));
+ return (strstr($header, ' flash')) ? true : false;
+ }
+
+ /**
+ * Is https secure request
+ *
+ * @return boolean
+ */
+ public function isSecure()
+ {
+ return ($this->getScheme() === self::SCHEME_HTTPS);
+ }
+
+ /**
+ * Return the raw body of the request, if present
+ *
+ * @return string|false Raw body, or false if not present
+ */
+ public function getRawBody()
+ {
+ if (null === $this->_rawBody) {
+ $body = file_get_contents('php://input');
+
+ if (strlen(trim($body)) > 0) {
+ $this->_rawBody = $body;
+ } else {
+ $this->_rawBody = false;
+ }
+ }
+ return $this->_rawBody;
+ }
+
+ /**
+ * Return the value of the given HTTP header. Pass the header name as the
+ * plain, HTTP-specified header name. Ex.: Ask for 'Accept' to get the
+ * Accept header, 'Accept-Encoding' to get the Accept-Encoding header.
+ *
+ * @param string $header HTTP header name
+ * @return string|false HTTP header value, or false if not found
+ * @throws Zend_Controller_Request_Exception
+ */
+ public function getHeader($header)
+ {
+ if (empty($header)) {
+ require_once 'Zend/Controller/Request/Exception.php';
+ throw new Zend_Controller_Request_Exception('An HTTP header name is required');
+ }
+
+ // Try to get it from the $_SERVER array first
+ $temp = 'HTTP_' . strtoupper(str_replace('-', '_', $header));
+ if (isset($_SERVER[$temp])) {
+ return $_SERVER[$temp];
+ }
+
+ // This seems to be the only way to get the Authorization header on
+ // Apache
+ if (function_exists('apache_request_headers')) {
+ $headers = apache_request_headers();
+ if (isset($headers[$header])) {
+ return $headers[$header];
+ }
+ $header = strtolower($header);
+ foreach ($headers as $key => $value) {
+ if (strtolower($key) == $header) {
+ return $value;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the request URI scheme
+ *
+ * @return string
+ */
+ public function getScheme()
+ {
+ return ($this->getServer('HTTPS') == 'on') ? self::SCHEME_HTTPS : self::SCHEME_HTTP;
+ }
+
+ /**
+ * Get the HTTP host.
+ *
+ * "Host" ":" host [ ":" port ] ; Section 3.2.2
+ * Note the HTTP Host header is not the same as the URI host.
+ * It includes the port while the URI host doesn't.
+ *
+ * @return string
+ */
+ public function getHttpHost()
+ {
+ $host = $this->getServer('HTTP_HOST');
+ if (!empty($host)) {
+ return $host;
+ }
+
+ $scheme = $this->getScheme();
+ $name = $this->getServer('SERVER_NAME');
+ $port = $this->getServer('SERVER_PORT');
+
+ if(null === $name) {
+ return '';
+ }
+ elseif (($scheme == self::SCHEME_HTTP && $port == 80) || ($scheme == self::SCHEME_HTTPS && $port == 443)) {
+ return $name;
+ } else {
+ return $name . ':' . $port;
+ }
+ }
+
+ /**
+ * Get the client's IP addres
+ *
+ * @param boolean $checkProxy
+ * @return string
+ */
+ public function getClientIp($checkProxy = true)
+ {
+ if ($checkProxy && $this->getServer('HTTP_CLIENT_IP') != null) {
+ $ip = $this->getServer('HTTP_CLIENT_IP');
+ } else if ($checkProxy && $this->getServer('HTTP_X_FORWARDED_FOR') != null) {
+ $ip = $this->getServer('HTTP_X_FORWARDED_FOR');
+ } else {
+ $ip = $this->getServer('REMOTE_ADDR');
+ }
+
+ return $ip;
+ }
+}
diff --git a/library/Zend/Controller/Request/HttpTestCase.php b/library/Zend/Controller/Request/HttpTestCase.php
new file mode 100644
index 0000000..a016328
--- /dev/null
+++ b/library/Zend/Controller/Request/HttpTestCase.php
@@ -0,0 +1,276 @@
+_rawBody = (string) $content;
+ return $this;
+ }
+
+ /**
+ * Get RAW POST body
+ *
+ * @return string|null
+ */
+ public function getRawBody()
+ {
+ return $this->_rawBody;
+ }
+
+ /**
+ * Clear raw POST body
+ *
+ * @return Zend_Controller_Request_HttpTestCase
+ */
+ public function clearRawBody()
+ {
+ $this->_rawBody = null;
+ return $this;
+ }
+
+ /**
+ * Set a cookie
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return Zend_Controller_Request_HttpTestCase
+ */
+ public function setCookie($key, $value)
+ {
+ $_COOKIE[(string) $key] = $value;
+ return $this;
+ }
+
+ /**
+ * Set multiple cookies at once
+ *
+ * @param array $cookies
+ * @return void
+ */
+ public function setCookies(array $cookies)
+ {
+ foreach ($cookies as $key => $value) {
+ $_COOKIE[$key] = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Clear all cookies
+ *
+ * @return Zend_Controller_Request_HttpTestCase
+ */
+ public function clearCookies()
+ {
+ $_COOKIE = array();
+ return $this;
+ }
+
+ /**
+ * Set request method
+ *
+ * @param string $type
+ * @return Zend_Controller_Request_HttpTestCase
+ */
+ public function setMethod($type)
+ {
+ $type = strtoupper(trim((string) $type));
+ if (!in_array($type, $this->_validMethodTypes)) {
+ require_once 'Zend/Controller/Exception.php';
+ throw new Zend_Controller_Exception('Invalid request method specified');
+ }
+ $this->_method = $type;
+ return $this;
+ }
+
+ /**
+ * Get request method
+ *
+ * @return string|null
+ */
+ public function getMethod()
+ {
+ return $this->_method;
+ }
+
+ /**
+ * Set a request header
+ *
+ * @param string $key
+ * @param string $value
+ * @return Zend_Controller_Request_HttpTestCase
+ */
+ public function setHeader($key, $value)
+ {
+ $key = $this->_normalizeHeaderName($key);
+ $this->_headers[$key] = (string) $value;
+ return $this;
+ }
+
+ /**
+ * Set request headers
+ *
+ * @param array $headers
+ * @return Zend_Controller_Request_HttpTestCase
+ */
+ public function setHeaders(array $headers)
+ {
+ foreach ($headers as $key => $value) {
+ $this->setHeader($key, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Get request header
+ *
+ * @param string $header
+ * @param mixed $default
+ * @return string|null
+ */
+ public function getHeader($header, $default = null)
+ {
+ $header = $this->_normalizeHeaderName($header);
+ if (array_key_exists($header, $this->_headers)) {
+ return $this->_headers[$header];
+ }
+ return $default;
+ }
+
+ /**
+ * Get all request headers
+ *
+ * @return array
+ */
+ public function getHeaders()
+ {
+ return $this->_headers;
+ }
+
+ /**
+ * Clear request headers
+ *
+ * @return Zend_Controller_Request_HttpTestCase
+ */
+ public function clearHeaders()
+ {
+ $this->_headers = array();
+ return $this;
+ }
+
+ /**
+ * Get REQUEST_URI
+ *
+ * @return null|string
+ */
+ public function getRequestUri()
+ {
+ return $this->_requestUri;
+ }
+
+ /**
+ * Normalize a header name for setting and retrieval
+ *
+ * @param string $name
+ * @return string
+ */
+ protected function _normalizeHeaderName($name)
+ {
+ $name = strtoupper((string) $name);
+ $name = str_replace('-', '_', $name);
+ return $name;
+ }
+}
diff --git a/library/Zend/Controller/Request/Simple.php b/library/Zend/Controller/Request/Simple.php
new file mode 100644
index 0000000..51fd7ea
--- /dev/null
+++ b/library/Zend/Controller/Request/Simple.php
@@ -0,0 +1,55 @@
+setActionName($action);
+ }
+
+ if ($controller) {
+ $this->setControllerName($controller);
+ }
+
+ if ($module) {
+ $this->setModuleName($module);
+ }
+
+ if ($params) {
+ $this->setParams($params);
+ }
+ }
+
+}
diff --git a/library/Zend/Controller/Response/Abstract.php b/library/Zend/Controller/Response/Abstract.php
new file mode 100644
index 0000000..19e278d
--- /dev/null
+++ b/library/Zend/Controller/Response/Abstract.php
@@ -0,0 +1,796 @@
+canSendHeaders(true);
+ $name = $this->_normalizeHeader($name);
+ $value = (string) $value;
+
+ if ($replace) {
+ foreach ($this->_headers as $key => $header) {
+ if ($name == $header['name']) {
+ unset($this->_headers[$key]);
+ }
+ }
+ }
+
+ $this->_headers[] = array(
+ 'name' => $name,
+ 'value' => $value,
+ 'replace' => $replace
+ );
+
+ return $this;
+ }
+
+ /**
+ * Set redirect URL
+ *
+ * Sets Location header and response code. Forces replacement of any prior
+ * redirects.
+ *
+ * @param string $url
+ * @param int $code
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function setRedirect($url, $code = 302)
+ {
+ $this->canSendHeaders(true);
+ $this->setHeader('Location', $url, true)
+ ->setHttpResponseCode($code);
+
+ return $this;
+ }
+
+ /**
+ * Is this a redirect?
+ *
+ * @return boolean
+ */
+ public function isRedirect()
+ {
+ return $this->_isRedirect;
+ }
+
+ /**
+ * Return array of headers; see {@link $_headers} for format
+ *
+ * @return array
+ */
+ public function getHeaders()
+ {
+ return $this->_headers;
+ }
+
+ /**
+ * Clear headers
+ *
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function clearHeaders()
+ {
+ $this->_headers = array();
+
+ return $this;
+ }
+
+ /**
+ * Clears the specified HTTP header
+ *
+ * @param string $name
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function clearHeader($name)
+ {
+ if (! count($this->_headers)) {
+ return $this;
+ }
+
+ foreach ($this->_headers as $index => $header) {
+ if ($name == $header['name']) {
+ unset($this->_headers[$index]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set raw HTTP header
+ *
+ * Allows setting non key => value headers, such as status codes
+ *
+ * @param string $value
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function setRawHeader($value)
+ {
+ $this->canSendHeaders(true);
+ if ('Location' == substr($value, 0, 8)) {
+ $this->_isRedirect = true;
+ }
+ $this->_headersRaw[] = (string) $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve all {@link setRawHeader() raw HTTP headers}
+ *
+ * @return array
+ */
+ public function getRawHeaders()
+ {
+ return $this->_headersRaw;
+ }
+
+ /**
+ * Clear all {@link setRawHeader() raw HTTP headers}
+ *
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function clearRawHeaders()
+ {
+ $this->_headersRaw = array();
+ return $this;
+ }
+
+ /**
+ * Clears the specified raw HTTP header
+ *
+ * @param string $headerRaw
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function clearRawHeader($headerRaw)
+ {
+ if (! count($this->_headersRaw)) {
+ return $this;
+ }
+
+ $key = array_search($headerRaw, $this->_headersRaw);
+ if ($key !== false) {
+ unset($this->_headersRaw[$key]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Clear all headers, normal and raw
+ *
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function clearAllHeaders()
+ {
+ return $this->clearHeaders()
+ ->clearRawHeaders();
+ }
+
+ /**
+ * Set HTTP response code to use with headers
+ *
+ * @param int $code
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function setHttpResponseCode($code)
+ {
+ if (!is_int($code) || (100 > $code) || (599 < $code)) {
+ require_once 'Zend/Controller/Response/Exception.php';
+ throw new Zend_Controller_Response_Exception('Invalid HTTP response code');
+ }
+
+ if ((300 <= $code) && (307 >= $code)) {
+ $this->_isRedirect = true;
+ } else {
+ $this->_isRedirect = false;
+ }
+
+ $this->_httpResponseCode = $code;
+ return $this;
+ }
+
+ /**
+ * Retrieve HTTP response code
+ *
+ * @return int
+ */
+ public function getHttpResponseCode()
+ {
+ return $this->_httpResponseCode;
+ }
+
+ /**
+ * Can we send headers?
+ *
+ * @param boolean $throw Whether or not to throw an exception if headers have been sent; defaults to false
+ * @return boolean
+ * @throws Zend_Controller_Response_Exception
+ */
+ public function canSendHeaders($throw = false)
+ {
+ $ok = headers_sent($file, $line);
+ if ($ok && $throw && $this->headersSentThrowsException) {
+ require_once 'Zend/Controller/Response/Exception.php';
+ throw new Zend_Controller_Response_Exception('Cannot send headers; headers already sent in ' . $file . ', line ' . $line);
+ }
+
+ return !$ok;
+ }
+
+ /**
+ * Send all headers
+ *
+ * Sends any headers specified. If an {@link setHttpResponseCode() HTTP response code}
+ * has been specified, it is sent with the first header.
+ *
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function sendHeaders()
+ {
+ // Only check if we can send headers if we have headers to send
+ if (count($this->_headersRaw) || count($this->_headers) || (200 != $this->_httpResponseCode)) {
+ $this->canSendHeaders(true);
+ } elseif (200 == $this->_httpResponseCode) {
+ // Haven't changed the response code, and we have no headers
+ return $this;
+ }
+
+ $httpCodeSent = false;
+
+ foreach ($this->_headersRaw as $header) {
+ if (!$httpCodeSent && $this->_httpResponseCode) {
+ header($header, true, $this->_httpResponseCode);
+ $httpCodeSent = true;
+ } else {
+ header($header);
+ }
+ }
+
+ foreach ($this->_headers as $header) {
+ if (!$httpCodeSent && $this->_httpResponseCode) {
+ header($header['name'] . ': ' . $header['value'], $header['replace'], $this->_httpResponseCode);
+ $httpCodeSent = true;
+ } else {
+ header($header['name'] . ': ' . $header['value'], $header['replace']);
+ }
+ }
+
+ if (!$httpCodeSent) {
+ header('HTTP/1.1 ' . $this->_httpResponseCode);
+ $httpCodeSent = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set body content
+ *
+ * If $name is not passed, or is not a string, resets the entire body and
+ * sets the 'default' key to $content.
+ *
+ * If $name is a string, sets the named segment in the body array to
+ * $content.
+ *
+ * @param string $content
+ * @param null|string $name
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function setBody($content, $name = null)
+ {
+ if ((null === $name) || !is_string($name)) {
+ $this->_body = array('default' => (string) $content);
+ } else {
+ $this->_body[$name] = (string) $content;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Append content to the body content
+ *
+ * @param string $content
+ * @param null|string $name
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function appendBody($content, $name = null)
+ {
+ if ((null === $name) || !is_string($name)) {
+ if (isset($this->_body['default'])) {
+ $this->_body['default'] .= (string) $content;
+ } else {
+ return $this->append('default', $content);
+ }
+ } elseif (isset($this->_body[$name])) {
+ $this->_body[$name] .= (string) $content;
+ } else {
+ return $this->append($name, $content);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Clear body array
+ *
+ * With no arguments, clears the entire body array. Given a $name, clears
+ * just that named segment; if no segment matching $name exists, returns
+ * false to indicate an error.
+ *
+ * @param string $name Named segment to clear
+ * @return boolean
+ */
+ public function clearBody($name = null)
+ {
+ if (null !== $name) {
+ $name = (string) $name;
+ if (isset($this->_body[$name])) {
+ unset($this->_body[$name]);
+ return true;
+ }
+
+ return false;
+ }
+
+ $this->_body = array();
+ return true;
+ }
+
+ /**
+ * Return the body content
+ *
+ * If $spec is false, returns the concatenated values of the body content
+ * array. If $spec is boolean true, returns the body content array. If
+ * $spec is a string and matches a named segment, returns the contents of
+ * that segment; otherwise, returns null.
+ *
+ * @param boolean $spec
+ * @return string|array|null
+ */
+ public function getBody($spec = false)
+ {
+ if (false === $spec) {
+ ob_start();
+ $this->outputBody();
+ return ob_get_clean();
+ } elseif (true === $spec) {
+ return $this->_body;
+ } elseif (is_string($spec) && isset($this->_body[$spec])) {
+ return $this->_body[$spec];
+ }
+
+ return null;
+ }
+
+ /**
+ * Append a named body segment to the body content array
+ *
+ * If segment already exists, replaces with $content and places at end of
+ * array.
+ *
+ * @param string $name
+ * @param string $content
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function append($name, $content)
+ {
+ if (!is_string($name)) {
+ require_once 'Zend/Controller/Response/Exception.php';
+ throw new Zend_Controller_Response_Exception('Invalid body segment key ("' . gettype($name) . '")');
+ }
+
+ if (isset($this->_body[$name])) {
+ unset($this->_body[$name]);
+ }
+ $this->_body[$name] = (string) $content;
+ return $this;
+ }
+
+ /**
+ * Prepend a named body segment to the body content array
+ *
+ * If segment already exists, replaces with $content and places at top of
+ * array.
+ *
+ * @param string $name
+ * @param string $content
+ * @return void
+ */
+ public function prepend($name, $content)
+ {
+ if (!is_string($name)) {
+ require_once 'Zend/Controller/Response/Exception.php';
+ throw new Zend_Controller_Response_Exception('Invalid body segment key ("' . gettype($name) . '")');
+ }
+
+ if (isset($this->_body[$name])) {
+ unset($this->_body[$name]);
+ }
+
+ $new = array($name => (string) $content);
+ $this->_body = $new + $this->_body;
+
+ return $this;
+ }
+
+ /**
+ * Insert a named segment into the body content array
+ *
+ * @param string $name
+ * @param string $content
+ * @param string $parent
+ * @param boolean $before Whether to insert the new segment before or
+ * after the parent. Defaults to false (after)
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function insert($name, $content, $parent = null, $before = false)
+ {
+ if (!is_string($name)) {
+ require_once 'Zend/Controller/Response/Exception.php';
+ throw new Zend_Controller_Response_Exception('Invalid body segment key ("' . gettype($name) . '")');
+ }
+
+ if ((null !== $parent) && !is_string($parent)) {
+ require_once 'Zend/Controller/Response/Exception.php';
+ throw new Zend_Controller_Response_Exception('Invalid body segment parent key ("' . gettype($parent) . '")');
+ }
+
+ if (isset($this->_body[$name])) {
+ unset($this->_body[$name]);
+ }
+
+ if ((null === $parent) || !isset($this->_body[$parent])) {
+ return $this->append($name, $content);
+ }
+
+ $ins = array($name => (string) $content);
+ $keys = array_keys($this->_body);
+ $loc = array_search($parent, $keys);
+ if (!$before) {
+ // Increment location if not inserting before
+ ++$loc;
+ }
+
+ if (0 === $loc) {
+ // If location of key is 0, we're prepending
+ $this->_body = $ins + $this->_body;
+ } elseif ($loc >= (count($this->_body))) {
+ // If location of key is maximal, we're appending
+ $this->_body = $this->_body + $ins;
+ } else {
+ // Otherwise, insert at location specified
+ $pre = array_slice($this->_body, 0, $loc);
+ $post = array_slice($this->_body, $loc);
+ $this->_body = $pre + $ins + $post;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Echo the body segments
+ *
+ * @return void
+ */
+ public function outputBody()
+ {
+ $body = implode('', $this->_body);
+ echo $body;
+ }
+
+ /**
+ * Register an exception with the response
+ *
+ * @param Exception $e
+ * @return Zend_Controller_Response_Abstract
+ */
+ public function setException(Exception $e)
+ {
+ $this->_exceptions[] = $e;
+ return $this;
+ }
+
+ /**
+ * Retrieve the exception stack
+ *
+ * @return array
+ */
+ public function getException()
+ {
+ return $this->_exceptions;
+ }
+
+ /**
+ * Has an exception been registered with the response?
+ *
+ * @return boolean
+ */
+ public function isException()
+ {
+ return !empty($this->_exceptions);
+ }
+
+ /**
+ * Does the response object contain an exception of a given type?
+ *
+ * @param string $type
+ * @return boolean
+ */
+ public function hasExceptionOfType($type)
+ {
+ foreach ($this->_exceptions as $e) {
+ if ($e instanceof $type) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Does the response object contain an exception with a given message?
+ *
+ * @param string $message
+ * @return boolean
+ */
+ public function hasExceptionOfMessage($message)
+ {
+ foreach ($this->_exceptions as $e) {
+ if ($message == $e->getMessage()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Does the response object contain an exception with a given code?
+ *
+ * @param int $code
+ * @return boolean
+ */
+ public function hasExceptionOfCode($code)
+ {
+ $code = (int) $code;
+ foreach ($this->_exceptions as $e) {
+ if ($code == $e->getCode()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieve all exceptions of a given type
+ *
+ * @param string $type
+ * @return false|array
+ */
+ public function getExceptionByType($type)
+ {
+ $exceptions = array();
+ foreach ($this->_exceptions as $e) {
+ if ($e instanceof $type) {
+ $exceptions[] = $e;
+ }
+ }
+
+ if (empty($exceptions)) {
+ $exceptions = false;
+ }
+
+ return $exceptions;
+ }
+
+ /**
+ * Retrieve all exceptions of a given message
+ *
+ * @param string $message
+ * @return false|array
+ */
+ public function getExceptionByMessage($message)
+ {
+ $exceptions = array();
+ foreach ($this->_exceptions as $e) {
+ if ($message == $e->getMessage()) {
+ $exceptions[] = $e;
+ }
+ }
+
+ if (empty($exceptions)) {
+ $exceptions = false;
+ }
+
+ return $exceptions;
+ }
+
+ /**
+ * Retrieve all exceptions of a given code
+ *
+ * @param mixed $code
+ * @return void
+ */
+ public function getExceptionByCode($code)
+ {
+ $code = (int) $code;
+ $exceptions = array();
+ foreach ($this->_exceptions as $e) {
+ if ($code == $e->getCode()) {
+ $exceptions[] = $e;
+ }
+ }
+
+ if (empty($exceptions)) {
+ $exceptions = false;
+ }
+
+ return $exceptions;
+ }
+
+ /**
+ * Whether or not to render exceptions (off by default)
+ *
+ * If called with no arguments or a null argument, returns the value of the
+ * flag; otherwise, sets it and returns the current value.
+ *
+ * @param boolean $flag Optional
+ * @return boolean
+ */
+ public function renderExceptions($flag = null)
+ {
+ if (null !== $flag) {
+ $this->_renderExceptions = $flag ? true : false;
+ }
+
+ return $this->_renderExceptions;
+ }
+
+ /**
+ * Send the response, including all headers, rendering exceptions if so
+ * requested.
+ *
+ * @return void
+ */
+ public function sendResponse()
+ {
+ $this->sendHeaders();
+
+ if ($this->isException() && $this->renderExceptions()) {
+ $exceptions = '';
+ foreach ($this->getException() as $e) {
+ $exceptions .= $e->__toString() . "\n";
+ }
+ echo $exceptions;
+ return;
+ }
+
+ $this->outputBody();
+ }
+
+ /**
+ * Magic __toString functionality
+ *
+ * Proxies to {@link sendResponse()} and returns response value as string
+ * using output buffering.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ ob_start();
+ $this->sendResponse();
+ return ob_get_clean();
+ }
+}
diff --git a/library/Zend/Controller/Response/Cli.php b/library/Zend/Controller/Response/Cli.php
new file mode 100644
index 0000000..74268f3
--- /dev/null
+++ b/library/Zend/Controller/Response/Cli.php
@@ -0,0 +1,68 @@
+isException() && $this->renderExceptions()) {
+ $exceptions = '';
+ foreach ($this->getException() as $e) {
+ $exceptions .= $e->__toString() . "\n";
+ }
+ return $exceptions;
+ }
+
+ return $this->_body;
+ }
+}
diff --git a/library/Zend/Controller/Response/Exception.php b/library/Zend/Controller/Response/Exception.php
new file mode 100644
index 0000000..2bc6005
--- /dev/null
+++ b/library/Zend/Controller/Response/Exception.php
@@ -0,0 +1,36 @@
+_headersRaw as $header) {
+ $headers[] = $header;
+ }
+ foreach ($this->_headers as $header) {
+ $name = $header['name'];
+ $key = strtolower($name);
+ if (array_key_exists($name, $headers)) {
+ if ($header['replace']) {
+ $headers[$key] = $header['name'] . ': ' . $header['value'];
+ }
+ } else {
+ $headers[$key] = $header['name'] . ': ' . $header['value'];
+ }
+ }
+ return $headers;
+ }
+
+ /**
+ * Can we send headers?
+ *
+ * @param bool $throw
+ * @return void
+ */
+ public function canSendHeaders($throw = false)
+ {
+ return true;
+ }
+
+ /**
+ * Return the concatenated body segments
+ *
+ * @return string
+ */
+ public function outputBody()
+ {
+ $fullContent = '';
+ foreach ($this->_body as $content) {
+ $fullContent .= $content;
+ }
+ return $fullContent;
+ }
+
+ /**
+ * Get body and/or body segments
+ *
+ * @param bool|string $spec
+ * @return string|array|null
+ */
+ public function getBody($spec = false)
+ {
+ if (false === $spec) {
+ return $this->outputBody();
+ } elseif (true === $spec) {
+ return $this->_body;
+ } elseif (is_string($spec) && isset($this->_body[$spec])) {
+ return $this->_body[$spec];
+ }
+
+ return null;
+ }
+
+ /**
+ * "send" Response
+ *
+ * Concats all response headers, and then final body (separated by two
+ * newlines)
+ *
+ * @return string
+ */
+ public function sendResponse()
+ {
+ $headers = $this->sendHeaders();
+ $content = implode("\n", $headers) . "\n\n";
+
+ if ($this->isException() && $this->renderExceptions()) {
+ $exceptions = '';
+ foreach ($this->getException() as $e) {
+ $exceptions .= $e->__toString() . "\n";
+ }
+ $content .= $exceptions;
+ } else {
+ $content .= $this->outputBody();
+ }
+
+ return $content;
+ }
+}
diff --git a/library/Zend/Controller/Router/Abstract.php b/library/Zend/Controller/Router/Abstract.php
new file mode 100644
index 0000000..1468740
--- /dev/null
+++ b/library/Zend/Controller/Router/Abstract.php
@@ -0,0 +1,175 @@
+setParams($params);
+ }
+
+ /**
+ * Add or modify a parameter to use when instantiating an action controller
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return Zend_Controller_Router
+ */
+ public function setParam($name, $value)
+ {
+ $name = (string) $name;
+ $this->_invokeParams[$name] = $value;
+ return $this;
+ }
+
+ /**
+ * Set parameters to pass to action controller constructors
+ *
+ * @param array $params
+ * @return Zend_Controller_Router
+ */
+ public function setParams(array $params)
+ {
+ $this->_invokeParams = array_merge($this->_invokeParams, $params);
+ return $this;
+ }
+
+ /**
+ * Retrieve a single parameter from the controller parameter stack
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function getParam($name)
+ {
+ if(isset($this->_invokeParams[$name])) {
+ return $this->_invokeParams[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve action controller instantiation parameters
+ *
+ * @return array
+ */
+ public function getParams()
+ {
+ return $this->_invokeParams;
+ }
+
+ /**
+ * Clear the controller parameter stack
+ *
+ * By default, clears all parameters. If a parameter name is given, clears
+ * only that parameter; if an array of parameter names is provided, clears
+ * each.
+ *
+ * @param null|string|array single key or array of keys for params to clear
+ * @return Zend_Controller_Router
+ */
+ public function clearParams($name = null)
+ {
+ if (null === $name) {
+ $this->_invokeParams = array();
+ } elseif (is_string($name) && isset($this->_invokeParams[$name])) {
+ unset($this->_invokeParams[$name]);
+ } elseif (is_array($name)) {
+ foreach ($name as $key) {
+ if (is_string($key) && isset($this->_invokeParams[$key])) {
+ unset($this->_invokeParams[$key]);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve Front Controller
+ *
+ * @return Zend_Controller_Front
+ */
+ public function getFrontController()
+ {
+ // Used cache version if found
+ if (null !== $this->_frontController) {
+ return $this->_frontController;
+ }
+
+ require_once 'Zend/Controller/Front.php';
+ $this->_frontController = Zend_Controller_Front::getInstance();
+ return $this->_frontController;
+ }
+
+ /**
+ * Set Front Controller
+ *
+ * @param Zend_Controller_Front $controller
+ * @return Zend_Controller_Router_Interface
+ */
+ public function setFrontController(Zend_Controller_Front $controller)
+ {
+ $this->_frontController = $controller;
+ return $this;
+ }
+
+}
diff --git a/library/Zend/Controller/Router/Exception.php b/library/Zend/Controller/Router/Exception.php
new file mode 100644
index 0000000..7e5a7b7
--- /dev/null
+++ b/library/Zend/Controller/Router/Exception.php
@@ -0,0 +1,36 @@
+hasRoute('default')) {
+ $dispatcher = $this->getFrontController()->getDispatcher();
+ $request = $this->getFrontController()->getRequest();
+
+ require_once 'Zend/Controller/Router/Route/Module.php';
+ $compat = new Zend_Controller_Router_Route_Module(array(), $dispatcher, $request);
+
+ $this->_routes = array('default' => $compat) + $this->_routes;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add route to the route chain
+ *
+ * If route contains method setRequest(), it is initialized with a request object
+ *
+ * @param string $name Name of the route
+ * @param Zend_Controller_Router_Route_Interface $route Instance of the route
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function addRoute($name, Zend_Controller_Router_Route_Interface $route)
+ {
+ if (method_exists($route, 'setRequest')) {
+ $route->setRequest($this->getFrontController()->getRequest());
+ }
+
+ $this->_routes[$name] = $route;
+
+ return $this;
+ }
+
+ /**
+ * Add routes to the route chain
+ *
+ * @param array $routes Array of routes with names as keys and routes as values
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function addRoutes($routes) {
+ foreach ($routes as $name => $route) {
+ $this->addRoute($name, $route);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Create routes out of Zend_Config configuration
+ *
+ * Example INI:
+ * routes.archive.route = "archive/:year/*"
+ * routes.archive.defaults.controller = archive
+ * routes.archive.defaults.action = show
+ * routes.archive.defaults.year = 2000
+ * routes.archive.reqs.year = "\d+"
+ *
+ * routes.news.type = "Zend_Controller_Router_Route_Static"
+ * routes.news.route = "news"
+ * routes.news.defaults.controller = "news"
+ * routes.news.defaults.action = "list"
+ *
+ * And finally after you have created a Zend_Config with above ini:
+ * $router = new Zend_Controller_Router_Rewrite();
+ * $router->addConfig($config, 'routes');
+ *
+ * @param Zend_Config $config Configuration object
+ * @param string $section Name of the config section containing route's definitions
+ * @throws Zend_Controller_Router_Exception
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function addConfig(Zend_Config $config, $section = null)
+ {
+ if ($section !== null) {
+ if ($config->{$section} === null) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception("No route configuration in section '{$section}'");
+ }
+
+ $config = $config->{$section};
+ }
+
+ foreach ($config as $name => $info) {
+ $route = $this->_getRouteFromConfig($info);
+
+ if ($route instanceof Zend_Controller_Router_Route_Chain) {
+ if (!isset($info->chain)) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception("No chain defined");
+ }
+
+ if ($info->chain instanceof Zend_Config) {
+ $childRouteNames = $info->chain;
+ } else {
+ $childRouteNames = explode(',', $info->chain);
+ }
+
+ foreach ($childRouteNames as $childRouteName) {
+ $childRoute = $this->getRoute(trim($childRouteName));
+ $route->chain($childRoute);
+ }
+
+ $this->addRoute($name, $route);
+ } elseif (isset($info->chains) && $info->chains instanceof Zend_Config) {
+ $this->_addChainRoutesFromConfig($name, $route, $info->chains);
+ } else {
+ $this->addRoute($name, $route);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get a route frm a config instance
+ *
+ * @param Zend_Config $info
+ * @return Zend_Controller_Router_Route_Interface
+ */
+ protected function _getRouteFromConfig(Zend_Config $info)
+ {
+ $class = (isset($info->type)) ? $info->type : 'Zend_Controller_Router_Route';
+ if (!class_exists($class)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($class);
+ }
+
+ $route = call_user_func(array($class, 'getInstance'), $info);
+
+ if (isset($info->abstract) && $info->abstract && method_exists($route, 'isAbstract')) {
+ $route->isAbstract(true);
+ }
+
+ return $route;
+ }
+
+ /**
+ * Add chain routes from a config route
+ *
+ * @param string $name
+ * @param Zend_Controller_Router_Route_Interface $route
+ * @param Zend_Config $childRoutesInfo
+ * @return void
+ */
+ protected function _addChainRoutesFromConfig($name,
+ Zend_Controller_Router_Route_Interface $route,
+ Zend_Config $childRoutesInfo)
+ {
+ foreach ($childRoutesInfo as $childRouteName => $childRouteInfo) {
+ if (is_string($childRouteInfo)) {
+ $childRouteName = $childRouteInfo;
+ $childRoute = $this->getRoute($childRouteName);
+ } else {
+ $childRoute = $this->_getRouteFromConfig($childRouteInfo);
+ }
+
+ if ($route instanceof Zend_Controller_Router_Route_Chain) {
+ $chainRoute = clone $route;
+ $chainRoute->chain($childRoute);
+ } else {
+ $chainRoute = $route->chain($childRoute);
+ }
+
+ $chainName = $name . $this->_chainNameSeparator . $childRouteName;
+
+ if (isset($childRouteInfo->chains)) {
+ $this->_addChainRoutesFromConfig($chainName, $chainRoute, $childRouteInfo->chains);
+ } else {
+ $this->addRoute($chainName, $chainRoute);
+ }
+ }
+ }
+
+ /**
+ * Remove a route from the route chain
+ *
+ * @param string $name Name of the route
+ * @throws Zend_Controller_Router_Exception
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function removeRoute($name)
+ {
+ if (!isset($this->_routes[$name])) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception("Route $name is not defined");
+ }
+
+ unset($this->_routes[$name]);
+
+ return $this;
+ }
+
+ /**
+ * Remove all standard default routes
+ *
+ * @param Zend_Controller_Router_Route_Interface Route
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function removeDefaultRoutes()
+ {
+ $this->_useDefaultRoutes = false;
+
+ return $this;
+ }
+
+ /**
+ * Check if named route exists
+ *
+ * @param string $name Name of the route
+ * @return boolean
+ */
+ public function hasRoute($name)
+ {
+ return isset($this->_routes[$name]);
+ }
+
+ /**
+ * Retrieve a named route
+ *
+ * @param string $name Name of the route
+ * @throws Zend_Controller_Router_Exception
+ * @return Zend_Controller_Router_Route_Interface Route object
+ */
+ public function getRoute($name)
+ {
+ if (!isset($this->_routes[$name])) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception("Route $name is not defined");
+ }
+
+ return $this->_routes[$name];
+ }
+
+ /**
+ * Retrieve a currently matched route
+ *
+ * @throws Zend_Controller_Router_Exception
+ * @return Zend_Controller_Router_Route_Interface Route object
+ */
+ public function getCurrentRoute()
+ {
+ if (!isset($this->_currentRoute)) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception("Current route is not defined");
+ }
+ return $this->getRoute($this->_currentRoute);
+ }
+
+ /**
+ * Retrieve a name of currently matched route
+ *
+ * @throws Zend_Controller_Router_Exception
+ * @return Zend_Controller_Router_Route_Interface Route object
+ */
+ public function getCurrentRouteName()
+ {
+ if (!isset($this->_currentRoute)) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception("Current route is not defined");
+ }
+ return $this->_currentRoute;
+ }
+
+ /**
+ * Retrieve an array of routes added to the route chain
+ *
+ * @return array All of the defined routes
+ */
+ public function getRoutes()
+ {
+ return $this->_routes;
+ }
+
+ /**
+ * Find a matching route to the current PATH_INFO and inject
+ * returning values to the Request object.
+ *
+ * @throws Zend_Controller_Router_Exception
+ * @return Zend_Controller_Request_Abstract Request object
+ */
+ public function route(Zend_Controller_Request_Abstract $request)
+ {
+ if (!$request instanceof Zend_Controller_Request_Http) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception('Zend_Controller_Router_Rewrite requires a Zend_Controller_Request_Http-based request object');
+ }
+
+ if ($this->_useDefaultRoutes) {
+ $this->addDefaultRoutes();
+ }
+
+ // Find the matching route
+ $routeMatched = false;
+
+ foreach (array_reverse($this->_routes, true) as $name => $route) {
+ // TODO: Should be an interface method. Hack for 1.0 BC
+ if (method_exists($route, 'isAbstract') && $route->isAbstract()) {
+ continue;
+ }
+
+ // TODO: Should be an interface method. Hack for 1.0 BC
+ if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) {
+ $match = $request->getPathInfo();
+ } else {
+ $match = $request;
+ }
+
+ if ($params = $route->match($match)) {
+ $this->_setRequestParams($request, $params);
+ $this->_currentRoute = $name;
+ $routeMatched = true;
+ break;
+ }
+ }
+
+ if (!$routeMatched) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception('No route matched the request', 404);
+ }
+
+ if($this->_useCurrentParamsAsGlobal) {
+ $params = $request->getParams();
+ foreach($params as $param => $value) {
+ $this->setGlobalParam($param, $value);
+ }
+ }
+
+ return $request;
+
+ }
+
+ protected function _setRequestParams($request, $params)
+ {
+ foreach ($params as $param => $value) {
+
+ $request->setParam($param, $value);
+
+ if ($param === $request->getModuleKey()) {
+ $request->setModuleName($value);
+ }
+ if ($param === $request->getControllerKey()) {
+ $request->setControllerName($value);
+ }
+ if ($param === $request->getActionKey()) {
+ $request->setActionName($value);
+ }
+
+ }
+ }
+
+ /**
+ * Generates a URL path that can be used in URL creation, redirection, etc.
+ *
+ * @param array $userParams Options passed by a user used to override parameters
+ * @param mixed $name The name of a Route to use
+ * @param bool $reset Whether to reset to the route defaults ignoring URL params
+ * @param bool $encode Tells to encode URL parts on output
+ * @throws Zend_Controller_Router_Exception
+ * @return string Resulting absolute URL path
+ */
+ public function assemble($userParams, $name = null, $reset = false, $encode = true)
+ {
+ if (!is_array($userParams)) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception('userParams must be an array');
+ }
+
+ if ($name == null) {
+ try {
+ $name = $this->getCurrentRouteName();
+ } catch (Zend_Controller_Router_Exception $e) {
+ $name = 'default';
+ }
+ }
+
+ // Use UNION (+) in order to preserve numeric keys
+ $params = $userParams + $this->_globalParams;
+
+ $route = $this->getRoute($name);
+ $url = $route->assemble($params, $reset, $encode);
+
+ if (!preg_match('|^[a-z]+://|', $url)) {
+ $url = rtrim($this->getFrontController()->getBaseUrl(), self::URI_DELIMITER) . self::URI_DELIMITER . $url;
+ }
+
+ return $url;
+ }
+
+ /**
+ * Set a global parameter
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function setGlobalParam($name, $value)
+ {
+ $this->_globalParams[$name] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Set the separator to use with chain names
+ *
+ * @param string $separator The separator to use
+ * @return Zend_Controller_Router_Rewrite
+ */
+ public function setChainNameSeparator($separator) {
+ $this->_chainNameSeparator = $separator;
+
+ return $this;
+ }
+
+ /**
+ * Get the separator to use for chain names
+ *
+ * @return string
+ */
+ public function getChainNameSeparator() {
+ return $this->_chainNameSeparator;
+ }
+
+ /**
+ * Determines/returns whether to use the request parameters as global parameters.
+ *
+ * @param boolean|null $use
+ * Null/unset when you want to retrieve the current state.
+ * True when request parameters should be global, false otherwise
+ * @return boolean|Zend_Controller_Router_Rewrite
+ * Returns a boolean if first param isn't set, returns an
+ * instance of Zend_Controller_Router_Rewrite otherwise.
+ *
+ */
+ public function useRequestParametersAsGlobal($use = null) {
+ if($use === null) {
+ return $this->_useCurrentParamsAsGlobal;
+ }
+
+ $this->_useCurrentParamsAsGlobal = (bool) $use;
+
+ return $this;
+ }
+}
diff --git a/library/Zend/Controller/Router/Route.php b/library/Zend/Controller/Router/Route.php
new file mode 100644
index 0000000..91a7c15
--- /dev/null
+++ b/library/Zend/Controller/Router/Route.php
@@ -0,0 +1,562 @@
+reqs instanceof Zend_Config) ? $config->reqs->toArray() : array();
+ $defs = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
+ return new self($config->route, $defs, $reqs);
+ }
+
+ /**
+ * Prepares the route for mapping by splitting (exploding) it
+ * to a corresponding atomic parts. These parts are assigned
+ * a position which is later used for matching and preparing values.
+ *
+ * @param string $route Map used to match with later submitted URL path
+ * @param array $defaults Defaults for map variables with keys as variable names
+ * @param array $reqs Regular expression requirements for variables (keys as variable names)
+ * @param Zend_Translate $translator Translator to use for this instance
+ */
+ public function __construct($route, $defaults = array(), $reqs = array(), Zend_Translate $translator = null, $locale = null)
+ {
+ $route = trim($route, $this->_urlDelimiter);
+ $this->_defaults = (array) $defaults;
+ $this->_requirements = (array) $reqs;
+ $this->_translator = $translator;
+ $this->_locale = $locale;
+
+ if ($route !== '') {
+ foreach (explode($this->_urlDelimiter, $route) as $pos => $part) {
+ if (substr($part, 0, 1) == $this->_urlVariable && substr($part, 1, 1) != $this->_urlVariable) {
+ $name = substr($part, 1);
+
+ if (substr($name, 0, 1) === '@' && substr($name, 1, 1) !== '@') {
+ $name = substr($name, 1);
+ $this->_translatable[] = $name;
+ $this->_isTranslated = true;
+ }
+
+ $this->_parts[$pos] = (isset($reqs[$name]) ? $reqs[$name] : $this->_defaultRegex);
+ $this->_variables[$pos] = $name;
+ } else {
+ if (substr($part, 0, 1) == $this->_urlVariable) {
+ $part = substr($part, 1);
+ }
+
+ if (substr($part, 0, 1) === '@' && substr($part, 1, 1) !== '@') {
+ $this->_isTranslated = true;
+ }
+
+ $this->_parts[$pos] = $part;
+
+ if ($part !== '*') {
+ $this->_staticCount++;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Matches a user submitted path with parts defined by a map. Assigns and
+ * returns an array of variables on a successful match.
+ *
+ * @param string $path Path used to match against this routing map
+ * @return array|false An array of assigned values or a false on a mismatch
+ */
+ public function match($path, $partial = false)
+ {
+ if ($this->_isTranslated) {
+ $translateMessages = $this->getTranslator()->getMessages();
+ }
+
+ $pathStaticCount = 0;
+ $values = array();
+ $matchedPath = '';
+
+ if (!$partial) {
+ $path = trim($path, $this->_urlDelimiter);
+ }
+
+ if ($path !== '') {
+ $path = explode($this->_urlDelimiter, $path);
+
+ foreach ($path as $pos => $pathPart) {
+ // Path is longer than a route, it's not a match
+ if (!array_key_exists($pos, $this->_parts)) {
+ if ($partial) {
+ break;
+ } else {
+ return false;
+ }
+ }
+
+ $matchedPath .= $pathPart . $this->_urlDelimiter;
+
+ // If it's a wildcard, get the rest of URL as wildcard data and stop matching
+ if ($this->_parts[$pos] == '*') {
+ $count = count($path);
+ for($i = $pos; $i < $count; $i+=2) {
+ $var = urldecode($path[$i]);
+ if (!isset($this->_wildcardData[$var]) && !isset($this->_defaults[$var]) && !isset($values[$var])) {
+ $this->_wildcardData[$var] = (isset($path[$i+1])) ? urldecode($path[$i+1]) : null;
+ }
+ }
+
+ $matchedPath = implode($this->_urlDelimiter, $path);
+ break;
+ }
+
+ $name = isset($this->_variables[$pos]) ? $this->_variables[$pos] : null;
+ $pathPart = urldecode($pathPart);
+
+ // Translate value if required
+ $part = $this->_parts[$pos];
+ if ($this->_isTranslated && (substr($part, 0, 1) === '@' && substr($part, 1, 1) !== '@' && $name === null) || $name !== null && in_array($name, $this->_translatable)) {
+ if (substr($part, 0, 1) === '@') {
+ $part = substr($part, 1);
+ }
+
+ if (($originalPathPart = array_search($pathPart, $translateMessages)) !== false) {
+ $pathPart = $originalPathPart;
+ }
+ }
+
+ if (substr($part, 0, 2) === '@@') {
+ $part = substr($part, 1);
+ }
+
+ // If it's a static part, match directly
+ if ($name === null && $part != $pathPart) {
+ return false;
+ }
+
+ // If it's a variable with requirement, match a regex. If not - everything matches
+ if ($part !== null && !preg_match($this->_regexDelimiter . '^' . $part . '$' . $this->_regexDelimiter . 'iu', $pathPart)) {
+ return false;
+ }
+
+ // If it's a variable store it's value for later
+ if ($name !== null) {
+ $values[$name] = $pathPart;
+ } else {
+ $pathStaticCount++;
+ }
+ }
+ }
+
+ // Check if all static mappings have been matched
+ if ($this->_staticCount != $pathStaticCount) {
+ return false;
+ }
+
+ $return = $values + $this->_wildcardData + $this->_defaults;
+
+ // Check if all map variables have been initialized
+ foreach ($this->_variables as $var) {
+ if (!array_key_exists($var, $return)) {
+ return false;
+ } elseif ($return[$var] == '' || $return[$var] === null) {
+ // Empty variable? Replace with the default value.
+ $return[$var] = $this->_defaults[$var];
+ }
+ }
+
+ $this->setMatchedPath(rtrim($matchedPath, $this->_urlDelimiter));
+
+ $this->_values = $values;
+
+ return $return;
+
+ }
+
+ /**
+ * Assembles user submitted parameters forming a URL path defined by this route
+ *
+ * @param array $data An array of variable and value pairs used as parameters
+ * @param boolean $reset Whether or not to set route defaults with those provided in $data
+ * @return string Route path with user submitted parameters
+ */
+ public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
+ {
+ if ($this->_isTranslated) {
+ $translator = $this->getTranslator();
+
+ if (isset($data['@locale'])) {
+ $locale = $data['@locale'];
+ unset($data['@locale']);
+ } else {
+ $locale = $this->getLocale();
+ }
+ }
+
+ $url = array();
+ $flag = false;
+
+ foreach ($this->_parts as $key => $part) {
+ $name = isset($this->_variables[$key]) ? $this->_variables[$key] : null;
+
+ $useDefault = false;
+ if (isset($name) && array_key_exists($name, $data) && $data[$name] === null) {
+ $useDefault = true;
+ }
+
+ if (isset($name)) {
+ if (isset($data[$name]) && !$useDefault) {
+ $value = $data[$name];
+ unset($data[$name]);
+ } elseif (!$reset && !$useDefault && isset($this->_values[$name])) {
+ $value = $this->_values[$name];
+ } elseif (!$reset && !$useDefault && isset($this->_wildcardData[$name])) {
+ $value = $this->_wildcardData[$name];
+ } elseif (array_key_exists($name, $this->_defaults)) {
+ $value = $this->_defaults[$name];
+ } else {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception($name . ' is not specified');
+ }
+
+ if ($this->_isTranslated && in_array($name, $this->_translatable)) {
+ $url[$key] = $translator->translate($value, $locale);
+ } else {
+ $url[$key] = $value;
+ }
+ } elseif ($part != '*') {
+ if ($this->_isTranslated && substr($part, 0, 1) === '@') {
+ if (substr($part, 1, 1) !== '@') {
+ $url[$key] = $translator->translate(substr($part, 1), $locale);
+ } else {
+ $url[$key] = substr($part, 1);
+ }
+ } else {
+ if (substr($part, 0, 2) === '@@') {
+ $part = substr($part, 1);
+ }
+
+ $url[$key] = $part;
+ }
+ } else {
+ if (!$reset) $data += $this->_wildcardData;
+ $defaults = $this->getDefaults();
+ foreach ($data as $var => $value) {
+ if ($value !== null && (!isset($defaults[$var]) || $value != $defaults[$var])) {
+ $url[$key++] = $var;
+ $url[$key++] = $value;
+ $flag = true;
+ }
+ }
+ }
+ }
+
+ $return = '';
+
+ foreach (array_reverse($url, true) as $key => $value) {
+ $defaultValue = null;
+
+ if (isset($this->_variables[$key])) {
+ $defaultValue = $this->getDefault($this->_variables[$key]);
+
+ if ($this->_isTranslated && $defaultValue !== null && isset($this->_translatable[$this->_variables[$key]])) {
+ $defaultValue = $translator->translate($defaultValue, $locale);
+ }
+ }
+
+ if ($flag || $value !== $defaultValue || $partial) {
+ if ($encode) $value = urlencode($value);
+ $return = $this->_urlDelimiter . $value . $return;
+ $flag = true;
+ }
+ }
+
+ return trim($return, $this->_urlDelimiter);
+
+ }
+
+ /**
+ * Return a single parameter of route's defaults
+ *
+ * @param string $name Array key of the parameter
+ * @return string Previously set default
+ */
+ public function getDefault($name) {
+ if (isset($this->_defaults[$name])) {
+ return $this->_defaults[$name];
+ }
+ return null;
+ }
+
+ /**
+ * Return an array of defaults
+ *
+ * @return array Route defaults
+ */
+ public function getDefaults() {
+ return $this->_defaults;
+ }
+
+ /**
+ * Get all variables which are used by the route
+ *
+ * @return array
+ */
+ public function getVariables()
+ {
+ return $this->_variables;
+ }
+
+ /**
+ * Set a default translator
+ *
+ * @param Zend_Translate $translator
+ * @return void
+ */
+ public static function setDefaultTranslator(Zend_Translate $translator = null)
+ {
+ self::$_defaultTranslator = $translator;
+ }
+
+ /**
+ * Get the default translator
+ *
+ * @return Zend_Translate
+ */
+ public static function getDefaultTranslator()
+ {
+ return self::$_defaultTranslator;
+ }
+
+ /**
+ * Set a translator
+ *
+ * @param Zend_Translate $translator
+ * @return void
+ */
+ public function setTranslator(Zend_Translate $translator)
+ {
+ $this->_translator = $translator;
+ }
+
+ /**
+ * Get the translator
+ *
+ * @throws Zend_Controller_Router_Exception When no translator can be found
+ * @return Zend_Translate
+ */
+ public function getTranslator()
+ {
+ if ($this->_translator !== null) {
+ return $this->_translator;
+ } else if (($translator = self::getDefaultTranslator()) !== null) {
+ return $translator;
+ } else {
+ try {
+ $translator = Zend_Registry::get('Zend_Translate');
+ } catch (Zend_Exception $e) {
+ $translator = null;
+ }
+
+ if ($translator instanceof Zend_Translate) {
+ return $translator;
+ }
+ }
+
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception('Could not find a translator');
+ }
+
+ /**
+ * Set a default locale
+ *
+ * @param mixed $locale
+ * @return void
+ */
+ public static function setDefaultLocale($locale = null)
+ {
+ self::$_defaultLocale = $locale;
+ }
+
+ /**
+ * Get the default locale
+ *
+ * @return mixed
+ */
+ public static function getDefaultLocale()
+ {
+ return self::$_defaultLocale;
+ }
+
+ /**
+ * Set a locale
+ *
+ * @param mixed $locale
+ * @return void
+ */
+ public function setLocale($locale)
+ {
+ $this->_locale = $locale;
+ }
+
+ /**
+ * Get the locale
+ *
+ * @return mixed
+ */
+ public function getLocale()
+ {
+ if ($this->_locale !== null) {
+ return $this->_locale;
+ } else if (($locale = self::getDefaultLocale()) !== null) {
+ return $locale;
+ } else {
+ try {
+ $locale = Zend_Registry::get('Zend_Locale');
+ } catch (Zend_Exception $e) {
+ $locale = null;
+ }
+
+ if ($locale !== null) {
+ return $locale;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/library/Zend/Controller/Router/Route/Abstract.php b/library/Zend/Controller/Router/Route/Abstract.php
new file mode 100644
index 0000000..929472f
--- /dev/null
+++ b/library/Zend/Controller/Router/Route/Abstract.php
@@ -0,0 +1,122 @@
+_matchedPath = $path;
+ }
+
+ /**
+ * Get partially matched path
+ *
+ * @return string
+ */
+ public function getMatchedPath()
+ {
+ return $this->_matchedPath;
+ }
+
+ /**
+ * Check or set wether this is an abstract route or not
+ *
+ * @param boolean $flag
+ * @return boolean
+ */
+ public function isAbstract($flag = null)
+ {
+ if ($flag !== null) {
+ $this->_isAbstract = $flag;
+ }
+
+ return $this->_isAbstract;
+ }
+
+ /**
+ * Create a new chain
+ *
+ * @param Zend_Controller_Router_Route_Abstract $route
+ * @param string $separator
+ * @return Zend_Controller_Router_Route_Chain
+ */
+ public function chain(Zend_Controller_Router_Route_Abstract $route, $separator = '/')
+ {
+ require_once 'Zend/Controller/Router/Route/Chain.php';
+
+ $chain = new Zend_Controller_Router_Route_Chain();
+ $chain->chain($this)->chain($route, $separator);
+
+ return $chain;
+ }
+
+}
diff --git a/library/Zend/Controller/Router/Route/Chain.php b/library/Zend/Controller/Router/Route/Chain.php
new file mode 100644
index 0000000..b09f6fa
--- /dev/null
+++ b/library/Zend/Controller/Router/Route/Chain.php
@@ -0,0 +1,173 @@
+defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
+ return new self($config->route, $defs);
+ }
+
+ /**
+ * Add a route to this chain
+ *
+ * @param Zend_Controller_Router_Route_Abstract $route
+ * @param string $separator
+ * @return Zend_Controller_Router_Route_Chain
+ */
+ public function chain(Zend_Controller_Router_Route_Abstract $route, $separator = self::URI_DELIMITER)
+ {
+ $this->_routes[] = $route;
+ $this->_separators[] = $separator;
+
+ return $this;
+
+ }
+
+ /**
+ * Matches a user submitted path with a previously defined route.
+ * Assigns and returns an array of defaults on a successful match.
+ *
+ * @param Zend_Controller_Request_Http $request Request to get the path info from
+ * @return array|false An array of assigned values or a false on a mismatch
+ */
+ public function match($request, $partial = null)
+ {
+ $path = trim($request->getPathInfo(), self::URI_DELIMITER);
+ $subPath = $path;
+ $values = array();
+
+ foreach ($this->_routes as $key => $route) {
+ if ($key > 0
+ && $matchedPath !== null
+ && $subPath !== ''
+ && $subPath !== false
+ ) {
+ $separator = substr($subPath, 0, strlen($this->_separators[$key]));
+
+ if ($separator !== $this->_separators[$key]) {
+ return false;
+ }
+
+ $subPath = substr($subPath, strlen($separator));
+ }
+
+ // TODO: Should be an interface method. Hack for 1.0 BC
+ if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) {
+ $match = $subPath;
+ } else {
+ $request->setPathInfo($subPath);
+ $match = $request;
+ }
+
+ $res = $route->match($match, true);
+ if ($res === false) {
+ return false;
+ }
+
+ $matchedPath = $route->getMatchedPath();
+
+ if ($matchedPath !== null) {
+ $subPath = substr($subPath, strlen($matchedPath));
+ $separator = substr($subPath, 0, strlen($this->_separators[$key]));
+ }
+
+ $values = $res + $values;
+ }
+
+ $request->setPathInfo($path);
+
+ if ($subPath !== '' && $subPath !== false) {
+ return false;
+ }
+
+ return $values;
+ }
+
+ /**
+ * Assembles a URL path defined by this route
+ *
+ * @param array $data An array of variable and value pairs used as parameters
+ * @return string Route path with user submitted parameters
+ */
+ public function assemble($data = array(), $reset = false, $encode = false)
+ {
+ $value = '';
+ $numRoutes = count($this->_routes);
+
+ foreach ($this->_routes as $key => $route) {
+ if ($key > 0) {
+ $value .= $this->_separators[$key];
+ }
+
+ $value .= $route->assemble($data, $reset, $encode, (($numRoutes - 1) > $key));
+
+ if (method_exists($route, 'getVariables')) {
+ $variables = $route->getVariables();
+
+ foreach ($variables as $variable) {
+ $data[$variable] = null;
+ }
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Set the request object for this and the child routes
+ *
+ * @param Zend_Controller_Request_Abstract|null $request
+ * @return void
+ */
+ public function setRequest(Zend_Controller_Request_Abstract $request = null)
+ {
+ $this->_request = $request;
+
+ foreach ($this->_routes as $route) {
+ if (method_exists($route, 'setRequest')) {
+ $route->setRequest($request);
+ }
+ }
+ }
+
+}
diff --git a/library/Zend/Controller/Router/Route/Hostname.php b/library/Zend/Controller/Router/Route/Hostname.php
new file mode 100644
index 0000000..f5c7044
--- /dev/null
+++ b/library/Zend/Controller/Router/Route/Hostname.php
@@ -0,0 +1,341 @@
+_request = $request;
+ }
+
+ /**
+ * Get the request object
+ *
+ * @return Zend_Controller_Request_Abstract $request
+ */
+ public function getRequest()
+ {
+ if ($this->_request === null) {
+ require_once 'Zend/Controller/Front.php';
+ $this->_request = Zend_Controller_Front::getInstance()->getRequest();
+ }
+
+ return $this->_request;
+ }
+
+ /**
+ * Instantiates route based on passed Zend_Config structure
+ *
+ * @param Zend_Config $config Configuration object
+ */
+ public static function getInstance(Zend_Config $config)
+ {
+ $reqs = ($config->reqs instanceof Zend_Config) ? $config->reqs->toArray() : array();
+ $defs = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
+ $scheme = (isset($config->scheme)) ? $config->scheme : null;
+ return new self($config->route, $defs, $reqs, $scheme);
+ }
+
+ /**
+ * Prepares the route for mapping by splitting (exploding) it
+ * to a corresponding atomic parts. These parts are assigned
+ * a position which is later used for matching and preparing values.
+ *
+ * @param string $route Map used to match with later submitted hostname
+ * @param array $defaults Defaults for map variables with keys as variable names
+ * @param array $reqs Regular expression requirements for variables (keys as variable names)
+ * @param string $scheme
+ */
+ public function __construct($route, $defaults = array(), $reqs = array(), $scheme = null)
+ {
+ $route = trim($route, '.');
+ $this->_defaults = (array) $defaults;
+ $this->_requirements = (array) $reqs;
+ $this->_scheme = $scheme;
+
+ if ($route != '') {
+ foreach (explode('.', $route) as $pos => $part) {
+ if (substr($part, 0, 1) == $this->_hostVariable) {
+ $name = substr($part, 1);
+ $this->_parts[$pos] = (isset($reqs[$name]) ? $reqs[$name] : $this->_defaultRegex);
+ $this->_variables[$pos] = $name;
+ } else {
+ $this->_parts[$pos] = $part;
+ $this->_staticCount++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Matches a user submitted path with parts defined by a map. Assigns and
+ * returns an array of variables on a successful match.
+ *
+ * @param Zend_Controller_Request_Http $request Request to get the host from
+ * @return array|false An array of assigned values or a false on a mismatch
+ */
+ public function match($request)
+ {
+ // Check the scheme if required
+ if ($this->_scheme !== null) {
+ $scheme = $request->getScheme();
+
+ if ($scheme !== $this->_scheme) {
+ return false;
+ }
+ }
+
+ // Get the host and remove unnecessary port information
+ $host = $request->getHttpHost();
+ if (preg_match('#:\d+$#', $host, $result) === 1) {
+ $host = substr($host, 0, -strlen($result[0]));
+ }
+
+ $hostStaticCount = 0;
+ $values = array();
+
+ $host = trim($host, '.');
+
+ if ($host != '') {
+ $host = explode('.', $host);
+
+ foreach ($host as $pos => $hostPart) {
+ // Host is longer than a route, it's not a match
+ if (!array_key_exists($pos, $this->_parts)) {
+ return false;
+ }
+
+ $name = isset($this->_variables[$pos]) ? $this->_variables[$pos] : null;
+ $hostPart = urldecode($hostPart);
+
+ // If it's a static part, match directly
+ if ($name === null && $this->_parts[$pos] != $hostPart) {
+ return false;
+ }
+
+ // If it's a variable with requirement, match a regex. If not - everything matches
+ if ($this->_parts[$pos] !== null && !preg_match($this->_regexDelimiter . '^' . $this->_parts[$pos] . '$' . $this->_regexDelimiter . 'iu', $hostPart)) {
+ return false;
+ }
+
+ // If it's a variable store it's value for later
+ if ($name !== null) {
+ $values[$name] = $hostPart;
+ } else {
+ $hostStaticCount++;
+ }
+ }
+ }
+
+ // Check if all static mappings have been matched
+ if ($this->_staticCount != $hostStaticCount) {
+ return false;
+ }
+
+ $return = $values + $this->_defaults;
+
+ // Check if all map variables have been initialized
+ foreach ($this->_variables as $var) {
+ if (!array_key_exists($var, $return)) {
+ return false;
+ }
+ }
+
+ $this->_values = $values;
+
+ return $return;
+
+ }
+
+ /**
+ * Assembles user submitted parameters forming a hostname defined by this route
+ *
+ * @param array $data An array of variable and value pairs used as parameters
+ * @param boolean $reset Whether or not to set route defaults with those provided in $data
+ * @return string Route path with user submitted parameters
+ */
+ public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
+ {
+ $host = array();
+ $flag = false;
+
+ foreach ($this->_parts as $key => $part) {
+ $name = isset($this->_variables[$key]) ? $this->_variables[$key] : null;
+
+ $useDefault = false;
+ if (isset($name) && array_key_exists($name, $data) && $data[$name] === null) {
+ $useDefault = true;
+ }
+
+ if (isset($name)) {
+ if (isset($data[$name]) && !$useDefault) {
+ $host[$key] = $data[$name];
+ unset($data[$name]);
+ } elseif (!$reset && !$useDefault && isset($this->_values[$name])) {
+ $host[$key] = $this->_values[$name];
+ } elseif (isset($this->_defaults[$name])) {
+ $host[$key] = $this->_defaults[$name];
+ } else {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception($name . ' is not specified');
+ }
+ } else {
+ $host[$key] = $part;
+ }
+ }
+
+ $return = '';
+
+ foreach (array_reverse($host, true) as $key => $value) {
+ if ($flag || !isset($this->_variables[$key]) || $value !== $this->getDefault($this->_variables[$key]) || $partial) {
+ if ($encode) $value = urlencode($value);
+ $return = '.' . $value . $return;
+ $flag = true;
+ }
+ }
+
+ $url = trim($return, '.');
+
+ if ($this->_scheme !== null) {
+ $scheme = $this->_scheme;
+ } else {
+ $request = $this->getRequest();
+ if ($request instanceof Zend_Controller_Request_Http) {
+ $scheme = $request->getScheme();
+ } else {
+ $scheme = 'http';
+ }
+ }
+
+ $url = $scheme . '://' . $url;
+
+ return $url;
+ }
+
+ /**
+ * Return a single parameter of route's defaults
+ *
+ * @param string $name Array key of the parameter
+ * @return string Previously set default
+ */
+ public function getDefault($name) {
+ if (isset($this->_defaults[$name])) {
+ return $this->_defaults[$name];
+ }
+ return null;
+ }
+
+ /**
+ * Return an array of defaults
+ *
+ * @return array Route defaults
+ */
+ public function getDefaults() {
+ return $this->_defaults;
+ }
+
+ /**
+ * Get all variables which are used by the route
+ *
+ * @return array
+ */
+ public function getVariables()
+ {
+ return $this->_variables;
+ }
+}
diff --git a/library/Zend/Controller/Router/Route/Interface.php b/library/Zend/Controller/Router/Route/Interface.php
new file mode 100644
index 0000000..36d6e34
--- /dev/null
+++ b/library/Zend/Controller/Router/Route/Interface.php
@@ -0,0 +1,37 @@
+defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
+ $dispatcher = $frontController->getDispatcher();
+ $request = $frontController->getRequest();
+
+ return new self($defs, $dispatcher, $request);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param array $defaults Defaults for map variables with keys as variable names
+ * @param Zend_Controller_Dispatcher_Interface $dispatcher Dispatcher object
+ * @param Zend_Controller_Request_Abstract $request Request object
+ */
+ public function __construct(array $defaults = array(),
+ Zend_Controller_Dispatcher_Interface $dispatcher = null,
+ Zend_Controller_Request_Abstract $request = null)
+ {
+ $this->_defaults = $defaults;
+
+ if (isset($request)) {
+ $this->_request = $request;
+ }
+
+ if (isset($dispatcher)) {
+ $this->_dispatcher = $dispatcher;
+ }
+ }
+
+ /**
+ * Set request keys based on values in request object
+ *
+ * @return void
+ */
+ protected function _setRequestKeys()
+ {
+ if (null !== $this->_request) {
+ $this->_moduleKey = $this->_request->getModuleKey();
+ $this->_controllerKey = $this->_request->getControllerKey();
+ $this->_actionKey = $this->_request->getActionKey();
+ }
+
+ if (null !== $this->_dispatcher) {
+ $this->_defaults += array(
+ $this->_controllerKey => $this->_dispatcher->getDefaultControllerName(),
+ $this->_actionKey => $this->_dispatcher->getDefaultAction(),
+ $this->_moduleKey => $this->_dispatcher->getDefaultModule()
+ );
+ }
+
+ $this->_keysSet = true;
+ }
+
+ /**
+ * Matches a user submitted path. Assigns and returns an array of variables
+ * on a successful match.
+ *
+ * If a request object is registered, it uses its setModuleName(),
+ * setControllerName(), and setActionName() accessors to set those values.
+ * Always returns the values as an array.
+ *
+ * @param string $path Path used to match against this routing map
+ * @return array An array of assigned values or a false on a mismatch
+ */
+ public function match($path, $partial = false)
+ {
+ $this->_setRequestKeys();
+
+ $values = array();
+ $params = array();
+
+ if (!$partial) {
+ $path = trim($path, self::URI_DELIMITER);
+ } else {
+ $matchedPath = $path;
+ }
+
+ if ($path != '') {
+ $path = explode(self::URI_DELIMITER, $path);
+
+ if ($this->_dispatcher && $this->_dispatcher->isValidModule($path[0])) {
+ $values[$this->_moduleKey] = array_shift($path);
+ $this->_moduleValid = true;
+ }
+
+ if (count($path) && !empty($path[0])) {
+ $values[$this->_controllerKey] = array_shift($path);
+ }
+
+ if (count($path) && !empty($path[0])) {
+ $values[$this->_actionKey] = array_shift($path);
+ }
+
+ if ($numSegs = count($path)) {
+ for ($i = 0; $i < $numSegs; $i = $i + 2) {
+ $key = urldecode($path[$i]);
+ $val = isset($path[$i + 1]) ? urldecode($path[$i + 1]) : null;
+ $params[$key] = (isset($params[$key]) ? (array_merge((array) $params[$key], array($val))): $val);
+ }
+ }
+ }
+
+ if ($partial) {
+ $this->setMatchedPath($matchedPath);
+ }
+
+ $this->_values = $values + $params;
+
+ return $this->_values + $this->_defaults;
+ }
+
+ /**
+ * Assembles user submitted parameters forming a URL path defined by this route
+ *
+ * @param array $data An array of variable and value pairs used as parameters
+ * @param bool $reset Weither to reset the current params
+ * @return string Route path with user submitted parameters
+ */
+ public function assemble($data = array(), $reset = false, $encode = true, $partial = false)
+ {
+ if (!$this->_keysSet) {
+ $this->_setRequestKeys();
+ }
+
+ $params = (!$reset) ? $this->_values : array();
+
+ foreach ($data as $key => $value) {
+ if ($value !== null) {
+ $params[$key] = $value;
+ } elseif (isset($params[$key])) {
+ unset($params[$key]);
+ }
+ }
+
+ $params += $this->_defaults;
+
+ $url = '';
+
+ if ($this->_moduleValid || array_key_exists($this->_moduleKey, $data)) {
+ if ($params[$this->_moduleKey] != $this->_defaults[$this->_moduleKey]) {
+ $module = $params[$this->_moduleKey];
+ }
+ }
+ unset($params[$this->_moduleKey]);
+
+ $controller = $params[$this->_controllerKey];
+ unset($params[$this->_controllerKey]);
+
+ $action = $params[$this->_actionKey];
+ unset($params[$this->_actionKey]);
+
+ foreach ($params as $key => $value) {
+ $key = ($encode) ? urlencode($key) : $key;
+ if (is_array($value)) {
+ foreach ($value as $arrayValue) {
+ $arrayValue = ($encode) ? urlencode($arrayValue) : $arrayValue;
+ $url .= self::URI_DELIMITER . $key;
+ $url .= self::URI_DELIMITER . $arrayValue;
+ }
+ } else {
+ if ($encode) $value = urlencode($value);
+ $url .= self::URI_DELIMITER . $key;
+ $url .= self::URI_DELIMITER . $value;
+ }
+ }
+
+ if (!empty($url) || $action !== $this->_defaults[$this->_actionKey]) {
+ if ($encode) $action = urlencode($action);
+ $url = self::URI_DELIMITER . $action . $url;
+ }
+
+ if (!empty($url) || $controller !== $this->_defaults[$this->_controllerKey]) {
+ if ($encode) $controller = urlencode($controller);
+ $url = self::URI_DELIMITER . $controller . $url;
+ }
+
+ if (isset($module)) {
+ if ($encode) $module = urlencode($module);
+ $url = self::URI_DELIMITER . $module . $url;
+ }
+
+ return ltrim($url, self::URI_DELIMITER);
+ }
+
+ /**
+ * Return a single parameter of route's defaults
+ *
+ * @param string $name Array key of the parameter
+ * @return string Previously set default
+ */
+ public function getDefault($name) {
+ if (isset($this->_defaults[$name])) {
+ return $this->_defaults[$name];
+ }
+ }
+
+ /**
+ * Return an array of defaults
+ *
+ * @return array Route defaults
+ */
+ public function getDefaults() {
+ return $this->_defaults;
+ }
+
+}
diff --git a/library/Zend/Controller/Router/Route/Regex.php b/library/Zend/Controller/Router/Route/Regex.php
new file mode 100644
index 0000000..72ac23c
--- /dev/null
+++ b/library/Zend/Controller/Router/Route/Regex.php
@@ -0,0 +1,269 @@
+defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
+ $map = ($config->map instanceof Zend_Config) ? $config->map->toArray() : array();
+ $reverse = (isset($config->reverse)) ? $config->reverse : null;
+ return new self($config->route, $defs, $map, $reverse);
+ }
+
+ public function __construct($route, $defaults = array(), $map = array(), $reverse = null)
+ {
+ $this->_regex = $route;
+ $this->_defaults = (array) $defaults;
+ $this->_map = (array) $map;
+ $this->_reverse = $reverse;
+ }
+
+ public function getVersion() {
+ return 1;
+ }
+
+ /**
+ * Matches a user submitted path with a previously defined route.
+ * Assigns and returns an array of defaults on a successful match.
+ *
+ * @param string $path Path used to match against this routing map
+ * @return array|false An array of assigned values or a false on a mismatch
+ */
+ public function match($path, $partial = false)
+ {
+ if (!$partial) {
+ $path = trim(urldecode($path), self::URI_DELIMITER);
+ $regex = '#^' . $this->_regex . '$#i';
+ } else {
+ $regex = '#^' . $this->_regex . '#i';
+ }
+
+ $res = preg_match($regex, $path, $values);
+
+ if ($res === 0) {
+ return false;
+ }
+
+ if ($partial) {
+ $this->setMatchedPath($values[0]);
+ }
+
+ // array_filter_key()? Why isn't this in a standard PHP function set yet? :)
+ foreach ($values as $i => $value) {
+ if (!is_int($i) || $i === 0) {
+ unset($values[$i]);
+ }
+ }
+
+ $this->_values = $values;
+
+ $values = $this->_getMappedValues($values);
+ $defaults = $this->_getMappedValues($this->_defaults, false, true);
+ $return = $values + $defaults;
+
+ return $return;
+ }
+
+ /**
+ * Maps numerically indexed array values to it's associative mapped counterpart.
+ * Or vice versa. Uses user provided map array which consists of index => name
+ * parameter mapping. If map is not found, it returns original array.
+ *
+ * Method strips destination type of keys form source array. Ie. if source array is
+ * indexed numerically then every associative key will be stripped. Vice versa if reversed
+ * is set to true.
+ *
+ * @param array $values Indexed or associative array of values to map
+ * @param boolean $reversed False means translation of index to association. True means reverse.
+ * @param boolean $preserve Should wrong type of keys be preserved or stripped.
+ * @return array An array of mapped values
+ */
+ protected function _getMappedValues($values, $reversed = false, $preserve = false)
+ {
+ if (count($this->_map) == 0) {
+ return $values;
+ }
+
+ $return = array();
+
+ foreach ($values as $key => $value) {
+ if (is_int($key) && !$reversed) {
+ if (array_key_exists($key, $this->_map)) {
+ $index = $this->_map[$key];
+ } elseif (false === ($index = array_search($key, $this->_map))) {
+ $index = $key;
+ }
+ $return[$index] = $values[$key];
+ } elseif ($reversed) {
+ $index = $key;
+ if (!is_int($key)) {
+ if (array_key_exists($key, $this->_map)) {
+ $index = $this->_map[$key];
+ } else {
+ $index = array_search($key, $this->_map, true);
+ }
+ }
+ if (false !== $index) {
+ $return[$index] = $values[$key];
+ }
+ } elseif ($preserve) {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Assembles a URL path defined by this route
+ *
+ * @param array $data An array of name (or index) and value pairs used as parameters
+ * @return string Route path with user submitted parameters
+ */
+ public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
+ {
+ if ($this->_reverse === null) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception('Cannot assemble. Reversed route is not specified.');
+ }
+
+ $defaultValuesMapped = $this->_getMappedValues($this->_defaults, true, false);
+ $matchedValuesMapped = $this->_getMappedValues($this->_values, true, false);
+ $dataValuesMapped = $this->_getMappedValues($data, true, false);
+
+ // handle resets, if so requested (By null value) to do so
+ if (($resetKeys = array_search(null, $dataValuesMapped, true)) !== false) {
+ foreach ((array) $resetKeys as $resetKey) {
+ if (isset($matchedValuesMapped[$resetKey])) {
+ unset($matchedValuesMapped[$resetKey]);
+ unset($dataValuesMapped[$resetKey]);
+ }
+ }
+ }
+
+ // merge all the data together, first defaults, then values matched, then supplied
+ $mergedData = $defaultValuesMapped;
+ $mergedData = $this->_arrayMergeNumericKeys($mergedData, $matchedValuesMapped);
+ $mergedData = $this->_arrayMergeNumericKeys($mergedData, $dataValuesMapped);
+
+ if ($encode) {
+ foreach ($mergedData as $key => &$value) {
+ $value = urlencode($value);
+ }
+ }
+
+ ksort($mergedData);
+
+ $return = @vsprintf($this->_reverse, $mergedData);
+
+ if ($return === false) {
+ require_once 'Zend/Controller/Router/Exception.php';
+ throw new Zend_Controller_Router_Exception('Cannot assemble. Too few arguments?');
+ }
+
+ return $return;
+
+ }
+
+ /**
+ * Return a single parameter of route's defaults
+ *
+ * @param string $name Array key of the parameter
+ * @return string Previously set default
+ */
+ public function getDefault($name) {
+ if (isset($this->_defaults[$name])) {
+ return $this->_defaults[$name];
+ }
+ }
+
+ /**
+ * Return an array of defaults
+ *
+ * @return array Route defaults
+ */
+ public function getDefaults() {
+ return $this->_defaults;
+ }
+
+ /**
+ * Get all variables which are used by the route
+ *
+ * @return array
+ */
+ public function getVariables()
+ {
+ $variables = array();
+
+ foreach ($this->_map as $key => $value) {
+ if (is_numeric($key)) {
+ $variables[] = $value;
+ } else {
+ $variables[] = $key;
+ }
+ }
+
+ return $variables;
+ }
+
+ /**
+ * _arrayMergeNumericKeys() - allows for a strict key (numeric's included) array_merge.
+ * php's array_merge() lacks the ability to merge with numeric keys.
+ *
+ * @param array $array1
+ * @param array $array2
+ * @return array
+ */
+ protected function _arrayMergeNumericKeys(Array $array1, Array $array2)
+ {
+ $returnArray = $array1;
+ foreach ($array2 as $array2Index => $array2Value) {
+ $returnArray[$array2Index] = $array2Value;
+ }
+ return $returnArray;
+ }
+
+
+}
diff --git a/library/Zend/Controller/Router/Route/Static.php b/library/Zend/Controller/Router/Route/Static.php
new file mode 100644
index 0000000..46d7c60
--- /dev/null
+++ b/library/Zend/Controller/Router/Route/Static.php
@@ -0,0 +1,127 @@
+defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
+ return new self($config->route, $defs);
+ }
+
+ /**
+ * Prepares the route for mapping.
+ *
+ * @param string $route Map used to match with later submitted URL path
+ * @param array $defaults Defaults for map variables with keys as variable names
+ */
+ public function __construct($route, $defaults = array())
+ {
+ $this->_route = trim($route, self::URI_DELIMITER);
+ $this->_defaults = (array) $defaults;
+ }
+
+ /**
+ * Matches a user submitted path with a previously defined route.
+ * Assigns and returns an array of defaults on a successful match.
+ *
+ * @param string $path Path used to match against this routing map
+ * @return array|false An array of assigned values or a false on a mismatch
+ */
+ public function match($path, $partial = false)
+ {
+ if ($partial) {
+ if ((empty($path) && empty($this->_route))
+ || (substr($path, 0, strlen($this->_route)) === $this->_route)
+ ) {
+ $this->setMatchedPath($this->_route);
+ return $this->_defaults;
+ }
+ } else {
+ if (trim($path, self::URI_DELIMITER) == $this->_route) {
+ return $this->_defaults;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Assembles a URL path defined by this route
+ *
+ * @param array $data An array of variable and value pairs used as parameters
+ * @return string Route path with user submitted parameters
+ */
+ public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
+ {
+ return $this->_route;
+ }
+
+ /**
+ * Return a single parameter of route's defaults
+ *
+ * @param string $name Array key of the parameter
+ * @return string Previously set default
+ */
+ public function getDefault($name) {
+ if (isset($this->_defaults[$name])) {
+ return $this->_defaults[$name];
+ }
+ return null;
+ }
+
+ /**
+ * Return an array of defaults
+ *
+ * @return array Route defaults
+ */
+ public function getDefaults() {
+ return $this->_defaults;
+ }
+
+}
diff --git a/library/Zend/Crypt.php b/library/Zend/Crypt.php
new file mode 100644
index 0000000..cc3405a
--- /dev/null
+++ b/library/Zend/Crypt.php
@@ -0,0 +1,168 @@
+setPrime($prime);
+ $this->setGenerator($generator);
+ if ($privateKey !== null) {
+ $this->setPrivateKey($privateKey, $privateKeyType);
+ }
+ $this->setBigIntegerMath();
+ }
+
+ /**
+ * Generate own public key. If a private number has not already been
+ * set, one will be generated at this stage.
+ *
+ * @return Zend_Crypt_DiffieHellman
+ */
+ public function generateKeys()
+ {
+ if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) {
+ $details = array();
+ $details['p'] = $this->getPrime();
+ $details['g'] = $this->getGenerator();
+ if ($this->hasPrivateKey()) {
+ $details['priv_key'] = $this->getPrivateKey();
+ }
+ $opensslKeyResource = openssl_pkey_new( array('dh' => $details) );
+ $data = openssl_pkey_get_details($opensslKeyResource);
+ $this->setPrivateKey($data['dh']['priv_key'], self::BINARY);
+ $this->setPublicKey($data['dh']['pub_key'], self::BINARY);
+ } else {
+ // Private key is lazy generated in the absence of PHP 5.3's ext/openssl
+ $publicKey = $this->_math->powmod($this->getGenerator(), $this->getPrivateKey(), $this->getPrime());
+ $this->setPublicKey($publicKey);
+ }
+ return $this;
+ }
+
+ /**
+ * Setter for the value of the public number
+ *
+ * @param string $number
+ * @param string $type
+ * @return Zend_Crypt_DiffieHellman
+ */
+ public function setPublicKey($number, $type = self::NUMBER)
+ {
+ if ($type == self::BINARY) {
+ $number = $this->_math->fromBinary($number);
+ }
+ if (!preg_match("/^\d+$/", $number)) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number');
+ }
+ $this->_publicKey = (string) $number;
+ return $this;
+ }
+
+ /**
+ * Returns own public key for communication to the second party to this
+ * transaction.
+ *
+ * @param string $type
+ * @return string
+ */
+ public function getPublicKey($type = self::NUMBER)
+ {
+ if ($this->_publicKey === null) {
+ require_once 'Zend/Crypt/DiffieHellman/Exception.php';
+ throw new Zend_Crypt_DiffieHellman_Exception('A public key has not yet been generated using a prior call to generateKeys()');
+ }
+ if ($type == self::BINARY) {
+ return $this->_math->toBinary($this->_publicKey);
+ } elseif ($type == self::BTWOC) {
+ return $this->_math->btwoc($this->_math->toBinary($this->_publicKey));
+ }
+ return $this->_publicKey;
+ }
+
+ /**
+ * Compute the shared secret key based on the public key received from the
+ * the second party to this transaction. This should agree to the secret
+ * key the second party computes on our own public key.
+ * Once in agreement, the key is known to only to both parties.
+ * By default, the function expects the public key to be in binary form
+ * which is the typical format when being transmitted.
+ *
+ * If you need the binary form of the shared secret key, call
+ * getSharedSecretKey() with the optional parameter for Binary output.
+ *
+ * @param string $publicKey
+ * @param string $type
+ * @return mixed
+ */
+ public function computeSecretKey($publicKey, $type = self::NUMBER, $output = self::NUMBER)
+ {
+ if ($type == self::BINARY) {
+ $publicKey = $this->_math->fromBinary($publicKey);
+ }
+ if (!preg_match("/^\d+$/", $publicKey)) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number');
+ }
+ if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) {
+ $this->_secretKey = openssl_dh_compute_key($publicKey, $this->getPublicKey());
+ } else {
+ $this->_secretKey = $this->_math->powmod($publicKey, $this->getPrivateKey(), $this->getPrime());
+ }
+ return $this->getSharedSecretKey($output);
+ }
+
+ /**
+ * Return the computed shared secret key from the DiffieHellman transaction
+ *
+ * @param string $type
+ * @return string
+ */
+ public function getSharedSecretKey($type = self::NUMBER)
+ {
+ if (!isset($this->_secretKey)) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('A secret key has not yet been computed; call computeSecretKey()');
+ }
+ if ($type == self::BINARY) {
+ return $this->_math->toBinary($this->_secretKey);
+ } elseif ($type == self::BTWOC) {
+ return $this->_math->btwoc($this->_math->toBinary($this->_secretKey));
+ }
+ return $this->_secretKey;
+ }
+
+ /**
+ * Setter for the value of the prime number
+ *
+ * @param string $number
+ * @return Zend_Crypt_DiffieHellman
+ */
+ public function setPrime($number)
+ {
+ if (!preg_match("/^\d+$/", $number) || $number < 11) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number or too small: should be a large natural number prime');
+ }
+ $this->_prime = (string) $number;
+ return $this;
+ }
+
+ /**
+ * Getter for the value of the prime number
+ *
+ * @return string
+ */
+ public function getPrime()
+ {
+ if (!isset($this->_prime)) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('No prime number has been set');
+ }
+ return $this->_prime;
+ }
+
+
+ /**
+ * Setter for the value of the generator number
+ *
+ * @param string $number
+ * @return Zend_Crypt_DiffieHellman
+ */
+ public function setGenerator($number)
+ {
+ if (!preg_match("/^\d+$/", $number) || $number < 2) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number greater than 1');
+ }
+ $this->_generator = (string) $number;
+ return $this;
+ }
+
+ /**
+ * Getter for the value of the generator number
+ *
+ * @return string
+ */
+ public function getGenerator()
+ {
+ if (!isset($this->_generator)) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('No generator number has been set');
+ }
+ return $this->_generator;
+ }
+
+ /**
+ * Setter for the value of the private number
+ *
+ * @param string $number
+ * @param string $type
+ * @return Zend_Crypt_DiffieHellman
+ */
+ public function setPrivateKey($number, $type = self::NUMBER)
+ {
+ if ($type == self::BINARY) {
+ $number = $this->_math->fromBinary($number);
+ }
+ if (!preg_match("/^\d+$/", $number)) {
+ require_once('Zend/Crypt/DiffieHellman/Exception.php');
+ throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number');
+ }
+ $this->_privateKey = (string) $number;
+ return $this;
+ }
+
+ /**
+ * Getter for the value of the private number
+ *
+ * @param string $type
+ * @return string
+ */
+ public function getPrivateKey($type = self::NUMBER)
+ {
+ if (!$this->hasPrivateKey()) {
+ $this->setPrivateKey($this->_generatePrivateKey(), self::BINARY);
+ }
+ if ($type == self::BINARY) {
+ return $this->_math->toBinary($this->_privateKey);
+ } elseif ($type == self::BTWOC) {
+ return $this->_math->btwoc($this->_math->toBinary($this->_privateKey));
+ }
+ return $this->_privateKey;
+ }
+
+ /**
+ * Check whether a private key currently exists.
+ *
+ * @return boolean
+ */
+ public function hasPrivateKey()
+ {
+ return isset($this->_privateKey);
+ }
+
+ /**
+ * Setter to pass an extension parameter which is used to create
+ * a specific BigInteger instance for a specific extension type.
+ * Allows manual setting of the class in case of an extension
+ * problem or bug.
+ *
+ * @param string $extension
+ * @return void
+ */
+ public function setBigIntegerMath($extension = null)
+ {
+ /**
+ * @see Zend_Crypt_Math
+ */
+ require_once 'Zend/Crypt/Math.php';
+ $this->_math = new Zend_Crypt_Math($extension);
+ }
+
+ /**
+ * In the event a private number/key has not been set by the user,
+ * or generated by ext/openssl, a best attempt will be made to
+ * generate a random key. Having a random number generator installed
+ * on linux/bsd is highly recommended! The alternative is not recommended
+ * for production unless without any other option.
+ *
+ * @return string
+ */
+ protected function _generatePrivateKey()
+ {
+ $rand = $this->_math->rand($this->getGenerator(), $this->getPrime());
+ return $rand;
+ }
+
+}
diff --git a/library/Zend/Crypt/DiffieHellman/Exception.php b/library/Zend/Crypt/DiffieHellman/Exception.php
new file mode 100644
index 0000000..34d85b3
--- /dev/null
+++ b/library/Zend/Crypt/DiffieHellman/Exception.php
@@ -0,0 +1,36 @@
+80 using internal algo)
+ * @todo Check if mhash() is a required alternative (will be PECL-only soon)
+ * @category Zend
+ * @package Zend_Crypt
+ * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Crypt_Hmac extends Zend_Crypt
+{
+
+ /**
+ * The key to use for the hash
+ *
+ * @var string
+ */
+ protected static $_key = null;
+
+ /**
+ * pack() format to be used for current hashing method
+ *
+ * @var string
+ */
+ protected static $_packFormat = null;
+
+ /**
+ * Hashing algorithm; can be the md5/sha1 functions or any algorithm name
+ * listed in the output of PHP 5.1.2+ hash_algos().
+ *
+ * @var string
+ */
+ protected static $_hashAlgorithm = 'md5';
+
+ /**
+ * List of algorithms supported my mhash()
+ *
+ * @var array
+ */
+ protected static $_supportedMhashAlgorithms = array('adler32',' crc32', 'crc32b', 'gost',
+ 'haval128', 'haval160', 'haval192', 'haval256', 'md4', 'md5', 'ripemd160',
+ 'sha1', 'sha256', 'tiger', 'tiger128', 'tiger160');
+
+ /**
+ * Constants representing the output mode of the hash algorithm
+ */
+ const STRING = 'string';
+ const BINARY = 'binary';
+
+ /**
+ * Performs a HMAC computation given relevant details such as Key, Hashing
+ * algorithm, the data to compute MAC of, and an output format of String,
+ * Binary notation or BTWOC.
+ *
+ * @param string $key
+ * @param string $hash
+ * @param string $data
+ * @param string $output
+ * @param boolean $internal
+ * @return string
+ */
+ public static function compute($key, $hash, $data, $output = self::STRING)
+ {
+ // set the key
+ if (!isset($key) || empty($key)) {
+ require_once 'Zend/Crypt/Hmac/Exception.php';
+ throw new Zend_Crypt_Hmac_Exception('provided key is null or empty');
+ }
+ self::$_key = $key;
+
+ // set the hash
+ self::_setHashAlgorithm($hash);
+
+ // perform hashing and return
+ return self::_hash($data, $output);
+ }
+
+ /**
+ * Setter for the hash method.
+ *
+ * @param string $hash
+ * @return Zend_Crypt_Hmac
+ */
+ protected static function _setHashAlgorithm($hash)
+ {
+ if (!isset($hash) || empty($hash)) {
+ require_once 'Zend/Crypt/Hmac/Exception.php';
+ throw new Zend_Crypt_Hmac_Exception('provided hash string is null or empty');
+ }
+
+ $hash = strtolower($hash);
+ $hashSupported = false;
+
+ if (function_exists('hash_algos') && in_array($hash, hash_algos())) {
+ $hashSupported = true;
+ }
+
+ if ($hashSupported === false && function_exists('mhash') && in_array($hash, self::$_supportedAlgosMhash)) {
+ $hashSupported = true;
+ }
+
+ if ($hashSupported === false) {
+ require_once 'Zend/Crypt/Hmac/Exception.php';
+ throw new Zend_Crypt_Hmac_Exception('hash algorithm provided is not supported on this PHP installation; please enable the hash or mhash extensions');
+ }
+ self::$_hashAlgorithm = $hash;
+ }
+
+ /**
+ * Perform HMAC and return the keyed data
+ *
+ * @param string $data
+ * @param string $output
+ * @param bool $internal Option to not use hash() functions for testing
+ * @return string
+ */
+ protected static function _hash($data, $output = self::STRING, $internal = false)
+ {
+ if (function_exists('hash_hmac')) {
+ if ($output == self::BINARY) {
+ return hash_hmac(self::$_hashAlgorithm, $data, self::$_key, 1);
+ }
+ return hash_hmac(self::$_hashAlgorithm, $data, self::$_key);
+ }
+
+ if (function_exists('mhash')) {
+ if ($output == self::BINARY) {
+ return mhash(self::_getMhashDefinition(self::$_hashAlgorithm), $data, self::$_key);
+ }
+ $bin = mhash(self::_getMhashDefinition(self::$_hashAlgorithm), $data, self::$_key);
+ return bin2hex($bin);
+ }
+ }
+
+ /**
+ * Since MHASH accepts an integer constant representing the hash algorithm
+ * we need to make a small detour to get the correct integer matching our
+ * algorithm's name.
+ *
+ * @param string $hashAlgorithm
+ * @return integer
+ */
+ protected static function _getMhashDefinition($hashAlgorithm)
+ {
+ for ($i = 0; $i <= mhash_count(); $i++)
+ {
+ $types[mhash_get_hash_name($i)] = $i;
+ }
+ return $types[strtoupper($hashAlgorithm)];
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Crypt/Hmac/Exception.php b/library/Zend/Crypt/Hmac/Exception.php
new file mode 100644
index 0000000..ebb8082
--- /dev/null
+++ b/library/Zend/Crypt/Hmac/Exception.php
@@ -0,0 +1,36 @@
+ 127) {
+ return "\x00" . $long;
+ }
+ return $long;
+ }
+
+ /**
+ * Translate a binary form into a big integer string
+ *
+ * @param string $binary
+ * @return string
+ */
+ public function fromBinary($binary) {
+ return $this->_math->binaryToInteger($binary);
+ }
+
+ /**
+ * Translate a big integer string into a binary form
+ *
+ * @param string $integer
+ * @return string
+ */
+ public function toBinary($integer)
+ {
+ return $this->_math->integerToBinary($integer);
+ }
+
+}
diff --git a/library/Zend/Crypt/Math/BigInteger.php b/library/Zend/Crypt/Math/BigInteger.php
new file mode 100644
index 0000000..750ae70
--- /dev/null
+++ b/library/Zend/Crypt/Math/BigInteger.php
@@ -0,0 +1,117 @@
+_loadAdapter($extension);
+ }
+
+ /**
+ * Redirect all public method calls to the wrapped extension object.
+ *
+ * @param string $methodName
+ * @param array $args
+ * @throws Zend_Crypt_Math_BigInteger_Exception
+ */
+ public function __call($methodName, $args)
+ {
+ if(!method_exists($this->_math, $methodName)) {
+ require_once 'Zend/Crypt/Math/BigInteger/Exception.php';
+ throw new Zend_Crypt_Math_BigInteger_Exception('invalid method call: ' . get_class($this->_math) . '::' . $methodName . '() does not exist');
+ }
+ return call_user_func_array(array($this->_math, $methodName), $args);
+ }
+
+ /**
+ * @param string $extension
+ * @throws Zend_Crypt_Math_BigInteger_Exception
+ */
+ protected function _loadAdapter($extension = null)
+ {
+ if ($extension === null) {
+ if (extension_loaded('gmp')) {
+ $extension = 'gmp';
+ //} elseif (extension_loaded('big_int')) {
+ // $extension = 'big_int';
+ } else {
+ $extension = 'bcmath';
+ }
+ }
+ if($extension == 'gmp' && extension_loaded('gmp')) {
+ require_once 'Zend/Crypt/Math/BigInteger/Gmp.php';
+ $this->_math = new Zend_Crypt_Math_BigInteger_Gmp();
+ //} elseif($extension == 'bigint' && extension_loaded('big_int')) {
+ // require_once 'Zend/Crypt_Math/BigInteger/Bigint.php';
+ // $this->_math = new Zend_Crypt_Math_BigInteger_Bigint();
+ } elseif ($extension == 'bcmath' && extension_loaded('bcmath')) {
+ require_once 'Zend/Crypt/Math/BigInteger/Bcmath.php';
+ $this->_math = new Zend_Crypt_Math_BigInteger_Bcmath();
+ } else {
+ require_once 'Zend/Crypt/Math/BigInteger/Exception.php';
+ throw new Zend_Crypt_Math_BigInteger_Exception($extension . ' big integer precision math support not detected');
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Crypt/Math/BigInteger/Bcmath.php b/library/Zend/Crypt/Math/BigInteger/Bcmath.php
new file mode 100644
index 0000000..ca67d14
--- /dev/null
+++ b/library/Zend/Crypt/Math/BigInteger/Bcmath.php
@@ -0,0 +1,203 @@
+ 0) {
+ $return = chr(bcmod($operand, 256)) . $return;
+ $operand = bcdiv($operand, 256);
+ }
+ if (ord($return[0]) > 127) {
+ $return = "\0" . $return;
+ }
+ return $return;
+ }
+
+ /**public function integerToBinary($operand)
+ {
+ $return = '';
+ while(bccomp($operand, '0')) {
+ $return .= chr(bcmod($operand, '256'));
+ $operand = bcdiv($operand, '256');
+ }
+ return $return;
+ }**/ // Prior version for referenced offset
+
+
+ public function hexToDecimal($operand)
+ {
+ $return = '0';
+ while(strlen($hex)) {
+ $hex = hexdec(substr($operand, 0, 4));
+ $dec = bcadd(bcmul($return, 65536), $hex);
+ $operand = substr($operand, 4);
+ }
+ return $return;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Crypt/Math/BigInteger/Exception.php b/library/Zend/Crypt/Math/BigInteger/Exception.php
new file mode 100644
index 0000000..bebfdae
--- /dev/null
+++ b/library/Zend/Crypt/Math/BigInteger/Exception.php
@@ -0,0 +1,36 @@
+ '7') {
+ $bigInt = '00' . $bigInt;
+ }
+ $return = pack("H*", $bigInt);
+ return $return;
+ }
+
+
+ public function hexToDecimal($operand)
+ {
+ $return = '0';
+ while(strlen($hex)) {
+ $hex = hexdec(substr($operand, 0, 4));
+ $dec = gmp_add(gmp_mul($return, 65536), $hex);
+ $operand = substr($operand, 4);
+ }
+ return $return;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Crypt/Math/BigInteger/Interface.php b/library/Zend/Crypt/Math/BigInteger/Interface.php
new file mode 100644
index 0000000..9dace60
--- /dev/null
+++ b/library/Zend/Crypt/Math/BigInteger/Interface.php
@@ -0,0 +1,51 @@
+_hashAlgorithm = OPENSSL_ALGO_SHA1;
+
+ if (isset($options)) {
+ $this->setOptions($options);
+ }
+ }
+
+ public function setOptions(array $options)
+ {
+ if (isset($options['passPhrase'])) {
+ $this->_passPhrase = $options['passPhrase'];
+ }
+ foreach ($options as $option=>$value) {
+ switch ($option) {
+ case 'pemString':
+ $this->setPemString($value);
+ break;
+ case 'pemPath':
+ $this->setPemPath($value);
+ break;
+ case 'certificateString':
+ $this->setCertificateString($value);
+ break;
+ case 'certificatePath':
+ $this->setCertificatePath($value);
+ break;
+ case 'hashAlgorithm':
+ $this->setHashAlgorithm($value);
+ break;
+ }
+ }
+ }
+
+ public function getPrivateKey()
+ {
+ return $this->_privateKey;
+ }
+
+ public function getPublicKey()
+ {
+ return $this->_publicKey;
+ }
+
+ /**
+ * @param string $data
+ * @param Zend_Crypt_Rsa_Key_Private $privateKey
+ * @param string $format
+ * @return string
+ */
+ public function sign($data, Zend_Crypt_Rsa_Key_Private $privateKey = null, $format = null)
+ {
+ $signature = '';
+ if (isset($privateKey)) {
+ $opensslKeyResource = $privateKey->getOpensslKeyResource();
+ } else {
+ $opensslKeyResource = $this->_privateKey->getOpensslKeyResource();
+ }
+ $result = openssl_sign(
+ $data, $signature,
+ $opensslKeyResource,
+ $this->getHashAlgorithm()
+ );
+ if ($format == self::BASE64) {
+ return base64_encode($signature);
+ }
+ return $signature;
+ }
+
+ /**
+ * @param string $data
+ * @param string $signature
+ * @param string $format
+ * @return string
+ */
+ public function verifySignature($data, $signature, $format = null)
+ {
+ if ($format == self::BASE64) {
+ $signature = base64_decode($signature);
+ }
+ $result = openssl_verify($data, $signature,
+ $this->getPublicKey()->getOpensslKeyResource(),
+ $this->getHashAlgorithm());
+ return $result;
+ }
+
+ /**
+ * @param string $data
+ * @param Zend_Crypt_Rsa_Key $key
+ * @param string $format
+ * @return string
+ */
+ public function encrypt($data, Zend_Crypt_Rsa_Key $key, $format = null)
+ {
+ $encrypted = '';
+ $function = 'openssl_public_encrypt';
+ if ($key instanceof Zend_Crypt_Rsa_Key_Private) {
+ $function = 'openssl_private_encrypt';
+ }
+ $function($data, $encrypted, $key->getOpensslKeyResource());
+ if ($format == self::BASE64) {
+ return base64_encode($encrypted);
+ }
+ return $encrypted;
+ }
+
+ /**
+ * @param string $data
+ * @param Zend_Crypt_Rsa_Key $key
+ * @param string $format
+ * @return string
+ */
+ public function decrypt($data, Zend_Crypt_Rsa_Key $key, $format = null)
+ {
+ $decrypted = '';
+ if ($format == self::BASE64) {
+ $data = base64_decode($data);
+ }
+ $function = 'openssl_private_decrypt';
+ if ($key instanceof Zend_Crypt_Rsa_Key_Public) {
+ $function = 'openssl_public_decrypt';
+ }
+ $function($data, $decrypted, $key->getOpensslKeyResource());
+ return $decrypted;
+ }
+
+ public function generateKeys(array $configargs = null)
+ {
+ $config = null;
+ $passPhrase = null;
+ if ($configargs !== null) {
+ if (isset($configargs['passPhrase'])) {
+ $passPhrase = $configargs['passPhrase'];
+ unset($configargs['passPhrase']);
+ }
+ $config = $this->_parseConfigArgs($configargs);
+ }
+ $privateKey = null;
+ $publicKey = null;
+ $resource = openssl_pkey_new($config);
+ // above fails on PHP 5.3
+ openssl_pkey_export($resource, $private, $passPhrase);
+ $privateKey = new Zend_Crypt_Rsa_Key_Private($private, $passPhrase);
+ $details = openssl_pkey_get_details($resource);
+ $publicKey = new Zend_Crypt_Rsa_Key_Public($details['key']);
+ $return = new ArrayObject(array(
+ 'privateKey'=>$privateKey,
+ 'publicKey'=>$publicKey
+ ), ArrayObject::ARRAY_AS_PROPS);
+ return $return;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setPemString($value)
+ {
+ $this->_pemString = $value;
+ try {
+ $this->_privateKey = new Zend_Crypt_Rsa_Key_Private($this->_pemString, $this->_passPhrase);
+ $this->_publicKey = $this->_privateKey->getPublicKey();
+ } catch (Zend_Crypt_Exception $e) {
+ $this->_privateKey = null;
+ $this->_publicKey = new Zend_Crypt_Rsa_Key_Public($this->_pemString);
+ }
+ }
+
+ public function setPemPath($value)
+ {
+ $this->_pemPath = $value;
+ $this->setPemString(file_get_contents($this->_pemPath));
+ }
+
+ public function setCertificateString($value)
+ {
+ $this->_certificateString = $value;
+ $this->_publicKey = new Zend_Crypt_Rsa_Key_Public($this->_certificateString, $this->_passPhrase);
+ }
+
+ public function setCertificatePath($value)
+ {
+ $this->_certificatePath = $value;
+ $this->setCertificateString(file_get_contents($this->_certificatePath));
+ }
+
+ public function setHashAlgorithm($name)
+ {
+ switch (strtolower($name)) {
+ case 'md2':
+ $this->_hashAlgorithm = OPENSSL_ALGO_MD2;
+ break;
+ case 'md4':
+ $this->_hashAlgorithm = OPENSSL_ALGO_MD4;
+ break;
+ case 'md5':
+ $this->_hashAlgorithm = OPENSSL_ALGO_MD5;
+ break;
+ case 'sha1':
+ $this->_hashAlgorithm = OPENSSL_ALGO_SHA1;
+ break;
+ case 'dss1':
+ $this->_hashAlgorithm = OPENSSL_ALGO_DSS1;
+ break;
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getPemString()
+ {
+ return $this->_pemString;
+ }
+
+ public function getPemPath()
+ {
+ return $this->_pemPath;
+ }
+
+ public function getCertificateString()
+ {
+ return $this->_certificateString;
+ }
+
+ public function getCertificatePath()
+ {
+ return $this->_certificatePath;
+ }
+
+ public function getHashAlgorithm()
+ {
+ return $this->_hashAlgorithm;
+ }
+
+ protected function _parseConfigArgs(array $config = null)
+ {
+ $configs = array();
+ if (isset($config['privateKeyBits'])) {
+ $configs['private_key_bits'] = $config['privateKeyBits'];
+ }
+ if (!empty($configs)) {
+ return $configs;
+ }
+ return null;
+ }
+
+}
diff --git a/library/Zend/Crypt/Rsa/Exception.php b/library/Zend/Crypt/Rsa/Exception.php
new file mode 100644
index 0000000..e5b2f36
--- /dev/null
+++ b/library/Zend/Crypt/Rsa/Exception.php
@@ -0,0 +1,36 @@
+_opensslKeyResource;
+ }
+
+ /**
+ * @return string
+ * @throws Zend_Crypt_Exception
+ */
+ public function toString()
+ {
+ if (!empty($this->_pemString)) {
+ return $this->_pemString;
+ } elseif (!empty($this->_certificateString)) {
+ return $this->_certificateString;
+ }
+ /**
+ * @see Zend_Crypt_Exception
+ */
+ require_once 'Zend/Crypt/Exception.php';
+ throw new Zend_Crypt_Exception('No public key string representation is available');
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ public function count()
+ {
+ return $this->_details['bits'];
+ }
+
+ public function getType()
+ {
+ return $this->_details['type'];
+ }
+}
\ No newline at end of file
diff --git a/library/Zend/Crypt/Rsa/Key/Private.php b/library/Zend/Crypt/Rsa/Key/Private.php
new file mode 100644
index 0000000..1fe0046
--- /dev/null
+++ b/library/Zend/Crypt/Rsa/Key/Private.php
@@ -0,0 +1,75 @@
+_pemString = $pemString;
+ $this->_parse($passPhrase);
+ }
+
+ /**
+ * @param string $passPhrase
+ * @throws Zend_Crypt_Exception
+ */
+ protected function _parse($passPhrase)
+ {
+ $result = openssl_get_privatekey($this->_pemString, $passPhrase);
+ if (!$result) {
+ /**
+ * @see Zend_Crypt_Exception
+ */
+ require_once 'Zend/Crypt/Exception.php';
+ throw new Zend_Crypt_Exception('Unable to load private key');
+ }
+ $this->_opensslKeyResource = $result;
+ $this->_details = openssl_pkey_get_details($this->_opensslKeyResource);
+ }
+
+ public function getPublicKey()
+ {
+ if ($this->_publicKey === null) {
+ /**
+ * @see Zend_Crypt_Rsa_Key_Public
+ */
+ require_once 'Zend/Crypt/Rsa/Key/Public.php';
+ $this->_publicKey = new Zend_Crypt_Rsa_Key_Public($this->_details['key']);
+ }
+ return $this->_publicKey;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Crypt/Rsa/Key/Public.php b/library/Zend/Crypt/Rsa/Key/Public.php
new file mode 100644
index 0000000..12ef2f0
--- /dev/null
+++ b/library/Zend/Crypt/Rsa/Key/Public.php
@@ -0,0 +1,74 @@
+_parse($string);
+ }
+
+ /**
+ * @param string $string
+ * @throws Zend_Crypt_Exception
+ */
+ protected function _parse($string)
+ {
+ if (preg_match("/^-----BEGIN CERTIFICATE-----/", $string)) {
+ $this->_certificateString = $string;
+ } else {
+ $this->_pemString = $string;
+ }
+ $result = openssl_get_publickey($string);
+ if (!$result) {
+ /**
+ * @see Zend_Crypt_Exception
+ */
+ require_once 'Zend/Crypt/Exception.php';
+ throw new Zend_Crypt_Exception('Unable to load public key');
+ }
+ //openssl_pkey_export($result, $public);
+ //$this->_pemString = $public;
+ $this->_opensslKeyResource = $result;
+ $this->_details = openssl_pkey_get_details($this->_opensslKeyResource);
+ }
+
+ public function getCertificate()
+ {
+ return $this->_certificateString;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Currency.php b/library/Zend/Currency.php
new file mode 100644
index 0000000..d3b5131
--- /dev/null
+++ b/library/Zend/Currency.php
@@ -0,0 +1,894 @@
+ Position for the currency sign
+ * 'script' => Script for the output
+ * 'format' => Locale for numeric output
+ * 'display' => Currency detail to show
+ * 'precision' => Precision for the currency
+ * 'name' => Name for this currency
+ * 'currency' => 3 lettered international abbreviation
+ * 'symbol' => Currency symbol
+ * 'locale' => Locale for this currency
+ * 'value' => Money value
+ * 'service' => Exchange service to use
+ *
+ * @var array
+ * @see Zend_Locale
+ */
+ protected $_options = array(
+ 'position' => self::STANDARD,
+ 'script' => null,
+ 'format' => null,
+ 'display' => self::NO_SYMBOL,
+ 'precision' => 2,
+ 'name' => null,
+ 'currency' => null,
+ 'symbol' => null,
+ 'locale' => null,
+ 'value' => 0,
+ 'service' => null,
+ 'tag' => 'Zend_Locale'
+ );
+
+ /**
+ * Creates a currency instance. Every supressed parameter is used from the actual or the given locale.
+ *
+ * @param string|array $options OPTIONAL Options array or currency short name
+ * when string is given
+ * @param string|Zend_Locale $locale OPTIONAL locale name
+ * @throws Zend_Currency_Exception When currency is invalid
+ */
+ public function __construct($options = null, $locale = null)
+ {
+ if (is_array($options)) {
+ $this->setLocale($locale);
+ $this->setFormat($options);
+ } else if (Zend_Locale::isLocale($options, false, false)) {
+ $this->setLocale($options);
+ $options = $locale;
+ } else {
+ $this->setLocale($locale);
+ }
+
+ // Get currency details
+ if (!isset($this->_options['currency']) || !is_array($options)) {
+ $this->_options['currency'] = self::getShortName($options, $this->_options['locale']);
+ }
+
+ if (!isset($this->_options['name']) || !is_array($options)) {
+ $this->_options['name'] = self::getName($options, $this->_options['locale']);
+ }
+
+ if (!isset($this->_options['symbol']) || !is_array($options)) {
+ $this->_options['symbol'] = self::getSymbol($options, $this->_options['locale']);
+ }
+
+ if (($this->_options['currency'] === null) and ($this->_options['name'] === null)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("Currency '$options' not found");
+ }
+
+ // Get the format
+ if (!empty($this->_options['symbol'])) {
+ $this->_options['display'] = self::USE_SYMBOL;
+ } else if (!empty($this->_options['currency'])) {
+ $this->_options['display'] = self::USE_SHORTNAME;
+ }
+ }
+
+ /**
+ * Returns a localized currency string
+ *
+ * @param integer|float $value OPTIONAL Currency value
+ * @param array $options OPTIONAL options to set temporary
+ * @throws Zend_Currency_Exception When the value is not a number
+ * @return string
+ */
+ public function toCurrency($value = null, array $options = array())
+ {
+ if ($value === null) {
+ if (is_array($options) && isset($options['value'])) {
+ $value = $options['value'];
+ } else {
+ $value = $this->_options['value'];
+ }
+ }
+
+ if (is_array($value)) {
+ $options += $value;
+ if (isset($options['value'])) {
+ $value = $options['value'];
+ }
+ }
+
+ // Validate the passed number
+ if (!(isset($value)) or (is_numeric($value) === false)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("Value '$value' has to be numeric");
+ }
+
+ if (isset($options['currency'])) {
+ if (!isset($options['locale'])) {
+ $options['locale'] = $this->_options['locale'];
+ }
+
+ $options['currency'] = self::getShortName($options['currency'], $options['locale']);
+ $options['name'] = self::getName($options['currency'], $options['locale']);
+ $options['symbol'] = self::getSymbol($options['currency'], $options['locale']);
+ }
+
+ $options = $this->_checkOptions($options) + $this->_options;
+
+ // Format the number
+ $format = $options['format'];
+ $locale = $options['locale'];
+ if (empty($format)) {
+ $format = Zend_Locale_Data::getContent($locale, 'currencynumber');
+ } else if (Zend_Locale::isLocale($format, true, false)) {
+ $locale = $format;
+ $format = Zend_Locale_Data::getContent($format, 'currencynumber');
+ }
+
+ $original = $value;
+ $value = Zend_Locale_Format::toNumber($value, array('locale' => $locale,
+ 'number_format' => $format,
+ 'precision' => $options['precision']));
+
+ if ($options['position'] !== self::STANDARD) {
+ $value = str_replace('¤', '', $value);
+ $space = '';
+ if (iconv_strpos($value, ' ') !== false) {
+ $value = str_replace(' ', '', $value);
+ $space = ' ';
+ }
+
+ if ($options['position'] == self::LEFT) {
+ $value = '¤' . $space . $value;
+ } else {
+ $value = $value . $space . '¤';
+ }
+ }
+
+ // Localize the number digits
+ if (empty($options['script']) === false) {
+ $value = Zend_Locale_Format::convertNumerals($value, 'Latn', $options['script']);
+ }
+
+ // Get the sign to be placed next to the number
+ if (is_numeric($options['display']) === false) {
+ $sign = $options['display'];
+ } else {
+ switch($options['display']) {
+ case self::USE_SYMBOL:
+ $sign = $this->_extractPattern($options['symbol'], $original);
+ break;
+
+ case self::USE_SHORTNAME:
+ $sign = $options['currency'];
+ break;
+
+ case self::USE_NAME:
+ $sign = $options['name'];
+ break;
+
+ default:
+ $sign = '';
+ $value = str_replace(' ', '', $value);
+ break;
+ }
+ }
+
+ $value = str_replace('¤', $sign, $value);
+ return $value;
+ }
+
+ /**
+ * Internal method to extract the currency pattern
+ * when a choice is given based on the given value
+ *
+ * @param string $pattern
+ * @param float|integer $value
+ * @return string
+ */
+ private function _extractPattern($pattern, $value)
+ {
+ if (strpos($pattern, '|') === false) {
+ return $pattern;
+ }
+
+ $patterns = explode('|', $pattern);
+ $token = $pattern;
+ $value = trim(str_replace('¤', '', $value));
+ krsort($patterns);
+ foreach($patterns as $content) {
+ if (strpos($content, '<') !== false) {
+ $check = iconv_substr($content, 0, iconv_strpos($content, '<'));
+ $token = iconv_substr($content, iconv_strpos($content, '<') + 1);
+ if ($check < $value) {
+ return $token;
+ }
+ } else {
+ $check = iconv_substr($content, 0, iconv_strpos($content, '≤'));
+ $token = iconv_substr($content, iconv_strpos($content, '≤') + 1);
+ if ($check <= $value) {
+ return $token;
+ }
+ }
+
+ }
+
+ return $token;
+ }
+
+ /**
+ * Sets the formating options of the localized currency string
+ * If no parameter is passed, the standard setting of the
+ * actual set locale will be used
+ *
+ * @param array $options (Optional) Options to set
+ * @return Zend_Currency
+ */
+ public function setFormat(array $options = array())
+ {
+ $this->_options = $this->_checkOptions($options) + $this->_options;
+ return $this;
+ }
+
+ /**
+ * Internal function for checking static given locale parameter
+ *
+ * @param string $currency (Optional) Currency name
+ * @param string|Zend_Locale $locale (Optional) Locale to display informations
+ * @throws Zend_Currency_Exception When locale contains no region
+ * @return string The extracted locale representation as string
+ */
+ private function _checkParams($currency = null, $locale = null)
+ {
+ // Manage the params
+ if ((empty($locale)) and (!empty($currency)) and
+ (Zend_Locale::isLocale($currency, true, false))) {
+ $locale = $currency;
+ $currency = null;
+ }
+
+ // Validate the locale and get the country short name
+ $country = null;
+ if ((Zend_Locale::isLocale($locale, true, false)) and (strlen($locale) > 4)) {
+ $country = substr($locale, (strpos($locale, '_') + 1));
+ } else {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("No region found within the locale '" . (string) $locale . "'");
+ }
+
+ // Get the available currencies for this country
+ $data = Zend_Locale_Data::getContent($locale, 'currencytoregion', $country);
+ if ((empty($currency) === false) and (empty($data) === false)) {
+ $abbreviation = $currency;
+ } else {
+ $abbreviation = $data;
+ }
+
+ return array('locale' => $locale, 'currency' => $currency, 'name' => $abbreviation, 'country' => $country);
+ }
+
+ /**
+ * Returns the actual or details of other currency symbols,
+ * when no symbol is available it returns the currency shortname (f.e. FIM for Finnian Mark)
+ *
+ * @param string $currency (Optional) Currency name
+ * @param string|Zend_Locale $locale (Optional) Locale to display informations
+ * @return string
+ */
+ public function getSymbol($currency = null, $locale = null)
+ {
+ if (($currency === null) and ($locale === null)) {
+ return $this->_options['symbol'];
+ }
+
+ $params = self::_checkParams($currency, $locale);
+
+ // Get the symbol
+ $symbol = Zend_Locale_Data::getContent($params['locale'], 'currencysymbol', $params['currency']);
+ if (empty($symbol) === true) {
+ $symbol = Zend_Locale_Data::getContent($params['locale'], 'currencysymbol', $params['name']);
+ }
+
+ if (empty($symbol) === true) {
+ return null;
+ }
+
+ return $symbol;
+ }
+
+ /**
+ * Returns the actual or details of other currency shortnames
+ *
+ * @param string $currency OPTIONAL Currency's name
+ * @param string|Zend_Locale $locale OPTIONAL The locale
+ * @return string
+ */
+ public function getShortName($currency = null, $locale = null)
+ {
+ if (($currency === null) and ($locale === null)) {
+ return $this->_options['currency'];
+ }
+
+ $params = self::_checkParams($currency, $locale);
+
+ // Get the shortname
+ if (empty($params['currency']) === true) {
+ return $params['name'];
+ }
+
+ $list = Zend_Locale_Data::getContent($params['locale'], 'currencytoname', $params['currency']);
+ if (empty($list) === true) {
+ $list = Zend_Locale_Data::getContent($params['locale'], 'nametocurrency', $params['currency']);
+ if (empty($list) === false) {
+ $list = $params['currency'];
+ }
+ }
+
+ if (empty($list) === true) {
+ return null;
+ }
+
+ return $list;
+ }
+
+ /**
+ * Returns the actual or details of other currency names
+ *
+ * @param string $currency (Optional) Currency's short name
+ * @param string|Zend_Locale $locale (Optional) The locale
+ * @return string
+ */
+ public function getName($currency = null, $locale = null)
+ {
+ if (($currency === null) and ($locale === null)) {
+ return $this->_options['name'];
+ }
+
+ $params = self::_checkParams($currency, $locale);
+
+ // Get the name
+ $name = Zend_Locale_Data::getContent($params['locale'], 'nametocurrency', $params['currency']);
+ if (empty($name) === true) {
+ $name = Zend_Locale_Data::getContent($params['locale'], 'nametocurrency', $params['name']);
+ }
+
+ if (empty($name) === true) {
+ return null;
+ }
+
+ return $name;
+ }
+
+ /**
+ * Returns a list of regions where this currency is or was known
+ *
+ * @param string $currency OPTIONAL Currency's short name
+ * @throws Zend_Currency_Exception When no currency was defined
+ * @return array List of regions
+ */
+ public function getRegionList($currency = null)
+ {
+ if ($currency === null) {
+ $currency = $this->_options['currency'];
+ }
+
+ if (empty($currency) === true) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception('No currency defined');
+ }
+
+ $data = Zend_Locale_Data::getContent($this->_options['locale'], 'regiontocurrency', $currency);
+
+ $result = explode(' ', $data);
+ return $result;
+ }
+
+ /**
+ * Returns a list of currencies which are used in this region
+ * a region name should be 2 charachters only (f.e. EG, DE, US)
+ * If no region is given, the actual region is used
+ *
+ * @param string $region OPTIONAL Region to return the currencies for
+ * @return array List of currencies
+ */
+ public function getCurrencyList($region = null)
+ {
+ if (empty($region) === true) {
+ if (strlen($this->_options['locale']) > 4) {
+ $region = substr($this->_options['locale'], (strpos($this->_options['locale'], '_') + 1));
+ }
+ }
+
+ $data = Zend_Locale_Data::getContent($this->_options['locale'], 'currencytoregion', $region);
+
+ $result = explode(' ', $data);
+ return $result;
+ }
+
+ /**
+ * Returns the actual currency name
+ *
+ * @return string
+ */
+ public function toString()
+ {
+ return $this->toCurrency();
+ }
+
+ /**
+ * Returns the currency name
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * Returns the set cache
+ *
+ * @return Zend_Cache_Core The set cache
+ */
+ public static function getCache()
+ {
+ return Zend_Locale_Data::getCache();
+ }
+
+ /**
+ * Sets a cache for Zend_Currency
+ *
+ * @param Zend_Cache_Core $cache Cache to set
+ * @return void
+ */
+ public static function setCache(Zend_Cache_Core $cache)
+ {
+ Zend_Locale_Data::setCache($cache);
+ }
+
+ /**
+ * Returns true when a cache is set
+ *
+ * @return boolean
+ */
+ public static function hasCache()
+ {
+ return Zend_Locale_Data::hasCache();
+ }
+
+ /**
+ * Removes any set cache
+ *
+ * @return void
+ */
+ public static function removeCache()
+ {
+ Zend_Locale_Data::removeCache();
+ }
+
+ /**
+ * Clears all set cache data
+ *
+ * @param string $tag Tag to clear when the default tag name is not used
+ * @return void
+ */
+ public static function clearCache($tag = null)
+ {
+ Zend_Locale_Data::clearCache($tag);
+ }
+
+ /**
+ * Sets a new locale for data retreivement
+ * Example: 'de_XX' will be set to 'de' because 'de_XX' does not exist
+ * 'xx_YY' will be set to 'root' because 'xx' does not exist
+ *
+ * @param string|Zend_Locale $locale (Optional) Locale for parsing input
+ * @throws Zend_Currency_Exception When the given locale does not exist
+ * @return Zend_Currency Provides fluent interface
+ */
+ public function setLocale($locale = null)
+ {
+ require_once 'Zend/Locale.php';
+ try {
+ $locale = Zend_Locale::findLocale($locale);
+ if (strlen($locale) > 4) {
+ $this->_options['locale'] = $locale;
+ } else {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("No region found within the locale '" . (string) $locale . "'");
+ }
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception($e->getMessage());
+ }
+
+ // Get currency details
+ $this->_options['currency'] = $this->getShortName(null, $this->_options['locale']);
+ $this->_options['name'] = $this->getName(null, $this->_options['locale']);
+ $this->_options['symbol'] = $this->getSymbol(null, $this->_options['locale']);
+
+ return $this;
+ }
+
+ /**
+ * Returns the actual set locale
+ *
+ * @return string
+ */
+ public function getLocale()
+ {
+ return $this->_options['locale'];
+ }
+
+ /**
+ * Returns the value
+ *
+ * @return float
+ */
+ public function getValue()
+ {
+ return $this->_options['value'];
+ }
+
+ /**
+ * Adds a currency
+ *
+ * @param float|integer|Zend_Currency $value Add this value to currency
+ * @param string|Zend_Currency $currency The currency to add
+ * @return Zend_Currency
+ */
+ public function setValue($value, $currency = null)
+ {
+ $this->_options['value'] = $this->_exchangeCurrency($value, $currency);
+ return $this;
+ }
+
+ /**
+ * Adds a currency
+ *
+ * @param float|integer|Zend_Currency $value Add this value to currency
+ * @param string|Zend_Currency $currency The currency to add
+ * @return Zend_Currency
+ */
+ public function add($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ $this->_options['value'] += (float) $value;
+ return $this;
+ }
+
+ /**
+ * Substracts a currency
+ *
+ * @param float|integer|Zend_Currency $value Substracts this value from currency
+ * @param string|Zend_Currency $currency The currency to substract
+ * @return Zend_Currency
+ */
+ public function sub($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ $this->_options['value'] -= (float) $value;
+ return $this;
+ }
+
+ /**
+ * Divides a currency
+ *
+ * @param float|integer|Zend_Currency $value Divides this value from currency
+ * @param string|Zend_Currency $currency The currency to divide
+ * @return Zend_Currency
+ */
+ public function div($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ $this->_options['value'] /= (float) $value;
+ return $this;
+ }
+
+ /**
+ * Multiplies a currency
+ *
+ * @param float|integer|Zend_Currency $value Multiplies this value from currency
+ * @param string|Zend_Currency $currency The currency to multiply
+ * @return Zend_Currency
+ */
+ public function mul($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ $this->_options['value'] *= (float) $value;
+ return $this;
+ }
+
+ /**
+ * Calculates the modulo from a currency
+ *
+ * @param float|integer|Zend_Currency $value Calculate modulo from this value
+ * @param string|Zend_Currency $currency The currency to calculate the modulo
+ * @return Zend_Currency
+ */
+ public function mod($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ $this->_options['value'] %= (float) $value;
+ return $this;
+ }
+
+ /**
+ * Compares two currencies
+ *
+ * @param float|integer|Zend_Currency $value Compares the currency with this value
+ * @param string|Zend_Currency $currency The currency to compare this value from
+ * @return Zend_Currency
+ */
+ public function compare($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ $value = $this->_options['value'] - $value;
+ if ($value < 0) {
+ return -1;
+ } else if ($value > 0) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Returns true when the two currencies are equal
+ *
+ * @param float|integer|Zend_Currency $value Compares the currency with this value
+ * @param string|Zend_Currency $currency The currency to compare this value from
+ * @return boolean
+ */
+ public function equals($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ if ($this->_options['value'] == $value) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true when the currency is more than the given value
+ *
+ * @param float|integer|Zend_Currency $value Compares the currency with this value
+ * @param string|Zend_Currency $currency The currency to compare this value from
+ * @return boolean
+ */
+ public function isMore($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ if ($this->_options['value'] > $value) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true when the currency is less than the given value
+ *
+ * @param float|integer|Zend_Currency $value Compares the currency with this value
+ * @param string|Zend_Currency $currency The currency to compare this value from
+ * @return boolean
+ */
+ public function isLess($value, $currency = null)
+ {
+ $value = $this->_exchangeCurrency($value, $currency);
+ if ($this->_options['value'] < $value) {
+ return true;
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Internal method which calculates the exchanges currency
+ *
+ * @param float|integer|Zend_Currency $value Compares the currency with this value
+ * @param string|Zend_Currency $currency The currency to compare this value from
+ * @return unknown
+ */
+ protected function _exchangeCurrency($value, $currency)
+ {
+ if ($value instanceof Zend_Currency) {
+ $currency = $value->getShortName();
+ $value = $value->getValue();
+ } else {
+ $currency = $this->getShortName($currency, $this->getLocale());
+ }
+
+ $rate = 1;
+ if ($currency !== $this->getShortName()) {
+ $service = $this->getService();
+ if (!($service instanceof Zend_Currency_CurrencyInterface)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception('No exchange service applied');
+ }
+
+ $rate = $service->getRate($currency, $this->getShortName());
+ }
+
+ $value *= $rate;
+ return $value;
+ }
+
+ /**
+ * Returns the set service class
+ *
+ * @return Zend_Service
+ */
+ public function getService()
+ {
+ return $this->_options['service'];
+ }
+
+ /**
+ * Sets a new exchange service
+ *
+ * @param string|Zend_Currency_CurrencyInterface $service Service class
+ * @return Zend_Currency
+ */
+ public function setService($service)
+ {
+ if (is_string($service)) {
+ require_once 'Zend/Loader.php';
+ if (!class_exists($service)) {
+ $file = str_replace('_', DIRECTORY_SEPARATOR, $service) . '.php';
+ if (Zend_Loader::isReadable($file)) {
+ Zend_Loader::loadClass($service);
+ }
+ }
+
+ $service = new $service;
+ }
+
+ if (!($service instanceof Zend_Currency_CurrencyInterface)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception('A currency service must implement Zend_Currency_CurrencyInterface');
+ }
+
+ $this->_options['service'] = $service;
+ return $this;
+ }
+
+ /**
+ * Internal method for checking the options array
+ *
+ * @param array $options Options to check
+ * @throws Zend_Currency_Exception On unknown position
+ * @throws Zend_Currency_Exception On unknown locale
+ * @throws Zend_Currency_Exception On unknown display
+ * @throws Zend_Currency_Exception On precision not between -1 and 30
+ * @throws Zend_Currency_Exception On problem with script conversion
+ * @throws Zend_Currency_Exception On unknown options
+ * @return array
+ */
+ protected function _checkOptions(array $options = array())
+ {
+ if (count($options) === 0) {
+ return $this->_options;
+ }
+
+ foreach ($options as $name => $value) {
+ $name = strtolower($name);
+ if ($name !== 'format') {
+ if (gettype($value) === 'string') {
+ $value = strtolower($value);
+ }
+ }
+
+ switch($name) {
+ case 'position':
+ if (($value !== self::STANDARD) and ($value !== self::RIGHT) and ($value !== self::LEFT)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("Unknown position '" . $value . "'");
+ }
+
+ break;
+
+ case 'format':
+ if ((empty($value) === false) and (Zend_Locale::isLocale($value, null, false) === false)) {
+ if (!is_string($value) || (strpos($value, '0') === false)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("'" .
+ ((gettype($value) === 'object') ? get_class($value) : $value)
+ . "' is no format token");
+ }
+ }
+ break;
+
+ case 'display':
+ if (is_numeric($value) and ($value !== self::NO_SYMBOL) and ($value !== self::USE_SYMBOL) and
+ ($value !== self::USE_SHORTNAME) and ($value !== self::USE_NAME)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("Unknown display '$value'");
+ }
+ break;
+
+ case 'precision':
+ if ($value === null) {
+ $value = -1;
+ }
+
+ if (($value < -1) or ($value > 30)) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception("'$value' precision has to be between -1 and 30.");
+ }
+ break;
+
+ case 'script':
+ try {
+ Zend_Locale_Format::convertNumerals(0, $options['script']);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Currency/Exception.php';
+ throw new Zend_Currency_Exception($e->getMessage());
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return $options;
+ }
+}
diff --git a/library/Zend/Currency/CurrencyInterface.php b/library/Zend/Currency/CurrencyInterface.php
new file mode 100644
index 0000000..771013b
--- /dev/null
+++ b/library/Zend/Currency/CurrencyInterface.php
@@ -0,0 +1,39 @@
+ 'iso', // format for date strings 'iso' or 'php'
+ 'fix_dst' => true, // fix dst on summer/winter time change
+ 'extend_month' => false, // false - addMonth like SQL, true like excel
+ 'cache' => null, // cache to set
+ 'timesync' => null // timesync server to set
+ );
+
+ // Class wide Date Constants
+ const DAY = 'dd';
+ const DAY_SHORT = 'd';
+ const DAY_SUFFIX = 'SS';
+ const DAY_OF_YEAR = 'D';
+ const WEEKDAY = 'EEEE';
+ const WEEKDAY_SHORT = 'EEE';
+ const WEEKDAY_NARROW = 'E';
+ const WEEKDAY_NAME = 'EE';
+ const WEEKDAY_8601 = 'eee';
+ const WEEKDAY_DIGIT = 'e';
+ const WEEK = 'ww';
+ const MONTH = 'MM';
+ const MONTH_SHORT = 'M';
+ const MONTH_DAYS = 'ddd';
+ const MONTH_NAME = 'MMMM';
+ const MONTH_NAME_SHORT = 'MMM';
+ const MONTH_NAME_NARROW = 'MMMMM';
+ const YEAR = 'y';
+ const YEAR_SHORT = 'yy';
+ const YEAR_8601 = 'Y';
+ const YEAR_SHORT_8601 = 'YY';
+ const LEAPYEAR = 'l';
+ const MERIDIEM = 'a';
+ const SWATCH = 'B';
+ const HOUR = 'HH';
+ const HOUR_SHORT = 'H';
+ const HOUR_AM = 'hh';
+ const HOUR_SHORT_AM = 'h';
+ const MINUTE = 'mm';
+ const MINUTE_SHORT = 'm';
+ const SECOND = 'ss';
+ const SECOND_SHORT = 's';
+ const MILLISECOND = 'S';
+ const TIMEZONE_NAME = 'zzzz';
+ const DAYLIGHT = 'I';
+ const GMT_DIFF = 'Z';
+ const GMT_DIFF_SEP = 'ZZZZ';
+ const TIMEZONE = 'z';
+ const TIMEZONE_SECS = 'X';
+ const ISO_8601 = 'c';
+ const RFC_2822 = 'r';
+ const TIMESTAMP = 'U';
+ const ERA = 'G';
+ const ERA_NAME = 'GGGG';
+ const ERA_NARROW = 'GGGGG';
+ const DATES = 'F';
+ const DATE_FULL = 'FFFFF';
+ const DATE_LONG = 'FFFF';
+ const DATE_MEDIUM = 'FFF';
+ const DATE_SHORT = 'FF';
+ const TIMES = 'WW';
+ const TIME_FULL = 'TTTTT';
+ const TIME_LONG = 'TTTT';
+ const TIME_MEDIUM = 'TTT';
+ const TIME_SHORT = 'TT';
+ const DATETIME = 'K';
+ const DATETIME_FULL = 'KKKKK';
+ const DATETIME_LONG = 'KKKK';
+ const DATETIME_MEDIUM = 'KKK';
+ const DATETIME_SHORT = 'KK';
+ const ATOM = 'OOO';
+ const COOKIE = 'CCC';
+ const RFC_822 = 'R';
+ const RFC_850 = 'RR';
+ const RFC_1036 = 'RRR';
+ const RFC_1123 = 'RRRR';
+ const RFC_3339 = 'RRRRR';
+ const RSS = 'SSS';
+ const W3C = 'WWW';
+
+ /**
+ * Generates the standard date object, could be a unix timestamp, localized date,
+ * string, integer, array and so on. Also parts of dates or time are supported
+ * Always set the default timezone: http://php.net/date_default_timezone_set
+ * For example, in your bootstrap: date_default_timezone_set('America/Los_Angeles');
+ * For detailed instructions please look in the docu.
+ *
+ * @param string|integer|Zend_Date|array $date OPTIONAL Date value or value of date part to set
+ * ,depending on $part. If null the actual time is set
+ * @param string $part OPTIONAL Defines the input format of $date
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ * @throws Zend_Date_Exception
+ */
+ public function __construct($date = null, $part = null, $locale = null)
+ {
+ if (is_object($date) and !($date instanceof Zend_TimeSync_Protocol) and
+ !($date instanceof Zend_Date)) {
+ if ($locale instanceof Zend_Locale) {
+ $locale = $date;
+ $date = null;
+ $part = null;
+ } else {
+ $date = (string) $date;
+ }
+ }
+
+ if (($date !== null) and !is_array($date) and !($date instanceof Zend_TimeSync_Protocol) and
+ !($date instanceof Zend_Date) and !defined($date) and Zend_Locale::isLocale($date, true, false)) {
+ $locale = $date;
+ $date = null;
+ $part = null;
+ } else if (($part !== null) and !defined($part) and Zend_Locale::isLocale($part, true, false)) {
+ $locale = $part;
+ $part = null;
+ }
+
+ $this->setLocale($locale);
+ if (is_string($date) && ($part === null) && (strlen($date) <= 5)) {
+ $part = $date;
+ $date = null;
+ }
+
+ if ($date === null) {
+ if ($part === null) {
+ $date = time();
+ } else if ($part !== self::TIMESTAMP) {
+ $date = self::now($locale);
+ $date = $date->get($part);
+ }
+ }
+
+ if ($date instanceof Zend_TimeSync_Protocol) {
+ $date = $date->getInfo();
+ $date = $this->_getTime($date['offset']);
+ $part = null;
+ } else if (parent::$_defaultOffset != 0) {
+ $date = $this->_getTime(parent::$_defaultOffset);
+ }
+
+ // set the timezone and offset for $this
+ $zone = @date_default_timezone_get();
+ $this->setTimezone($zone);
+
+ // try to get timezone from date-string
+ if (!is_int($date)) {
+ $zone = $this->getTimezoneFromString($date);
+ $this->setTimezone($zone);
+ }
+
+ // set datepart
+ if (($part !== null && $part !== self::TIMESTAMP) or (!is_numeric($date))) {
+ // switch off dst handling for value setting
+ $this->setUnixTimestamp($this->getGmtOffset());
+ $this->set($date, $part, $this->_locale);
+
+ // DST fix
+ if (is_array($date) === true) {
+ if (!isset($date['hour'])) {
+ $date['hour'] = 0;
+ }
+
+ $hour = $this->toString('H', 'iso', true);
+ $hour = $date['hour'] - $hour;
+ switch ($hour) {
+ case 1 :
+ case -23 :
+ $this->addTimestamp(3600);
+ break;
+ case -1 :
+ case 23 :
+ $this->subTimestamp(3600);
+ break;
+ case 2 :
+ case -22 :
+ $this->addTimestamp(7200);
+ break;
+ case -2 :
+ case 22 :
+ $this->subTimestamp(7200);
+ break;
+ }
+ }
+ } else {
+ $this->setUnixTimestamp($date);
+ }
+ }
+
+ /**
+ * Sets class wide options, if no option was given, the actual set options will be returned
+ *
+ * @param array $options Options to set
+ * @throws Zend_Date_Exception
+ * @return Options array if no option was given
+ */
+ public static function setOptions(array $options = array())
+ {
+ if (empty($options)) {
+ return self::$_options;
+ }
+
+ foreach ($options as $name => $value) {
+ $name = strtolower($name);
+
+ if (array_key_exists($name, self::$_options)) {
+ switch($name) {
+ case 'format_type' :
+ if ((strtolower($value) != 'php') && (strtolower($value) != 'iso')) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("Unknown format type ($value) for dates, only 'iso' and 'php' supported", 0, null, $value);
+ }
+ break;
+ case 'fix_dst' :
+ if (!is_bool($value)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("'fix_dst' has to be boolean", 0, null, $value);
+ }
+ break;
+ case 'extend_month' :
+ if (!is_bool($value)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("'extend_month' has to be boolean", 0, null, $value);
+ }
+ break;
+ case 'cache' :
+ if ($value === null) {
+ parent::$_cache = null;
+ } else {
+ if (!$value instanceof Zend_Cache_Core) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("Instance of Zend_Cache expected");
+ }
+
+ parent::$_cache = $value;
+ parent::$_cacheTags = Zend_Date_DateObject::_getTagSupportForCache();
+ Zend_Locale_Data::setCache($value);
+ }
+ break;
+ case 'timesync' :
+ if ($value === null) {
+ parent::$_defaultOffset = 0;
+ } else {
+ if (!$value instanceof Zend_TimeSync_Protocol) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("Instance of Zend_TimeSync expected");
+ }
+
+ $date = $value->getInfo();
+ parent::$_defaultOffset = $date['offset'];
+ }
+ break;
+ }
+ self::$_options[$name] = $value;
+ }
+ else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("Unknown option: $name = $value");
+ }
+ }
+ }
+
+ /**
+ * Returns this object's internal UNIX timestamp (equivalent to Zend_Date::TIMESTAMP).
+ * If the timestamp is too large for integers, then the return value will be a string.
+ * This function does not return the timestamp as an object.
+ * Use clone() or copyPart() instead.
+ *
+ * @return integer|string UNIX timestamp
+ */
+ public function getTimestamp()
+ {
+ return $this->getUnixTimestamp();
+ }
+
+ /**
+ * Returns the calculated timestamp
+ * HINT: timestamps are always GMT
+ *
+ * @param string $calc Type of calculation to make
+ * @param string|integer|array|Zend_Date $stamp Timestamp to calculate, when null the actual timestamp is calculated
+ * @return Zend_Date|integer
+ * @throws Zend_Date_Exception
+ */
+ private function _timestamp($calc, $stamp)
+ {
+ if ($stamp instanceof Zend_Date) {
+ // extract timestamp from object
+ $stamp = $stamp->getTimestamp();
+ }
+
+ if (is_array($stamp)) {
+ if (isset($stamp['timestamp']) === true) {
+ $stamp = $stamp['timestamp'];
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('no timestamp given in array');
+ }
+ }
+
+ if ($calc === 'set') {
+ $return = $this->setUnixTimestamp($stamp);
+ } else {
+ $return = $this->_calcdetail($calc, $stamp, self::TIMESTAMP, null);
+ }
+ if ($calc != 'cmp') {
+ return $this;
+ }
+ return $return;
+ }
+
+ /**
+ * Sets a new timestamp
+ *
+ * @param integer|string|array|Zend_Date $timestamp Timestamp to set
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setTimestamp($timestamp)
+ {
+ return $this->_timestamp('set', $timestamp);
+ }
+
+ /**
+ * Adds a timestamp
+ *
+ * @param integer|string|array|Zend_Date $timestamp Timestamp to add
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addTimestamp($timestamp)
+ {
+ return $this->_timestamp('add', $timestamp);
+ }
+
+ /**
+ * Subtracts a timestamp
+ *
+ * @param integer|string|array|Zend_Date $timestamp Timestamp to sub
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subTimestamp($timestamp)
+ {
+ return $this->_timestamp('sub', $timestamp);
+ }
+
+ /**
+ * Compares two timestamps, returning the difference as integer
+ *
+ * @param integer|string|array|Zend_Date $timestamp Timestamp to compare
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareTimestamp($timestamp)
+ {
+ return $this->_timestamp('cmp', $timestamp);
+ }
+
+ /**
+ * Returns a string representation of the object
+ * Supported format tokens are:
+ * G - era, y - year, Y - ISO year, M - month, w - week of year, D - day of year, d - day of month
+ * E - day of week, e - number of weekday (1-7), h - hour 1-12, H - hour 0-23, m - minute, s - second
+ * A - milliseconds of day, z - timezone, Z - timezone offset, S - fractional second, a - period of day
+ *
+ * Additionally format tokens but non ISO conform are:
+ * SS - day suffix, eee - php number of weekday(0-6), ddd - number of days per month
+ * l - Leap year, B - swatch internet time, I - daylight saving time, X - timezone offset in seconds
+ * r - RFC2822 format, U - unix timestamp
+ *
+ * Not supported ISO tokens are
+ * u - extended year, Q - quarter, q - quarter, L - stand alone month, W - week of month
+ * F - day of week of month, g - modified julian, c - stand alone weekday, k - hour 0-11, K - hour 1-24
+ * v - wall zone
+ *
+ * @param string $format OPTIONAL Rule for formatting output. If null the default date format is used
+ * @param string $type OPTIONAL Type for the format string which overrides the standard setting
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return string
+ */
+ public function toString($format = null, $type = null, $locale = null)
+ {
+ if (is_object($format)) {
+ if ($format instanceof Zend_Locale) {
+ $locale = $format;
+ $format = null;
+ } else {
+ $format = (string) $format;
+ }
+ }
+
+ if (is_object($type)) {
+ if ($type instanceof Zend_Locale) {
+ $locale = $type;
+ $type = null;
+ } else {
+ $type = (string) $type;
+ }
+ }
+
+ if (($format !== null) && !defined($format)
+ && ($format != 'ee') && ($format != 'ss') && ($format != 'GG') && ($format != 'MM') && ($format != 'EE') && ($format != 'TT')
+ && Zend_Locale::isLocale($format, null, false)) {
+ $locale = $format;
+ $format = null;
+ }
+
+ if (($type !== null) and ($type != 'php') and ($type != 'iso') and
+ Zend_Locale::isLocale($type, null, false)) {
+ $locale = $type;
+ $type = null;
+ }
+
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ if ($format === null) {
+ $format = Zend_Locale_Format::getDateFormat($locale) . ' ' . Zend_Locale_Format::getTimeFormat($locale);
+ } else if (((self::$_options['format_type'] == 'php') && ($type === null)) or ($type == 'php')) {
+ $format = Zend_Locale_Format::convertPhpToIsoFormat($format);
+ }
+
+ return $this->date($this->_toToken($format, $locale), $this->getUnixTimestamp(), false);
+ }
+
+ /**
+ * Returns a string representation of the date which is equal with the timestamp
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString(null, $this->_locale);
+ }
+
+ /**
+ * Returns a integer representation of the object
+ * But returns false when the given part is no value f.e. Month-Name
+ *
+ * @param string|integer|Zend_Date $part OPTIONAL Defines the date or datepart to return as integer
+ * @return integer|false
+ */
+ public function toValue($part = null)
+ {
+ $result = $this->get($part);
+ if (is_numeric($result)) {
+ return intval("$result");
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns an array representation of the object
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return array('day' => $this->toString(self::DAY_SHORT, 'iso'),
+ 'month' => $this->toString(self::MONTH_SHORT, 'iso'),
+ 'year' => $this->toString(self::YEAR, 'iso'),
+ 'hour' => $this->toString(self::HOUR_SHORT, 'iso'),
+ 'minute' => $this->toString(self::MINUTE_SHORT, 'iso'),
+ 'second' => $this->toString(self::SECOND_SHORT, 'iso'),
+ 'timezone' => $this->toString(self::TIMEZONE, 'iso'),
+ 'timestamp' => $this->toString(self::TIMESTAMP, 'iso'),
+ 'weekday' => $this->toString(self::WEEKDAY_8601, 'iso'),
+ 'dayofyear' => $this->toString(self::DAY_OF_YEAR, 'iso'),
+ 'week' => $this->toString(self::WEEK, 'iso'),
+ 'gmtsecs' => $this->toString(self::TIMEZONE_SECS, 'iso'));
+ }
+
+ /**
+ * Returns a representation of a date or datepart
+ * This could be for example a localized monthname, the time without date,
+ * the era or only the fractional seconds. There are about 50 different supported date parts.
+ * For a complete list of supported datepart values look into the docu
+ *
+ * @param string $part OPTIONAL Part of the date to return, if null the timestamp is returned
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return string date or datepart
+ */
+ public function get($part = null, $locale = null)
+ {
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ if (($part !== null) && !defined($part)
+ && ($part != 'ee') && ($part != 'ss') && ($part != 'GG') && ($part != 'MM') && ($part != 'EE') && ($part != 'TT')
+ && Zend_Locale::isLocale($part, null, false)) {
+ $locale = $part;
+ $part = null;
+ }
+
+ if ($part === null) {
+ $part = self::TIMESTAMP;
+ } else if (self::$_options['format_type'] == 'php') {
+ $part = Zend_Locale_Format::convertPhpToIsoFormat($part);
+ }
+
+ return $this->date($this->_toToken($part, $locale), $this->getUnixTimestamp(), false);
+ }
+
+ /**
+ * Internal method to apply tokens
+ *
+ * @param string $part
+ * @param string $locale
+ * @return string
+ */
+ private function _toToken($part, $locale) {
+ // get format tokens
+ $comment = false;
+ $format = '';
+ $orig = '';
+ for ($i = 0; isset($part[$i]); ++$i) {
+ if ($part[$i] == "'") {
+ $comment = $comment ? false : true;
+ if (isset($part[$i+1]) && ($part[$i+1] == "'")) {
+ $comment = $comment ? false : true;
+ $format .= "\\'";
+ ++$i;
+ }
+
+ $orig = '';
+ continue;
+ }
+
+ if ($comment) {
+ $format .= '\\' . $part[$i];
+ $orig = '';
+ } else {
+ $orig .= $part[$i];
+ if (!isset($part[$i+1]) || (isset($orig[0]) && ($orig[0] != $part[$i+1]))) {
+ $format .= $this->_parseIsoToDate($orig, $locale);
+ $orig = '';
+ }
+ }
+ }
+
+ return $format;
+ }
+
+ /**
+ * Internal parsing method
+ *
+ * @param string $token
+ * @param string $locale
+ * @return string
+ */
+ private function _parseIsoToDate($token, $locale) {
+ switch($token) {
+ case self::DAY :
+ return 'd';
+ break;
+
+ case self::WEEKDAY_SHORT :
+ $weekday = strtolower($this->date('D', $this->getUnixTimestamp(), false));
+ $day = Zend_Locale_Data::getContent($locale, 'day', array('gregorian', 'format', 'wide', $weekday));
+ return $this->_toComment(iconv_substr($day, 0, 3, 'UTF-8'));
+ break;
+
+ case self::DAY_SHORT :
+ return 'j';
+ break;
+
+ case self::WEEKDAY :
+ $weekday = strtolower($this->date('D', $this->getUnixTimestamp(), false));
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'day', array('gregorian', 'format', 'wide', $weekday)));
+ break;
+
+ case self::WEEKDAY_8601 :
+ return 'N';
+ break;
+
+ case 'ee' :
+ return $this->_toComment(str_pad($this->date('N', $this->getUnixTimestamp(), false), 2, '0', STR_PAD_LEFT));
+ break;
+
+ case self::DAY_SUFFIX :
+ return 'S';
+ break;
+
+ case self::WEEKDAY_DIGIT :
+ return 'w';
+ break;
+
+ case self::DAY_OF_YEAR :
+ return 'z';
+ break;
+
+ case 'DDD' :
+ return $this->_toComment(str_pad($this->date('z', $this->getUnixTimestamp(), false), 3, '0', STR_PAD_LEFT));
+ break;
+
+ case 'DD' :
+ return $this->_toComment(str_pad($this->date('z', $this->getUnixTimestamp(), false), 2, '0', STR_PAD_LEFT));
+ break;
+
+ case self::WEEKDAY_NARROW :
+ case 'EEEEE' :
+ $weekday = strtolower($this->date('D', $this->getUnixTimestamp(), false));
+ $day = Zend_Locale_Data::getContent($locale, 'day', array('gregorian', 'format', 'abbreviated', $weekday));
+ return $this->_toComment(iconv_substr($day, 0, 1, 'UTF-8'));
+ break;
+
+ case self::WEEKDAY_NAME :
+ $weekday = strtolower($this->date('D', $this->getUnixTimestamp(), false));
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'day', array('gregorian', 'format', 'abbreviated', $weekday)));
+ break;
+
+ case 'w' :
+ $week = $this->date('W', $this->getUnixTimestamp(), false);
+ return $this->_toComment(($week[0] == '0') ? $week[1] : $week);
+ break;
+
+ case self::WEEK :
+ return 'W';
+ break;
+
+ case self::MONTH_NAME :
+ $month = $this->date('n', $this->getUnixTimestamp(), false);
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'month', array('gregorian', 'format', 'wide', $month)));
+ break;
+
+ case self::MONTH :
+ return 'm';
+ break;
+
+ case self::MONTH_NAME_SHORT :
+ $month = $this->date('n', $this->getUnixTimestamp(), false);
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'month', array('gregorian', 'format', 'abbreviated', $month)));
+ break;
+
+ case self::MONTH_SHORT :
+ return 'n';
+ break;
+
+ case self::MONTH_DAYS :
+ return 't';
+ break;
+
+ case self::MONTH_NAME_NARROW :
+ $month = $this->date('n', $this->getUnixTimestamp(), false);
+ $mon = Zend_Locale_Data::getContent($locale, 'month', array('gregorian', 'format', 'abbreviated', $month));
+ return $this->_toComment(iconv_substr($mon, 0, 1, 'UTF-8'));
+ break;
+
+ case self::LEAPYEAR :
+ return 'L';
+ break;
+
+ case self::YEAR_8601 :
+ return 'o';
+ break;
+
+ case self::YEAR :
+ return 'Y';
+ break;
+
+ case self::YEAR_SHORT :
+ return 'y';
+ break;
+
+ case self::YEAR_SHORT_8601 :
+ return $this->_toComment(substr($this->date('o', $this->getUnixTimestamp(), false), -2, 2));
+ break;
+
+ case self::MERIDIEM :
+ $am = $this->date('a', $this->getUnixTimestamp(), false);
+ if ($am == 'am') {
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'am'));
+ }
+
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'pm'));
+ break;
+
+ case self::SWATCH :
+ return 'B';
+ break;
+
+ case self::HOUR_SHORT_AM :
+ return 'g';
+ break;
+
+ case self::HOUR_SHORT :
+ return 'G';
+ break;
+
+ case self::HOUR_AM :
+ return 'h';
+ break;
+
+ case self::HOUR :
+ return 'H';
+ break;
+
+ case self::MINUTE :
+ return $this->_toComment(str_pad($this->date('i', $this->getUnixTimestamp(), false), 2, '0', STR_PAD_LEFT));
+ break;
+
+ case self::SECOND :
+ return $this->_toComment(str_pad($this->date('s', $this->getUnixTimestamp(), false), 2, '0', STR_PAD_LEFT));
+ break;
+
+ case self::MINUTE_SHORT :
+ return 'i';
+ break;
+
+ case self::SECOND_SHORT :
+ return 's';
+ break;
+
+ case self::MILLISECOND :
+ return $this->_toComment($this->getMilliSecond());
+ break;
+
+ case self::TIMEZONE_NAME :
+ case 'vvvv' :
+ return 'e';
+ break;
+
+ case self::DAYLIGHT :
+ return 'I';
+ break;
+
+ case self::GMT_DIFF :
+ case 'ZZ' :
+ case 'ZZZ' :
+ return 'O';
+ break;
+
+ case self::GMT_DIFF_SEP :
+ return 'P';
+ break;
+
+ case self::TIMEZONE :
+ case 'v' :
+ case 'zz' :
+ case 'zzz' :
+ return 'T';
+ break;
+
+ case self::TIMEZONE_SECS :
+ return 'Z';
+ break;
+
+ case self::ISO_8601 :
+ return 'c';
+ break;
+
+ case self::RFC_2822 :
+ return 'r';
+ break;
+
+ case self::TIMESTAMP :
+ return 'U';
+ break;
+
+ case self::ERA :
+ case 'GG' :
+ case 'GGG' :
+ $year = $this->date('Y', $this->getUnixTimestamp(), false);
+ if ($year < 0) {
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'era', array('gregorian', 'Abbr', '0')));
+ }
+
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'era', array('gregorian', 'Abbr', '1')));
+ break;
+
+ case self::ERA_NARROW :
+ $year = $this->date('Y', $this->getUnixTimestamp(), false);
+ if ($year < 0) {
+ return $this->_toComment(iconv_substr(Zend_Locale_Data::getContent($locale, 'era', array('gregorian', 'Abbr', '0')), 0, 1, 'UTF-8')) . '.';
+ }
+
+ return $this->_toComment(iconv_substr(Zend_Locale_Data::getContent($locale, 'era', array('gregorian', 'Abbr', '1')), 0, 1, 'UTF-8')) . '.';
+ break;
+
+ case self::ERA_NAME :
+ $year = $this->date('Y', $this->getUnixTimestamp(), false);
+ if ($year < 0) {
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'era', array('gregorian', 'Names', '0')));
+ }
+
+ return $this->_toComment(Zend_Locale_Data::getContent($locale, 'era', array('gregorian', 'Names', '1')));
+ break;
+
+ case self::DATES :
+ return $this->_toToken(Zend_Locale_Format::getDateFormat($locale), $locale);
+ break;
+
+ case self::DATE_FULL :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'full')), $locale);
+ break;
+
+ case self::DATE_LONG :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'long')), $locale);
+ break;
+
+ case self::DATE_MEDIUM :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'medium')), $locale);
+ break;
+
+ case self::DATE_SHORT :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'short')), $locale);
+ break;
+
+ case self::TIMES :
+ return $this->_toToken(Zend_Locale_Format::getTimeFormat($locale), $locale);
+ break;
+
+ case self::TIME_FULL :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'time', 'full'), $locale);
+ break;
+
+ case self::TIME_LONG :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'time', 'long'), $locale);
+ break;
+
+ case self::TIME_MEDIUM :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'time', 'medium'), $locale);
+ break;
+
+ case self::TIME_SHORT :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'time', 'short'), $locale);
+ break;
+
+ case self::DATETIME :
+ return $this->_toToken(Zend_Locale_Format::getDateTimeFormat($locale), $locale);
+ break;
+
+ case self::DATETIME_FULL :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'full')), $locale);
+ break;
+
+ case self::DATETIME_LONG :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'long')), $locale);
+ break;
+
+ case self::DATETIME_MEDIUM :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'medium')), $locale);
+ break;
+
+ case self::DATETIME_SHORT :
+ return $this->_toToken(Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'short')), $locale);
+ break;
+
+ case self::ATOM :
+ return 'Y\-m\-d\TH\:i\:sP';
+ break;
+
+ case self::COOKIE :
+ return 'l\, d\-M\-y H\:i\:s e';
+ break;
+
+ case self::RFC_822 :
+ return 'D\, d M y H\:i\:s O';
+ break;
+
+ case self::RFC_850 :
+ return 'l\, d\-M\-y H\:i\:s e';
+ break;
+
+ case self::RFC_1036 :
+ return 'D\, d M y H\:i\:s O';
+ break;
+
+ case self::RFC_1123 :
+ return 'D\, d M Y H\:i\:s O';
+ break;
+
+ case self::RFC_3339 :
+ return 'Y\-m\-d\TH\:i\:sP';
+ break;
+
+ case self::RSS :
+ return 'D\, d M Y H\:i\:s O';
+ break;
+
+ case self::W3C :
+ return 'Y\-m\-d\TH\:i\:sP';
+ break;
+ }
+
+ if ($token == '') {
+ return '';
+ }
+
+ switch ($token[0]) {
+ case 'y' :
+ if ((strlen($token) == 4) && (abs($this->getUnixTimestamp()) <= 0x7FFFFFFF)) {
+ return 'Y';
+ }
+
+ $length = iconv_strlen($token, 'UTF-8');
+ return $this->_toComment(str_pad($this->date('Y', $this->getUnixTimestamp(), false), $length, '0', STR_PAD_LEFT));
+ break;
+
+ case 'Y' :
+ if ((strlen($token) == 4) && (abs($this->getUnixTimestamp()) <= 0x7FFFFFFF)) {
+ return 'o';
+ }
+
+ $length = iconv_strlen($token, 'UTF-8');
+ return $this->_toComment(str_pad($this->date('o', $this->getUnixTimestamp(), false), $length, '0', STR_PAD_LEFT));
+ break;
+
+ case 'A' :
+ $length = iconv_strlen($token, 'UTF-8');
+ $result = substr($this->getMilliSecond(), 0, 3);
+ $result += $this->date('s', $this->getUnixTimestamp(), false) * 1000;
+ $result += $this->date('i', $this->getUnixTimestamp(), false) * 60000;
+ $result += $this->date('H', $this->getUnixTimestamp(), false) * 3600000;
+
+ return $this->_toComment(str_pad($result, $length, '0', STR_PAD_LEFT));
+ break;
+ }
+
+ return $this->_toComment($token);
+ }
+
+ /**
+ * Private function to make a comment of a token
+ *
+ * @param string $token
+ * @return string
+ */
+ private function _toComment($token)
+ {
+ $token = str_split($token);
+ $result = '';
+ foreach ($token as $tok) {
+ $result .= '\\' . $tok;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return digit from standard names (english)
+ * Faster implementation than locale aware searching
+ *
+ * @param string $name
+ * @return integer Number of this month
+ * @throws Zend_Date_Exception
+ */
+ private function _getDigitFromName($name)
+ {
+ switch($name) {
+ case "Jan":
+ return 1;
+
+ case "Feb":
+ return 2;
+
+ case "Mar":
+ return 3;
+
+ case "Apr":
+ return 4;
+
+ case "May":
+ return 5;
+
+ case "Jun":
+ return 6;
+
+ case "Jul":
+ return 7;
+
+ case "Aug":
+ return 8;
+
+ case "Sep":
+ return 9;
+
+ case "Oct":
+ return 10;
+
+ case "Nov":
+ return 11;
+
+ case "Dec":
+ return 12;
+
+ default:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('Month ($name) is not a known month');
+ }
+ }
+
+ /**
+ * Counts the exact year number
+ * < 70 - 2000 added, >70 < 100 - 1900, others just returned
+ *
+ * @param integer $value year number
+ * @return integer Number of year
+ */
+ public static function getFullYear($value)
+ {
+ if ($value >= 0) {
+ if ($value < 70) {
+ $value += 2000;
+ } else if ($value < 100) {
+ $value += 1900;
+ }
+ }
+ return $value;
+ }
+
+ /**
+ * Sets the given date as new date or a given datepart as new datepart returning the new datepart
+ * This could be for example a localized dayname, the date without time,
+ * the month or only the seconds. There are about 50 different supported date parts.
+ * For a complete list of supported datepart values look into the docu
+ *
+ * @param string|integer|array|Zend_Date $date Date or datepart to set
+ * @param string $part OPTIONAL Part of the date to set, if null the timestamp is set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function set($date, $part = null, $locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $part = Zend_Locale_Format::convertPhpToIsoFormat($part);
+ }
+
+ $zone = $this->getTimezoneFromString($date);
+ $this->setTimezone($zone);
+
+ $this->_calculate('set', $date, $part, $locale);
+ return $this;
+ }
+
+ /**
+ * Adds a date or datepart to the existing date, by extracting $part from $date,
+ * and modifying this object by adding that part. The $part is then extracted from
+ * this object and returned as an integer or numeric string (for large values, or $part's
+ * corresponding to pre-defined formatted date strings).
+ * This could be for example a ISO 8601 date, the hour the monthname or only the minute.
+ * There are about 50 different supported date parts.
+ * For a complete list of supported datepart values look into the docu.
+ *
+ * @param string|integer|array|Zend_Date $date Date or datepart to add
+ * @param string $part OPTIONAL Part of the date to add, if null the timestamp is added
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function add($date, $part = self::TIMESTAMP, $locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $part = Zend_Locale_Format::convertPhpToIsoFormat($part);
+ }
+
+ $this->_calculate('add', $date, $part, $locale);
+ return $this;
+ }
+
+ /**
+ * Subtracts a date from another date.
+ * This could be for example a RFC2822 date, the time,
+ * the year or only the timestamp. There are about 50 different supported date parts.
+ * For a complete list of supported datepart values look into the docu
+ * Be aware: Adding -2 Months is not equal to Subtracting 2 Months !!!
+ *
+ * @param string|integer|array|Zend_Date $date Date or datepart to subtract
+ * @param string $part OPTIONAL Part of the date to sub, if null the timestamp is subtracted
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function sub($date, $part = self::TIMESTAMP, $locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $part = Zend_Locale_Format::convertPhpToIsoFormat($part);
+ }
+
+ $this->_calculate('sub', $date, $part, $locale);
+ return $this;
+ }
+
+ /**
+ * Compares a date or datepart with the existing one.
+ * Returns -1 if earlier, 0 if equal and 1 if later.
+ *
+ * @param string|integer|array|Zend_Date $date Date or datepart to compare with the date object
+ * @param string $part OPTIONAL Part of the date to compare, if null the timestamp is subtracted
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compare($date, $part = self::TIMESTAMP, $locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $part = Zend_Locale_Format::convertPhpToIsoFormat($part);
+ }
+
+ $compare = $this->_calculate('cmp', $date, $part, $locale);
+
+ if ($compare > 0) {
+ return 1;
+ } else if ($compare < 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns a new instance of Zend_Date with the selected part copied.
+ * To make an exact copy, use PHP's clone keyword.
+ * For a complete list of supported date part values look into the docu.
+ * If a date part is copied, all other date parts are set to standard values.
+ * For example: If only YEAR is copied, the returned date object is equal to
+ * 01-01-YEAR 00:00:00 (01-01-1970 00:00:00 is equal to timestamp 0)
+ * If only HOUR is copied, the returned date object is equal to
+ * 01-01-1970 HOUR:00:00 (so $this contains a timestamp equal to a timestamp of 0 plus HOUR).
+ *
+ * @param string $part Part of the date to compare, if null the timestamp is subtracted
+ * @param string|Zend_Locale $locale OPTIONAL New object's locale. No adjustments to timezone are made.
+ * @return Zend_Date New clone with requested part
+ */
+ public function copyPart($part, $locale = null)
+ {
+ $clone = clone $this; // copy all instance variables
+ $clone->setUnixTimestamp(0); // except the timestamp
+ if ($locale != null) {
+ $clone->setLocale($locale); // set an other locale if selected
+ }
+ $clone->set($this, $part);
+ return $clone;
+ }
+
+ /**
+ * Internal function, returns the offset of a given timezone
+ *
+ * @param string $zone
+ * @return integer
+ */
+ public function getTimezoneFromString($zone)
+ {
+ if (is_array($zone)) {
+ return $this->getTimezone();
+ }
+
+ if ($zone instanceof Zend_Date) {
+ return $zone->getTimezone();
+ }
+
+ $match = array();
+ preg_match('/\dZ$/', $zone, $match);
+ if (!empty($match)) {
+ return "Etc/UTC";
+ }
+
+ preg_match('/([+-]\d{2}):{0,1}\d{2}/', $zone, $match);
+ if (!empty($match) and ($match[count($match) - 1] <= 12) and ($match[count($match) - 1] >= -12)) {
+ $zone = "Etc/GMT";
+ $zone .= ($match[count($match) - 1] < 0) ? "+" : "-";
+ $zone .= (int) abs($match[count($match) - 1]);
+ return $zone;
+ }
+
+ preg_match('/([[:alpha:]\/]{3,30})(?!.*([[:alpha:]\/]{3,30}))/', $zone, $match);
+ try {
+ if (!empty($match) and (!is_int($match[count($match) - 1]))) {
+ $oldzone = $this->getTimezone();
+ $this->setTimezone($match[count($match) - 1]);
+ $result = $this->getTimezone();
+ $this->setTimezone($oldzone);
+ if ($result !== $oldzone) {
+ return $match[count($match) - 1];
+ }
+ }
+ } catch (Exception $e) {
+ // fall through
+ }
+
+ return $this->getTimezone();
+ }
+
+ /**
+ * Calculates the date or object
+ *
+ * @param string $calc Calculation to make
+ * @param string|integer $date Date for calculation
+ * @param string|integer $comp Second date for calculation
+ * @param boolean|integer $dst Use dst correction if option is set
+ * @return integer|string|Zend_Date new timestamp or Zend_Date depending on calculation
+ */
+ private function _assign($calc, $date, $comp = 0, $dst = false)
+ {
+ switch ($calc) {
+ case 'set' :
+ if (!empty($comp)) {
+ $this->setUnixTimestamp(call_user_func(Zend_Locale_Math::$sub, $this->getUnixTimestamp(), $comp));
+ }
+ $this->setUnixTimestamp(call_user_func(Zend_Locale_Math::$add, $this->getUnixTimestamp(), $date));
+ $value = $this->getUnixTimestamp();
+ break;
+ case 'add' :
+ $this->setUnixTimestamp(call_user_func(Zend_Locale_Math::$add, $this->getUnixTimestamp(), $date));
+ $value = $this->getUnixTimestamp();
+ break;
+ case 'sub' :
+ $this->setUnixTimestamp(call_user_func(Zend_Locale_Math::$sub, $this->getUnixTimestamp(), $date));
+ $value = $this->getUnixTimestamp();
+ break;
+ default :
+ // cmp - compare
+ return call_user_func(Zend_Locale_Math::$comp, $comp, $date);
+ break;
+ }
+
+ // dst-correction if 'fix_dst' = true and dst !== false but only for non UTC and non GMT
+ if ((self::$_options['fix_dst'] === true) and ($dst !== false) and ($this->_dst === true)) {
+ $hour = $this->toString(self::HOUR, 'iso');
+ if ($hour != $dst) {
+ if (($dst == ($hour + 1)) or ($dst == ($hour - 23))) {
+ $value += 3600;
+ } else if (($dst == ($hour - 1)) or ($dst == ($hour + 23))) {
+ $value -= 3600;
+ }
+ $this->setUnixTimestamp($value);
+ }
+ }
+ return $this->getUnixTimestamp();
+ }
+
+
+ /**
+ * Calculates the date or object
+ *
+ * @param string $calc Calculation to make, one of: 'add'|'sub'|'cmp'|'copy'|'set'
+ * @param string|integer|array|Zend_Date $date Date or datepart to calculate with
+ * @param string $part Part of the date to calculate, if null the timestamp is used
+ * @param string|Zend_Locale $locale Locale for parsing input
+ * @return integer|string|Zend_Date new timestamp
+ * @throws Zend_Date_Exception
+ */
+ private function _calculate($calc, $date, $part, $locale)
+ {
+ if ($date === null) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('parameter $date must be set, null is not allowed');
+ }
+
+ if (($part !== null) && (strlen($part) !== 2) && (Zend_Locale::isLocale($part, null, false))) {
+ $locale = $part;
+ $part = null;
+ }
+
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ $locale = (string) $locale;
+
+ // Create date parts
+ $year = $this->toString(self::YEAR, 'iso');
+ $month = $this->toString(self::MONTH_SHORT, 'iso');
+ $day = $this->toString(self::DAY_SHORT, 'iso');
+ $hour = $this->toString(self::HOUR_SHORT, 'iso');
+ $minute = $this->toString(self::MINUTE_SHORT, 'iso');
+ $second = $this->toString(self::SECOND_SHORT, 'iso');
+ // If object extract value
+ if ($date instanceof Zend_Date) {
+ $date = $date->toString($part, 'iso', $locale);
+ }
+
+ if (is_array($date) === true) {
+ if (empty($part) === false) {
+ switch($part) {
+ // Fall through
+ case self::DAY:
+ case self::DAY_SHORT:
+ if (isset($date['day']) === true) {
+ $date = $date['day'];
+ }
+ break;
+ // Fall through
+ case self::WEEKDAY_SHORT:
+ case self::WEEKDAY:
+ case self::WEEKDAY_8601:
+ case self::WEEKDAY_DIGIT:
+ case self::WEEKDAY_NARROW:
+ case self::WEEKDAY_NAME:
+ if (isset($date['weekday']) === true) {
+ $date = $date['weekday'];
+ $part = self::WEEKDAY_DIGIT;
+ }
+ break;
+ case self::DAY_OF_YEAR:
+ if (isset($date['day_of_year']) === true) {
+ $date = $date['day_of_year'];
+ }
+ break;
+ // Fall through
+ case self::MONTH:
+ case self::MONTH_SHORT:
+ case self::MONTH_NAME:
+ case self::MONTH_NAME_SHORT:
+ case self::MONTH_NAME_NARROW:
+ if (isset($date['month']) === true) {
+ $date = $date['month'];
+ }
+ break;
+ // Fall through
+ case self::YEAR:
+ case self::YEAR_SHORT:
+ case self::YEAR_8601:
+ case self::YEAR_SHORT_8601:
+ if (isset($date['year']) === true) {
+ $date = $date['year'];
+ }
+ break;
+ // Fall through
+ case self::HOUR:
+ case self::HOUR_AM:
+ case self::HOUR_SHORT:
+ case self::HOUR_SHORT_AM:
+ if (isset($date['hour']) === true) {
+ $date = $date['hour'];
+ }
+ break;
+ // Fall through
+ case self::MINUTE:
+ case self::MINUTE_SHORT:
+ if (isset($date['minute']) === true) {
+ $date = $date['minute'];
+ }
+ break;
+ // Fall through
+ case self::SECOND:
+ case self::SECOND_SHORT:
+ if (isset($date['second']) === true) {
+ $date = $date['second'];
+ }
+ break;
+ // Fall through
+ case self::TIMEZONE:
+ case self::TIMEZONE_NAME:
+ if (isset($date['timezone']) === true) {
+ $date = $date['timezone'];
+ }
+ break;
+ case self::TIMESTAMP:
+ if (isset($date['timestamp']) === true) {
+ $date = $date['timestamp'];
+ }
+ break;
+ case self::WEEK:
+ if (isset($date['week']) === true) {
+ $date = $date['week'];
+ }
+ break;
+ case self::TIMEZONE_SECS:
+ if (isset($date['gmtsecs']) === true) {
+ $date = $date['gmtsecs'];
+ }
+ break;
+ default:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("datepart for part ($part) not found in array");
+ break;
+ }
+ } else {
+ $hours = 0;
+ if (isset($date['hour']) === true) {
+ $hours = $date['hour'];
+ }
+ $minutes = 0;
+ if (isset($date['minute']) === true) {
+ $minutes = $date['minute'];
+ }
+ $seconds = 0;
+ if (isset($date['second']) === true) {
+ $seconds = $date['second'];
+ }
+ $months = 0;
+ if (isset($date['month']) === true) {
+ $months = $date['month'];
+ }
+ $days = 0;
+ if (isset($date['day']) === true) {
+ $days = $date['day'];
+ }
+ $years = 0;
+ if (isset($date['year']) === true) {
+ $years = $date['year'];
+ }
+ return $this->_assign($calc, $this->mktime($hours, $minutes, $seconds, $months, $days, $years, true),
+ $this->mktime($hour, $minute, $second, $month, $day, $year, true), $hour);
+ }
+ }
+
+ // $date as object, part of foreign date as own date
+ switch($part) {
+
+ // day formats
+ case self::DAY:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + intval($date), 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + intval($day), 1970, true), $hour);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, day expected", 0, null, $date);
+ break;
+
+ case self::WEEKDAY_SHORT:
+ $daylist = Zend_Locale_Data::getList($locale, 'day');
+ $weekday = (int) $this->toString(self::WEEKDAY_DIGIT, 'iso', $locale);
+ $cnt = 0;
+
+ foreach ($daylist as $key => $value) {
+ if (strtoupper(iconv_substr($value, 0, 3, 'UTF-8')) == strtoupper($date)) {
+ $found = $cnt;
+ break;
+ }
+ ++$cnt;
+ }
+
+ // Weekday found
+ if ($cnt < 7) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + $found, 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + $weekday, 1970, true), $hour);
+ }
+
+ // Weekday not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, weekday expected", 0, null, $date);
+ break;
+
+ case self::DAY_SHORT:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + intval($date), 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + intval($day), 1970, true), $hour);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, day expected", 0, null, $date);
+ break;
+
+ case self::WEEKDAY:
+ $daylist = Zend_Locale_Data::getList($locale, 'day');
+ $weekday = (int) $this->toString(self::WEEKDAY_DIGIT, 'iso', $locale);
+ $cnt = 0;
+
+ foreach ($daylist as $key => $value) {
+ if (strtoupper($value) == strtoupper($date)) {
+ $found = $cnt;
+ break;
+ }
+ ++$cnt;
+ }
+
+ // Weekday found
+ if ($cnt < 7) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + $found, 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + $weekday, 1970, true), $hour);
+ }
+
+ // Weekday not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, weekday expected", 0, null, $date);
+ break;
+
+ case self::WEEKDAY_8601:
+ $weekday = (int) $this->toString(self::WEEKDAY_8601, 'iso', $locale);
+ if ((intval($date) > 0) and (intval($date) < 8)) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + intval($date), 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + $weekday, 1970, true), $hour);
+ }
+
+ // Weekday not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, weekday expected", 0, null, $date);
+ break;
+
+ case self::DAY_SUFFIX:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('day suffix not supported', 0, null, $date);
+ break;
+
+ case self::WEEKDAY_DIGIT:
+ $weekday = (int) $this->toString(self::WEEKDAY_DIGIT, 'iso', $locale);
+ if (is_numeric($date) and (intval($date) >= 0) and (intval($date) < 7)) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + $date, 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + $weekday, 1970, true), $hour);
+ }
+
+ // Weekday not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, weekday expected", 0, null, $date);
+ break;
+
+ case self::DAY_OF_YEAR:
+ if (is_numeric($date)) {
+ if (($calc == 'add') || ($calc == 'sub')) {
+ $year = 1970;
+ ++$date;
+ ++$day;
+ }
+
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, $date, $year, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), $hour);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, day expected", 0, null, $date);
+ break;
+
+ case self::WEEKDAY_NARROW:
+ $daylist = Zend_Locale_Data::getList($locale, 'day', array('gregorian', 'format', 'abbreviated'));
+ $weekday = (int) $this->toString(self::WEEKDAY_DIGIT, 'iso', $locale);
+ $cnt = 0;
+ foreach ($daylist as $key => $value) {
+ if (strtoupper(iconv_substr($value, 0, 1, 'UTF-8')) == strtoupper($date)) {
+ $found = $cnt;
+ break;
+ }
+ ++$cnt;
+ }
+
+ // Weekday found
+ if ($cnt < 7) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + $found, 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + $weekday, 1970, true), $hour);
+ }
+
+ // Weekday not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, weekday expected", 0, null, $date);
+ break;
+
+ case self::WEEKDAY_NAME:
+ $daylist = Zend_Locale_Data::getList($locale, 'day', array('gregorian', 'format', 'abbreviated'));
+ $weekday = (int) $this->toString(self::WEEKDAY_DIGIT, 'iso', $locale);
+ $cnt = 0;
+ foreach ($daylist as $key => $value) {
+ if (strtoupper($value) == strtoupper($date)) {
+ $found = $cnt;
+ break;
+ }
+ ++$cnt;
+ }
+
+ // Weekday found
+ if ($cnt < 7) {
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1, 1 + $found, 1970, true),
+ $this->mktime(0, 0, 0, 1, 1 + $weekday, 1970, true), $hour);
+ }
+
+ // Weekday not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, weekday expected", 0, null, $date);
+ break;
+
+ // week formats
+ case self::WEEK:
+ if (is_numeric($date)) {
+ $week = (int) $this->toString(self::WEEK, 'iso', $locale);
+ return $this->_assign($calc, parent::mktime(0, 0, 0, 1, 1 + ($date * 7), 1970, true),
+ parent::mktime(0, 0, 0, 1, 1 + ($week * 7), 1970, true), $hour);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, week expected", 0, null, $date);
+ break;
+
+ // month formats
+ case self::MONTH_NAME:
+ $monthlist = Zend_Locale_Data::getList($locale, 'month');
+ $cnt = 0;
+ foreach ($monthlist as $key => $value) {
+ if (strtoupper($value) == strtoupper($date)) {
+ $found = $key;
+ break;
+ }
+ ++$cnt;
+ }
+ $date = array_search($date, $monthlist);
+
+ // Monthname found
+ if ($cnt < 12) {
+ $fixday = 0;
+ if ($calc == 'add') {
+ $date += $found;
+ $calc = 'set';
+ if (self::$_options['extend_month'] == false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ } else if ($calc == 'sub') {
+ $date = $month - $found;
+ $calc = 'set';
+ if (self::$_options['extend_month'] == false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $date, $day + $fixday, $year, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), $hour);
+ }
+
+ // Monthname not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, month expected", 0, null, $date);
+ break;
+
+ case self::MONTH:
+ if (is_numeric($date)) {
+ $fixday = 0;
+ if ($calc == 'add') {
+ $date += $month;
+ $calc = 'set';
+ if (self::$_options['extend_month'] == false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ } else if ($calc == 'sub') {
+ $date = $month - $date;
+ $calc = 'set';
+ if (self::$_options['extend_month'] == false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $date, $day + $fixday, $year, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), $hour);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, month expected", 0, null, $date);
+ break;
+
+ case self::MONTH_NAME_SHORT:
+ $monthlist = Zend_Locale_Data::getList($locale, 'month', array('gregorian', 'format', 'abbreviated'));
+ $cnt = 0;
+ foreach ($monthlist as $key => $value) {
+ if (strtoupper($value) == strtoupper($date)) {
+ $found = $key;
+ break;
+ }
+ ++$cnt;
+ }
+ $date = array_search($date, $monthlist);
+
+ // Monthname found
+ if ($cnt < 12) {
+ $fixday = 0;
+ if ($calc == 'add') {
+ $date += $found;
+ $calc = 'set';
+ if (self::$_options['extend_month'] === false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ } else if ($calc == 'sub') {
+ $date = $month - $found;
+ $calc = 'set';
+ if (self::$_options['extend_month'] === false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $date, $day + $fixday, $year, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), $hour);
+ }
+
+ // Monthname not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, month expected", 0, null, $date);
+ break;
+
+ case self::MONTH_SHORT:
+ if (is_numeric($date) === true) {
+ $fixday = 0;
+ if ($calc === 'add') {
+ $date += $month;
+ $calc = 'set';
+ if (self::$_options['extend_month'] === false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ } else if ($calc === 'sub') {
+ $date = $month - $date;
+ $calc = 'set';
+ if (self::$_options['extend_month'] === false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ }
+
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $date, $day + $fixday, $year, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), $hour);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, month expected", 0, null, $date);
+ break;
+
+ case self::MONTH_DAYS:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('month days not supported', 0, null, $date);
+ break;
+
+ case self::MONTH_NAME_NARROW:
+ $monthlist = Zend_Locale_Data::getList($locale, 'month', array('gregorian', 'stand-alone', 'narrow'));
+ $cnt = 0;
+ foreach ($monthlist as $key => $value) {
+ if (strtoupper($value) === strtoupper($date)) {
+ $found = $key;
+ break;
+ }
+ ++$cnt;
+ }
+ $date = array_search($date, $monthlist);
+
+ // Monthname found
+ if ($cnt < 12) {
+ $fixday = 0;
+ if ($calc === 'add') {
+ $date += $found;
+ $calc = 'set';
+ if (self::$_options['extend_month'] === false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ } else if ($calc === 'sub') {
+ $date = $month - $found;
+ $calc = 'set';
+ if (self::$_options['extend_month'] === false) {
+ $parts = $this->getDateParts($this->mktime($hour, $minute, $second, $date, $day, $year, false));
+ if ($parts['mday'] != $day) {
+ $fixday = ($parts['mday'] < $day) ? -$parts['mday'] : ($parts['mday'] - $day);
+ }
+ }
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $date, $day + $fixday, $year, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), $hour);
+ }
+
+ // Monthname not found
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, month expected", 0, null, $date);
+ break;
+
+ // year formats
+ case self::LEAPYEAR:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('leap year not supported', 0, null, $date);
+ break;
+
+ case self::YEAR_8601:
+ if (is_numeric($date)) {
+ if ($calc === 'add') {
+ $date += $year;
+ $calc = 'set';
+ } else if ($calc === 'sub') {
+ $date = $year - $date;
+ $calc = 'set';
+ }
+
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $month, $day, intval($date), true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, year expected", 0, null, $date);
+ break;
+
+ case self::YEAR:
+ if (is_numeric($date)) {
+ if ($calc === 'add') {
+ $date += $year;
+ $calc = 'set';
+ } else if ($calc === 'sub') {
+ $date = $year - $date;
+ $calc = 'set';
+ }
+
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $month, $day, intval($date), true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, year expected", 0, null, $date);
+ break;
+
+ case self::YEAR_SHORT:
+ if (is_numeric($date)) {
+ $date = intval($date);
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ $date = self::getFullYear($date);
+ }
+ if ($calc === 'add') {
+ $date += $year;
+ $calc = 'set';
+ } else if ($calc === 'sub') {
+ $date = $year - $date;
+ $calc = 'set';
+ }
+
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $month, $day, $date, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, year expected", 0, null, $date);
+ break;
+
+ case self::YEAR_SHORT_8601:
+ if (is_numeric($date)) {
+ $date = intval($date);
+ if (($calc === 'set') || ($calc === 'cmp')) {
+ $date = self::getFullYear($date);
+ }
+ if ($calc === 'add') {
+ $date += $year;
+ $calc = 'set';
+ } else if ($calc === 'sub') {
+ $date = $year - $date;
+ $calc = 'set';
+ }
+
+ return $this->_assign($calc, $this->mktime(0, 0, 0, $month, $day, $date, true),
+ $this->mktime(0, 0, 0, $month, $day, $year, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, year expected", 0, null, $date);
+ break;
+
+ // time formats
+ case self::MERIDIEM:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('meridiem not supported', 0, null, $date);
+ break;
+
+ case self::SWATCH:
+ if (is_numeric($date)) {
+ $rest = intval($date);
+ $hours = floor($rest * 24 / 1000);
+ $rest = $rest - ($hours * 1000 / 24);
+ $minutes = floor($rest * 1440 / 1000);
+ $rest = $rest - ($minutes * 1000 / 1440);
+ $seconds = floor($rest * 86400 / 1000);
+ return $this->_assign($calc, $this->mktime($hours, $minutes, $seconds, 1, 1, 1970, true),
+ $this->mktime($hour, $minute, $second, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, swatchstamp expected", 0, null, $date);
+ break;
+
+ case self::HOUR_SHORT_AM:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(intval($date), 0, 0, 1, 1, 1970, true),
+ $this->mktime($hour, 0, 0, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, hour expected", 0, null, $date);
+ break;
+
+ case self::HOUR_SHORT:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(intval($date), 0, 0, 1, 1, 1970, true),
+ $this->mktime($hour, 0, 0, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, hour expected", 0, null, $date);
+ break;
+
+ case self::HOUR_AM:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(intval($date), 0, 0, 1, 1, 1970, true),
+ $this->mktime($hour, 0, 0, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, hour expected", 0, null, $date);
+ break;
+
+ case self::HOUR:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(intval($date), 0, 0, 1, 1, 1970, true),
+ $this->mktime($hour, 0, 0, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, hour expected", 0, null, $date);
+ break;
+
+ case self::MINUTE:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(0, intval($date), 0, 1, 1, 1970, true),
+ $this->mktime(0, $minute, 0, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, minute expected", 0, null, $date);
+ break;
+
+ case self::SECOND:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(0, 0, intval($date), 1, 1, 1970, true),
+ $this->mktime(0, 0, $second, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, second expected", 0, null, $date);
+ break;
+
+ case self::MILLISECOND:
+ if (is_numeric($date)) {
+ switch($calc) {
+ case 'set' :
+ return $this->setMillisecond($date);
+ break;
+ case 'add' :
+ return $this->addMillisecond($date);
+ break;
+ case 'sub' :
+ return $this->subMillisecond($date);
+ break;
+ }
+
+ return $this->compareMillisecond($date);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, milliseconds expected", 0, null, $date);
+ break;
+
+ case self::MINUTE_SHORT:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(0, intval($date), 0, 1, 1, 1970, true),
+ $this->mktime(0, $minute, 0, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, minute expected", 0, null, $date);
+ break;
+
+ case self::SECOND_SHORT:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $this->mktime(0, 0, intval($date), 1, 1, 1970, true),
+ $this->mktime(0, 0, $second, 1, 1, 1970, true), false);
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, second expected", 0, null, $date);
+ break;
+
+ // timezone formats
+ // break intentionally omitted
+ case self::TIMEZONE_NAME:
+ case self::TIMEZONE:
+ case self::TIMEZONE_SECS:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('timezone not supported', 0, null, $date);
+ break;
+
+ case self::DAYLIGHT:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('daylight not supported', 0, null, $date);
+ break;
+
+ case self::GMT_DIFF:
+ case self::GMT_DIFF_SEP:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('gmtdiff not supported', 0, null, $date);
+ break;
+
+ // date strings
+ case self::ISO_8601:
+ // (-)YYYY-MM-dd
+ preg_match('/^(-{0,1}\d{4})-(\d{2})-(\d{2})/', $date, $datematch);
+ // (-)YY-MM-dd
+ if (empty($datematch)) {
+ preg_match('/^(-{0,1}\d{2})-(\d{2})-(\d{2})/', $date, $datematch);
+ }
+ // (-)YYYYMMdd
+ if (empty($datematch)) {
+ preg_match('/^(-{0,1}\d{4})(\d{2})(\d{2})/', $date, $datematch);
+ }
+ // (-)YYMMdd
+ if (empty($datematch)) {
+ preg_match('/^(-{0,1}\d{2})(\d{2})(\d{2})/', $date, $datematch);
+ }
+ $tmpdate = $date;
+ if (!empty($datematch)) {
+ $dateMatchCharCount = iconv_strlen($datematch[0], 'UTF-8');
+ $tmpdate = iconv_substr($date,
+ $dateMatchCharCount,
+ iconv_strlen($date, 'UTF-8') - $dateMatchCharCount,
+ 'UTF-8');
+ }
+ // (T)hh:mm:ss
+ preg_match('/[T,\s]{0,1}(\d{2}):(\d{2}):(\d{2})/', $tmpdate, $timematch);
+ if (empty($timematch)) {
+ preg_match('/[T,\s]{0,1}(\d{2})(\d{2})(\d{2})/', $tmpdate, $timematch);
+ }
+ if (empty($datematch) and empty($timematch)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("unsupported ISO8601 format ($date)", 0, null, $date);
+ }
+ if (!empty($timematch)) {
+ $timeMatchCharCount = iconv_strlen($timematch[0], 'UTF-8');
+ $tmpdate = iconv_substr($tmpdate,
+ $timeMatchCharCount,
+ iconv_strlen($tmpdate, 'UTF-8') - $timeMatchCharCount,
+ 'UTF-8');
+ }
+ if (empty($datematch)) {
+ $datematch[1] = 1970;
+ $datematch[2] = 1;
+ $datematch[3] = 1;
+ } else if (iconv_strlen($datematch[1], 'UTF-8') == 2) {
+ $datematch[1] = self::getFullYear($datematch[1]);
+ }
+ if (empty($timematch)) {
+ $timematch[1] = 0;
+ $timematch[2] = 0;
+ $timematch[3] = 0;
+ }
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$datematch[2];
+ --$month;
+ --$datematch[3];
+ --$day;
+ $datematch[1] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($timematch[1], $timematch[2], $timematch[3], 1 + $datematch[2], 1 + $datematch[3], 1970 + $datematch[1], false),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, false), false);
+ break;
+
+ case self::RFC_2822:
+ $result = preg_match('/^\w{3},\s(\d{1,2})\s(\w{3})\s(\d{4})\s'
+ . '(\d{2}):(\d{2}):{0,1}(\d{0,2})\s([+-]'
+ . '{1}\d{4}|\w{1,20})$/', $date, $match);
+
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("no RFC 2822 format ($date)", 0, null, $date);
+ }
+
+ $months = $this->_getDigitFromName($match[2]);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$months;
+ --$month;
+ --$match[1];
+ --$day;
+ $match[3] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $months, 1 + $match[1], 1970 + $match[3], false),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, false), false);
+ break;
+
+ case self::TIMESTAMP:
+ if (is_numeric($date)) {
+ return $this->_assign($calc, $date, $this->getUnixTimestamp());
+ }
+
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, timestamp expected", 0, null, $date);
+ break;
+
+ // additional formats
+ // break intentionally omitted
+ case self::ERA:
+ case self::ERA_NAME:
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('era not supported', 0, null, $date);
+ break;
+
+ case self::DATES:
+ try {
+ $parsed = Zend_Locale_Format::getDate($date, array('locale' => $locale, 'format_type' => 'iso', 'fix_date' => true));
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime(0, 0, 0, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATE_FULL:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'full'));
+ $parsed = Zend_Locale_Format::getDate($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime(0, 0, 0, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATE_LONG:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'long'));
+ $parsed = Zend_Locale_Format::getDate($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+
+ if (($calc == 'set') || ($calc == 'cmp')){
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime(0, 0, 0, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATE_MEDIUM:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'medium'));
+ $parsed = Zend_Locale_Format::getDate($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime(0, 0, 0, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATE_SHORT:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'short'));
+ $parsed = Zend_Locale_Format::getDate($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+
+ $parsed['year'] = self::getFullYear($parsed['year']);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime(0, 0, 0, 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime(0, 0, 0, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::TIMES:
+ try {
+ if ($calc != 'set') {
+ $month = 1;
+ $day = 1;
+ $year = 1970;
+ }
+ $parsed = Zend_Locale_Format::getTime($date, array('locale' => $locale, 'format_type' => 'iso', 'fix_date' => true));
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], $month, $day, $year, true),
+ $this->mktime($hour, $minute, $second, $month, $day, $year, true), false);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::TIME_FULL:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'full'));
+ $parsed = Zend_Locale_Format::getTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+ if ($calc != 'set') {
+ $month = 1;
+ $day = 1;
+ $year = 1970;
+ }
+
+ if (!isset($parsed['second'])) {
+ $parsed['second'] = 0;
+ }
+
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], $month, $day, $year, true),
+ $this->mktime($hour, $minute, $second, $month, $day, $year, true), false);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::TIME_LONG:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'long'));
+ $parsed = Zend_Locale_Format::getTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+ if ($calc != 'set') {
+ $month = 1;
+ $day = 1;
+ $year = 1970;
+ }
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], $month, $day, $year, true),
+ $this->mktime($hour, $minute, $second, $month, $day, $year, true), false);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::TIME_MEDIUM:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'medium'));
+ $parsed = Zend_Locale_Format::getTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+ if ($calc != 'set') {
+ $month = 1;
+ $day = 1;
+ $year = 1970;
+ }
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], $month, $day, $year, true),
+ $this->mktime($hour, $minute, $second, $month, $day, $year, true), false);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::TIME_SHORT:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'short'));
+ $parsed = Zend_Locale_Format::getTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+ if ($calc != 'set') {
+ $month = 1;
+ $day = 1;
+ $year = 1970;
+ }
+
+ if (!isset($parsed['second'])) {
+ $parsed['second'] = 0;
+ }
+
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], $month, $day, $year, true),
+ $this->mktime($hour, $minute, $second, $month, $day, $year, true), false);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATETIME:
+ try {
+ $parsed = Zend_Locale_Format::getDateTime($date, array('locale' => $locale, 'format_type' => 'iso', 'fix_date' => true));
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATETIME_FULL:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'full'));
+ $parsed = Zend_Locale_Format::getDateTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+
+ if (!isset($parsed['second'])) {
+ $parsed['second'] = 0;
+ }
+
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATETIME_LONG:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'long'));
+ $parsed = Zend_Locale_Format::getDateTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+
+ if (($calc == 'set') || ($calc == 'cmp')){
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATETIME_MEDIUM:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'medium'));
+ $parsed = Zend_Locale_Format::getDateTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ case self::DATETIME_SHORT:
+ try {
+ $format = Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'short'));
+ $parsed = Zend_Locale_Format::getDateTime($date, array('date_format' => $format, 'format_type' => 'iso', 'locale' => $locale));
+
+ $parsed['year'] = self::getFullYear($parsed['year']);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$parsed['month'];
+ --$month;
+ --$parsed['day'];
+ --$day;
+ $parsed['year'] -= 1970;
+ $year -= 1970;
+ }
+
+ if (!isset($parsed['second'])) {
+ $parsed['second'] = 0;
+ }
+
+ return $this->_assign($calc, $this->mktime($parsed['hour'], $parsed['minute'], $parsed['second'], 1 + $parsed['month'], 1 + $parsed['day'], 1970 + $parsed['year'], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), $hour);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ break;
+
+ // ATOM and RFC_3339 are identical
+ case self::ATOM:
+ case self::RFC_3339:
+ $result = preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\d{0,4}([+-]{1}\d{2}:\d{2}|Z)$/', $date, $match);
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, ATOM format expected", 0, null, $date);
+ }
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$match[2];
+ --$month;
+ --$match[3];
+ --$day;
+ $match[1] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $match[2], 1 + $match[3], 1970 + $match[1], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), false);
+ break;
+
+ case self::COOKIE:
+ $result = preg_match("/^\w{6,9},\s(\d{2})-(\w{3})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})\s.{3,20}$/", $date, $match);
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, COOKIE format expected", 0, null, $date);
+ }
+ $matchStartPos = iconv_strpos($match[0], ' ', 0, 'UTF-8') + 1;
+ $match[0] = iconv_substr($match[0],
+ $matchStartPos,
+ iconv_strlen($match[0], 'UTF-8') - $matchStartPos,
+ 'UTF-8');
+
+ $months = $this->_getDigitFromName($match[2]);
+ $match[3] = self::getFullYear($match[3]);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$months;
+ --$month;
+ --$match[1];
+ --$day;
+ $match[3] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $months, 1 + $match[1], 1970 + $match[3], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), false);
+ break;
+
+ case self::RFC_822:
+ case self::RFC_1036:
+ // new RFC 822 format, identical to RFC 1036 standard
+ $result = preg_match('/^\w{0,3},{0,1}\s{0,1}(\d{1,2})\s(\w{3})\s(\d{2})\s(\d{2}):(\d{2}):{0,1}(\d{0,2})\s([+-]{1}\d{4}|\w{1,20})$/', $date, $match);
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, RFC 822 date format expected", 0, null, $date);
+ }
+
+ $months = $this->_getDigitFromName($match[2]);
+ $match[3] = self::getFullYear($match[3]);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$months;
+ --$month;
+ --$match[1];
+ --$day;
+ $match[3] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $months, 1 + $match[1], 1970 + $match[3], false),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, false), false);
+ break;
+
+ case self::RFC_850:
+ $result = preg_match('/^\w{6,9},\s(\d{2})-(\w{3})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})\s.{3,21}$/', $date, $match);
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, RFC 850 date format expected", 0, null, $date);
+ }
+
+ $months = $this->_getDigitFromName($match[2]);
+ $match[3] = self::getFullYear($match[3]);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$months;
+ --$month;
+ --$match[1];
+ --$day;
+ $match[3] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $months, 1 + $match[1], 1970 + $match[3], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), false);
+ break;
+
+ case self::RFC_1123:
+ $result = preg_match('/^\w{0,3},{0,1}\s{0,1}(\d{1,2})\s(\w{3})\s(\d{2,4})\s(\d{2}):(\d{2}):{0,1}(\d{0,2})\s([+-]{1}\d{4}|\w{1,20})$/', $date, $match);
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, RFC 1123 date format expected", 0, null, $date);
+ }
+
+ $months = $this->_getDigitFromName($match[2]);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$months;
+ --$month;
+ --$match[1];
+ --$day;
+ $match[3] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $months, 1 + $match[1], 1970 + $match[3], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), false);
+ break;
+
+ case self::RSS:
+ $result = preg_match('/^\w{3},\s(\d{2})\s(\w{3})\s(\d{2,4})\s(\d{1,2}):(\d{2}):(\d{2})\s.{1,21}$/', $date, $match);
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, RSS date format expected", 0, null, $date);
+ }
+
+ $months = $this->_getDigitFromName($match[2]);
+ $match[3] = self::getFullYear($match[3]);
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$months;
+ --$month;
+ --$match[1];
+ --$day;
+ $match[3] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $months, 1 + $match[1], 1970 + $match[3], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), false);
+ break;
+
+ case self::W3C:
+ $result = preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})[+-]{1}\d{2}:\d{2}$/', $date, $match);
+ if (!$result) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid date ($date) operand, W3C date format expected", 0, null, $date);
+ }
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ --$match[2];
+ --$month;
+ --$match[3];
+ --$day;
+ $match[1] -= 1970;
+ $year -= 1970;
+ }
+ return $this->_assign($calc, $this->mktime($match[4], $match[5], $match[6], 1 + $match[2], 1 + $match[3], 1970 + $match[1], true),
+ $this->mktime($hour, $minute, $second, 1 + $month, 1 + $day, 1970 + $year, true), false);
+ break;
+
+ default:
+ if (!is_numeric($date) || !empty($part)) {
+ try {
+ if (empty($part)) {
+ $part = Zend_Locale_Format::getDateFormat($locale) . " ";
+ $part .= Zend_Locale_Format::getTimeFormat($locale);
+ }
+
+ $parsed = Zend_Locale_Format::getDate($date, array('date_format' => $part, 'locale' => $locale, 'fix_date' => true, 'format_type' => 'iso'));
+ if ((strpos(strtoupper($part), 'YY') !== false) and (strpos(strtoupper($part), 'YYYY') === false)) {
+ $parsed['year'] = self::getFullYear($parsed['year']);
+ }
+
+ if (($calc == 'set') || ($calc == 'cmp')) {
+ if (isset($parsed['month'])) {
+ --$parsed['month'];
+ } else {
+ $parsed['month'] = 0;
+ }
+
+ if (isset($parsed['day'])) {
+ --$parsed['day'];
+ } else {
+ $parsed['day'] = 0;
+ }
+
+ if (isset($parsed['year'])) {
+ $parsed['year'] -= 1970;
+ } else {
+ $parsed['year'] = 0;
+ }
+ }
+
+ return $this->_assign($calc, $this->mktime(
+ isset($parsed['hour']) ? $parsed['hour'] : 0,
+ isset($parsed['minute']) ? $parsed['minute'] : 0,
+ isset($parsed['second']) ? $parsed['second'] : 0,
+ isset($parsed['month']) ? (1 + $parsed['month']) : 1,
+ isset($parsed['day']) ? (1 + $parsed['day']) : 1,
+ isset($parsed['year']) ? (1970 + $parsed['year']) : 1970,
+ false), $this->getUnixTimestamp(), false);
+ } catch (Zend_Locale_Exception $e) {
+ if (!is_numeric($date)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e, $date);
+ }
+ }
+ }
+
+ return $this->_assign($calc, $date, $this->getUnixTimestamp(), false);
+ break;
+ }
+ }
+
+ /**
+ * Returns true when both date objects or date parts are equal.
+ * For example:
+ * 15.May.2000 <-> 15.June.2000 Equals only for Day or Year... all other will return false
+ *
+ * @param string|integer|array|Zend_Date $date Date or datepart to equal with
+ * @param string $part OPTIONAL Part of the date to compare, if null the timestamp is used
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return boolean
+ * @throws Zend_Date_Exception
+ */
+ public function equals($date, $part = self::TIMESTAMP, $locale = null)
+ {
+ $result = $this->compare($date, $part, $locale);
+
+ if ($result == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns if the given date or datepart is earlier
+ * For example:
+ * 15.May.2000 <-> 13.June.1999 will return true for day, year and date, but not for month
+ *
+ * @param string|integer|array|Zend_Date $date Date or datepart to compare with
+ * @param string $part OPTIONAL Part of the date to compare, if null the timestamp is used
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return boolean
+ * @throws Zend_Date_Exception
+ */
+ public function isEarlier($date, $part = null, $locale = null)
+ {
+ $result = $this->compare($date, $part, $locale);
+
+ if ($result == -1) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns if the given date or datepart is later
+ * For example:
+ * 15.May.2000 <-> 13.June.1999 will return true for month but false for day, year and date
+ * Returns if the given date is later
+ *
+ * @param string|integer|array|Zend_Date $date Date or datepart to compare with
+ * @param string $part OPTIONAL Part of the date to compare, if null the timestamp is used
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return boolean
+ * @throws Zend_Date_Exception
+ */
+ public function isLater($date, $part = null, $locale = null)
+ {
+ $result = $this->compare($date, $part, $locale);
+
+ if ($result == 1) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns only the time of the date as new Zend_Date object
+ * For example:
+ * 15.May.2000 10:11:23 will return a dateobject equal to 01.Jan.1970 10:11:23
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getTime($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'H:i:s';
+ } else {
+ $format = self::TIME_MEDIUM;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+ /**
+ * Returns the calculated time
+ *
+ * @param string $calc Calculation to make
+ * @param string|integer|array|Zend_Date $time Time to calculate with, if null the actual time is taken
+ * @param string $format Timeformat for parsing input
+ * @param string|Zend_Locale $locale Locale for parsing input
+ * @return integer|Zend_Date new time
+ * @throws Zend_Date_Exception
+ */
+ private function _time($calc, $time, $format, $locale)
+ {
+ if ($time === null) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('parameter $time must be set, null is not allowed');
+ }
+
+ if ($time instanceof Zend_Date) {
+ // extract time from object
+ $time = $time->toString('HH:mm:ss', 'iso');
+ } else {
+ if (is_array($time)) {
+ if ((isset($time['hour']) === true) or (isset($time['minute']) === true) or
+ (isset($time['second']) === true)) {
+ $parsed = $time;
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("no hour, minute or second given in array");
+ }
+ } else {
+ if (self::$_options['format_type'] == 'php') {
+ $format = Zend_Locale_Format::convertPhpToIsoFormat($format);
+ }
+ try {
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ $parsed = Zend_Locale_Format::getTime($time, array('date_format' => $format, 'locale' => $locale, 'format_type' => 'iso'));
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e);
+ }
+ }
+
+ if (!array_key_exists('hour', $parsed)) {
+ $parsed['hour'] = 0;
+ }
+
+ if (!array_key_exists('minute', $parsed)) {
+ $parsed['minute'] = 0;
+ }
+
+ if (!array_key_exists('second', $parsed)) {
+ $parsed['second'] = 0;
+ }
+
+ $time = str_pad($parsed['hour'], 2, '0', STR_PAD_LEFT) . ":";
+ $time .= str_pad($parsed['minute'], 2, '0', STR_PAD_LEFT) . ":";
+ $time .= str_pad($parsed['second'], 2, '0', STR_PAD_LEFT);
+ }
+
+ $return = $this->_calcdetail($calc, $time, self::TIMES, 'de');
+ if ($calc != 'cmp') {
+ return $this;
+ }
+
+ return $return;
+ }
+
+
+ /**
+ * Sets a new time for the date object. Format defines how to parse the time string.
+ * Also a complete date can be given, but only the time is used for setting.
+ * For example: dd.MMMM.yyTHH:mm' and 'ss sec'-> 10.May.07T25:11 and 44 sec => 1h11min44sec + 1 day
+ * Returned is the new date object and the existing date is left as it was before
+ *
+ * @param string|integer|array|Zend_Date $time Time to set
+ * @param string $format OPTIONAL Timeformat for parsing input
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setTime($time, $format = null, $locale = null)
+ {
+ return $this->_time('set', $time, $format, $locale);
+ }
+
+
+ /**
+ * Adds a time to the existing date. Format defines how to parse the time string.
+ * If only parts are given the other parts are set to 0.
+ * If no format is given, the standardformat of this locale is used.
+ * For example: HH:mm:ss -> 10 -> +10 hours
+ *
+ * @param string|integer|array|Zend_Date $time Time to add
+ * @param string $format OPTIONAL Timeformat for parsing input
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addTime($time, $format = null, $locale = null)
+ {
+ return $this->_time('add', $time, $format, $locale);
+ }
+
+
+ /**
+ * Subtracts a time from the existing date. Format defines how to parse the time string.
+ * If only parts are given the other parts are set to 0.
+ * If no format is given, the standardformat of this locale is used.
+ * For example: HH:mm:ss -> 10 -> -10 hours
+ *
+ * @param string|integer|array|Zend_Date $time Time to sub
+ * @param string $format OPTIONAL Timeformat for parsing input
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid inteface
+ * @throws Zend_Date_Exception
+ */
+ public function subTime($time, $format = null, $locale = null)
+ {
+ return $this->_time('sub', $time, $format, $locale);
+ }
+
+
+ /**
+ * Compares the time from the existing date. Format defines how to parse the time string.
+ * If only parts are given the other parts are set to default.
+ * If no format us given, the standardformat of this locale is used.
+ * For example: HH:mm:ss -> 10 -> 10 hours
+ *
+ * @param string|integer|array|Zend_Date $time Time to compare
+ * @param string $format OPTIONAL Timeformat for parsing input
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareTime($time, $format = null, $locale = null)
+ {
+ return $this->_time('cmp', $time, $format, $locale);
+ }
+
+ /**
+ * Returns a clone of $this, with the time part set to 00:00:00.
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getDate($locale = null)
+ {
+ $orig = self::$_options['format_type'];
+ if (self::$_options['format_type'] == 'php') {
+ self::$_options['format_type'] = 'iso';
+ }
+
+ $date = $this->copyPart(self::DATE_MEDIUM, $locale);
+ $date->addTimestamp($this->getGmtOffset());
+ self::$_options['format_type'] = $orig;
+
+ return $date;
+ }
+
+ /**
+ * Returns the calculated date
+ *
+ * @param string $calc Calculation to make
+ * @param string|integer|array|Zend_Date $date Date to calculate with, if null the actual date is taken
+ * @param string $format Date format for parsing
+ * @param string|Zend_Locale $locale Locale for parsing input
+ * @return integer|Zend_Date new date
+ * @throws Zend_Date_Exception
+ */
+ private function _date($calc, $date, $format, $locale)
+ {
+ if ($date === null) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('parameter $date must be set, null is not allowed');
+ }
+
+ if ($date instanceof Zend_Date) {
+ // extract date from object
+ $date = $date->toString('d.M.y', 'iso');
+ } else {
+ if (is_array($date)) {
+ if ((isset($date['year']) === true) or (isset($date['month']) === true) or
+ (isset($date['day']) === true)) {
+ $parsed = $date;
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("no day,month or year given in array");
+ }
+ } else {
+ if ((self::$_options['format_type'] == 'php') && !defined($format)) {
+ $format = Zend_Locale_Format::convertPhpToIsoFormat($format);
+ }
+ try {
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ $parsed = Zend_Locale_Format::getDate($date, array('date_format' => $format, 'locale' => $locale, 'format_type' => 'iso'));
+ if ((strpos(strtoupper($format), 'YY') !== false) and (strpos(strtoupper($format), 'YYYY') === false)) {
+ $parsed['year'] = self::getFullYear($parsed['year']);
+ }
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e);
+ }
+ }
+
+ if (!array_key_exists('day', $parsed)) {
+ $parsed['day'] = 1;
+ }
+
+ if (!array_key_exists('month', $parsed)) {
+ $parsed['month'] = 1;
+ }
+
+ if (!array_key_exists('year', $parsed)) {
+ $parsed['year'] = 0;
+ }
+
+ $date = $parsed['day'] . "." . $parsed['month'] . "." . $parsed['year'];
+ }
+
+ $return = $this->_calcdetail($calc, $date, self::DATE_MEDIUM, 'de');
+ if ($calc != 'cmp') {
+ return $this;
+ }
+ return $return;
+ }
+
+
+ /**
+ * Sets a new date for the date object. Format defines how to parse the date string.
+ * Also a complete date with time can be given, but only the date is used for setting.
+ * For example: MMMM.yy HH:mm-> May.07 22:11 => 01.May.07 00:00
+ * Returned is the new date object and the existing time is left as it was before
+ *
+ * @param string|integer|array|Zend_Date $date Date to set
+ * @param string $format OPTIONAL Date format for parsing
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setDate($date, $format = null, $locale = null)
+ {
+ return $this->_date('set', $date, $format, $locale);
+ }
+
+
+ /**
+ * Adds a date to the existing date object. Format defines how to parse the date string.
+ * If only parts are given the other parts are set to 0.
+ * If no format is given, the standardformat of this locale is used.
+ * For example: MM.dd.YYYY -> 10 -> +10 months
+ *
+ * @param string|integer|array|Zend_Date $date Date to add
+ * @param string $format OPTIONAL Date format for parsing input
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addDate($date, $format = null, $locale = null)
+ {
+ return $this->_date('add', $date, $format, $locale);
+ }
+
+
+ /**
+ * Subtracts a date from the existing date object. Format defines how to parse the date string.
+ * If only parts are given the other parts are set to 0.
+ * If no format is given, the standardformat of this locale is used.
+ * For example: MM.dd.YYYY -> 10 -> -10 months
+ * Be aware: Subtracting 2 months is not equal to Adding -2 months !!!
+ *
+ * @param string|integer|array|Zend_Date $date Date to sub
+ * @param string $format OPTIONAL Date format for parsing input
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subDate($date, $format = null, $locale = null)
+ {
+ return $this->_date('sub', $date, $format, $locale);
+ }
+
+
+ /**
+ * Compares the date from the existing date object, ignoring the time.
+ * Format defines how to parse the date string.
+ * If only parts are given the other parts are set to 0.
+ * If no format is given, the standardformat of this locale is used.
+ * For example: 10.01.2000 => 10.02.1999 -> false
+ *
+ * @param string|integer|array|Zend_Date $date Date to compare
+ * @param string $format OPTIONAL Date format for parsing input
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareDate($date, $format = null, $locale = null)
+ {
+ return $this->_date('cmp', $date, $format, $locale);
+ }
+
+
+ /**
+ * Returns the full ISO 8601 date from the date object.
+ * Always the complete ISO 8601 specifiction is used. If an other ISO date is needed
+ * (ISO 8601 defines several formats) use toString() instead.
+ * This function does not return the ISO date as object. Use copy() instead.
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return string
+ */
+ public function getIso($locale = null)
+ {
+ return $this->toString(self::ISO_8601, 'iso', $locale);
+ }
+
+
+ /**
+ * Sets a new date for the date object. Not given parts are set to default.
+ * Only supported ISO 8601 formats are accepted.
+ * For example: 050901 -> 01.Sept.2005 00:00:00, 20050201T10:00:30 -> 01.Feb.2005 10h00m30s
+ * Returned is the new date object
+ *
+ * @param string|integer|Zend_Date $date ISO Date to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setIso($date, $locale = null)
+ {
+ return $this->_calcvalue('set', $date, 'iso', self::ISO_8601, $locale);
+ }
+
+
+ /**
+ * Adds a ISO date to the date object. Not given parts are set to default.
+ * Only supported ISO 8601 formats are accepted.
+ * For example: 050901 -> + 01.Sept.2005 00:00:00, 10:00:00 -> +10h
+ * Returned is the new date object
+ *
+ * @param string|integer|Zend_Date $date ISO Date to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addIso($date, $locale = null)
+ {
+ return $this->_calcvalue('add', $date, 'iso', self::ISO_8601, $locale);
+ }
+
+
+ /**
+ * Subtracts a ISO date from the date object. Not given parts are set to default.
+ * Only supported ISO 8601 formats are accepted.
+ * For example: 050901 -> - 01.Sept.2005 00:00:00, 10:00:00 -> -10h
+ * Returned is the new date object
+ *
+ * @param string|integer|Zend_Date $date ISO Date to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subIso($date, $locale = null)
+ {
+ return $this->_calcvalue('sub', $date, 'iso', self::ISO_8601, $locale);
+ }
+
+
+ /**
+ * Compares a ISO date with the date object. Not given parts are set to default.
+ * Only supported ISO 8601 formats are accepted.
+ * For example: 050901 -> - 01.Sept.2005 00:00:00, 10:00:00 -> -10h
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|Zend_Date $date ISO Date to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareIso($date, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $date, 'iso', self::ISO_8601, $locale);
+ }
+
+
+ /**
+ * Returns a RFC 822 compilant datestring from the date object.
+ * This function does not return the RFC date as object. Use copy() instead.
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return string
+ */
+ public function getArpa($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'D\, d M y H\:i\:s O';
+ } else {
+ $format = self::RFC_822;
+ }
+
+ return $this->toString($format, 'iso', $locale);
+ }
+
+
+ /**
+ * Sets a RFC 822 date as new date for the date object.
+ * Only RFC 822 compilant date strings are accepted.
+ * For example: Sat, 14 Feb 09 00:31:30 +0100
+ * Returned is the new date object
+ *
+ * @param string|integer|Zend_Date $date RFC 822 to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setArpa($date, $locale = null)
+ {
+ return $this->_calcvalue('set', $date, 'arpa', self::RFC_822, $locale);
+ }
+
+
+ /**
+ * Adds a RFC 822 date to the date object.
+ * ARPA messages are used in emails or HTTP Headers.
+ * Only RFC 822 compilant date strings are accepted.
+ * For example: Sat, 14 Feb 09 00:31:30 +0100
+ * Returned is the new date object
+ *
+ * @param string|integer|Zend_Date $date RFC 822 Date to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addArpa($date, $locale = null)
+ {
+ return $this->_calcvalue('add', $date, 'arpa', self::RFC_822, $locale);
+ }
+
+
+ /**
+ * Subtracts a RFC 822 date from the date object.
+ * ARPA messages are used in emails or HTTP Headers.
+ * Only RFC 822 compilant date strings are accepted.
+ * For example: Sat, 14 Feb 09 00:31:30 +0100
+ * Returned is the new date object
+ *
+ * @param string|integer|Zend_Date $date RFC 822 Date to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subArpa($date, $locale = null)
+ {
+ return $this->_calcvalue('sub', $date, 'arpa', self::RFC_822, $locale);
+ }
+
+
+ /**
+ * Compares a RFC 822 compilant date with the date object.
+ * ARPA messages are used in emails or HTTP Headers.
+ * Only RFC 822 compilant date strings are accepted.
+ * For example: Sat, 14 Feb 09 00:31:30 +0100
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|Zend_Date $date RFC 822 Date to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareArpa($date, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $date, 'arpa', self::RFC_822, $locale);
+ }
+
+
+ /**
+ * Check if location is supported
+ *
+ * @param array $location locations array
+ * @return $horizon float
+ */
+ private function _checkLocation($location)
+ {
+ if (!isset($location['longitude']) or !isset($location['latitude'])) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('Location must include \'longitude\' and \'latitude\'', 0, null, $location);
+ }
+ if (($location['longitude'] > 180) or ($location['longitude'] < -180)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('Longitude must be between -180 and 180', 0, null, $location);
+ }
+ if (($location['latitude'] > 90) or ($location['latitude'] < -90)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('Latitude must be between -90 and 90', 0, null, $location);
+ }
+
+ if (!isset($location['horizon'])){
+ $location['horizon'] = 'effective';
+ }
+
+ switch ($location['horizon']) {
+ case 'civil' :
+ return -0.104528;
+ break;
+ case 'nautic' :
+ return -0.207912;
+ break;
+ case 'astronomic' :
+ return -0.309017;
+ break;
+ default :
+ return -0.0145439;
+ break;
+ }
+ }
+
+
+ /**
+ * Returns the time of sunrise for this date and a given location as new date object
+ * For a list of cities and correct locations use the class Zend_Date_Cities
+ *
+ * @param array $location location of sunrise
+ * ['horizon'] -> civil, nautic, astronomical, effective (default)
+ * ['longitude'] -> longitude of location
+ * ['latitude'] -> latitude of location
+ * @return Zend_Date
+ * @throws Zend_Date_Exception
+ */
+ public function getSunrise($location)
+ {
+ $horizon = $this->_checkLocation($location);
+ $result = clone $this;
+ $result->set($this->calcSun($location, $horizon, true), self::TIMESTAMP);
+ return $result;
+ }
+
+
+ /**
+ * Returns the time of sunset for this date and a given location as new date object
+ * For a list of cities and correct locations use the class Zend_Date_Cities
+ *
+ * @param array $location location of sunset
+ * ['horizon'] -> civil, nautic, astronomical, effective (default)
+ * ['longitude'] -> longitude of location
+ * ['latitude'] -> latitude of location
+ * @return Zend_Date
+ * @throws Zend_Date_Exception
+ */
+ public function getSunset($location)
+ {
+ $horizon = $this->_checkLocation($location);
+ $result = clone $this;
+ $result->set($this->calcSun($location, $horizon, false), self::TIMESTAMP);
+ return $result;
+ }
+
+
+ /**
+ * Returns an array with the sunset and sunrise dates for all horizon types
+ * For a list of cities and correct locations use the class Zend_Date_Cities
+ *
+ * @param array $location location of suninfo
+ * ['horizon'] -> civil, nautic, astronomical, effective (default)
+ * ['longitude'] -> longitude of location
+ * ['latitude'] -> latitude of location
+ * @return array - [sunset|sunrise][effective|civil|nautic|astronomic]
+ * @throws Zend_Date_Exception
+ */
+ public function getSunInfo($location)
+ {
+ $suninfo = array();
+ for ($i = 0; $i < 4; ++$i) {
+ switch ($i) {
+ case 0 :
+ $location['horizon'] = 'effective';
+ break;
+ case 1 :
+ $location['horizon'] = 'civil';
+ break;
+ case 2 :
+ $location['horizon'] = 'nautic';
+ break;
+ case 3 :
+ $location['horizon'] = 'astronomic';
+ break;
+ }
+ $horizon = $this->_checkLocation($location);
+ $result = clone $this;
+ $result->set($this->calcSun($location, $horizon, true), self::TIMESTAMP);
+ $suninfo['sunrise'][$location['horizon']] = $result;
+ $result = clone $this;
+ $result->set($this->calcSun($location, $horizon, false), self::TIMESTAMP);
+ $suninfo['sunset'][$location['horizon']] = $result;
+ }
+ return $suninfo;
+ }
+
+
+ /**
+ * Check a given year for leap year.
+ *
+ * @param integer|array|Zend_Date $year Year to check
+ * @return boolean
+ */
+ public static function checkLeapYear($year)
+ {
+ if ($year instanceof Zend_Date) {
+ $year = (int) $year->toString(self::YEAR, 'iso');
+ }
+
+ if (is_array($year)) {
+ if (isset($year['year']) === true) {
+ $year = $year['year'];
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("no year given in array");
+ }
+ }
+
+ if (!is_numeric($year)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("year ($year) has to be integer for checkLeapYear()", 0, null, $year);
+ }
+
+ return (bool) parent::isYearLeapYear($year);
+ }
+
+
+ /**
+ * Returns true, if the year is a leap year.
+ *
+ * @return boolean
+ */
+ public function isLeapYear()
+ {
+ return self::checkLeapYear($this);
+ }
+
+
+ /**
+ * Returns if the set date is todays date
+ *
+ * @return boolean
+ */
+ public function isToday()
+ {
+ $today = $this->date('Ymd', $this->_getTime());
+ $day = $this->date('Ymd', $this->getUnixTimestamp());
+ return ($today == $day);
+ }
+
+
+ /**
+ * Returns if the set date is yesterdays date
+ *
+ * @return boolean
+ */
+ public function isYesterday()
+ {
+ list($year, $month, $day) = explode('-', $this->date('Y-m-d', $this->_getTime()));
+ // adjusts for leap days and DST changes that are timezone specific
+ $yesterday = $this->date('Ymd', $this->mktime(0, 0, 0, $month, $day -1, $year));
+ $day = $this->date('Ymd', $this->getUnixTimestamp());
+ return $day == $yesterday;
+ }
+
+
+ /**
+ * Returns if the set date is tomorrows date
+ *
+ * @return boolean
+ */
+ public function isTomorrow()
+ {
+ list($year, $month, $day) = explode('-', $this->date('Y-m-d', $this->_getTime()));
+ // adjusts for leap days and DST changes that are timezone specific
+ $tomorrow = $this->date('Ymd', $this->mktime(0, 0, 0, $month, $day +1, $year));
+ $day = $this->date('Ymd', $this->getUnixTimestamp());
+ return $day == $tomorrow;
+ }
+
+ /**
+ * Returns the actual date as new date object
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public static function now($locale = null)
+ {
+ return new Zend_Date(time(), self::TIMESTAMP, $locale);
+ }
+
+ /**
+ * Calculate date details
+ *
+ * @param string $calc Calculation to make
+ * @param string|integer|array|Zend_Date $date Date or Part to calculate
+ * @param string $part Datepart for Calculation
+ * @param string|Zend_Locale $locale Locale for parsing input
+ * @return integer|string new date
+ * @throws Zend_Date_Exception
+ */
+ private function _calcdetail($calc, $date, $type, $locale)
+ {
+ $old = false;
+ if (self::$_options['format_type'] == 'php') {
+ self::$_options['format_type'] = 'iso';
+ $old = true;
+ }
+
+ switch($calc) {
+ case 'set' :
+ $return = $this->set($date, $type, $locale);
+ break;
+ case 'add' :
+ $return = $this->add($date, $type, $locale);
+ break;
+ case 'sub' :
+ $return = $this->sub($date, $type, $locale);
+ break;
+ default :
+ $return = $this->compare($date, $type, $locale);
+ break;
+ }
+
+ if ($old) {
+ self::$_options['format_type'] = 'php';
+ }
+
+ return $return;
+ }
+
+ /**
+ * Internal calculation, returns the requested date type
+ *
+ * @param string $calc Calculation to make
+ * @param string|integer|Zend_Date $value Datevalue to calculate with, if null the actual value is taken
+ * @param string|Zend_Locale $locale Locale for parsing input
+ * @return integer|Zend_Date new date
+ * @throws Zend_Date_Exception
+ */
+ private function _calcvalue($calc, $value, $type, $parameter, $locale)
+ {
+ if ($value === null) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("parameter $type must be set, null is not allowed");
+ }
+
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ if ($value instanceof Zend_Date) {
+ // extract value from object
+ $value = $value->toString($parameter, 'iso', $locale);
+ } else if (!is_array($value) && !is_numeric($value) && ($type != 'iso') && ($type != 'arpa')) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid $type ($value) operand", 0, null, $value);
+ }
+
+ $return = $this->_calcdetail($calc, $value, $parameter, $locale);
+ if ($calc != 'cmp') {
+ return $this;
+ }
+ return $return;
+ }
+
+
+ /**
+ * Returns only the year from the date object as new object.
+ * For example: 10.May.2000 10:30:00 -> 01.Jan.2000 00:00:00
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getYear($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'Y';
+ } else {
+ $format = self::YEAR;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+
+ /**
+ * Sets a new year
+ * If the year is between 0 and 69, 2000 will be set (2000-2069)
+ * If the year if between 70 and 99, 1999 will be set (1970-1999)
+ * 3 or 4 digit years are set as expected. If you need to set year 0-99
+ * use set() instead.
+ * Returned is the new date object
+ *
+ * @param string|integer|array|Zend_Date $date Year to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setYear($year, $locale = null)
+ {
+ return $this->_calcvalue('set', $year, 'year', self::YEAR, $locale);
+ }
+
+
+ /**
+ * Adds the year to the existing date object
+ * If the year is between 0 and 69, 2000 will be added (2000-2069)
+ * If the year if between 70 and 99, 1999 will be added (1970-1999)
+ * 3 or 4 digit years are added as expected. If you need to add years from 0-99
+ * use add() instead.
+ * Returned is the new date object
+ *
+ * @param string|integer|array|Zend_Date $date Year to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addYear($year, $locale = null)
+ {
+ return $this->_calcvalue('add', $year, 'year', self::YEAR, $locale);
+ }
+
+
+ /**
+ * Subs the year from the existing date object
+ * If the year is between 0 and 69, 2000 will be subtracted (2000-2069)
+ * If the year if between 70 and 99, 1999 will be subtracted (1970-1999)
+ * 3 or 4 digit years are subtracted as expected. If you need to subtract years from 0-99
+ * use sub() instead.
+ * Returned is the new date object
+ *
+ * @param string|integer|array|Zend_Date $date Year to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subYear($year, $locale = null)
+ {
+ return $this->_calcvalue('sub', $year, 'year', self::YEAR, $locale);
+ }
+
+
+ /**
+ * Compares the year with the existing date object, ignoring other date parts.
+ * For example: 10.03.2000 -> 15.02.2000 -> true
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $year Year to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareYear($year, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $year, 'year', self::YEAR, $locale);
+ }
+
+
+ /**
+ * Returns only the month from the date object as new object.
+ * For example: 10.May.2000 10:30:00 -> 01.May.1970 00:00:00
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getMonth($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'm';
+ } else {
+ $format = self::MONTH;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+
+ /**
+ * Returns the calculated month
+ *
+ * @param string $calc Calculation to make
+ * @param string|integer|array|Zend_Date $month Month to calculate with, if null the actual month is taken
+ * @param string|Zend_Locale $locale Locale for parsing input
+ * @return integer|Zend_Date new time
+ * @throws Zend_Date_Exception
+ */
+ private function _month($calc, $month, $locale)
+ {
+ if ($month === null) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('parameter $month must be set, null is not allowed');
+ }
+
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ if ($month instanceof Zend_Date) {
+ // extract month from object
+ $found = $month->toString(self::MONTH_SHORT, 'iso', $locale);
+ } else {
+ if (is_numeric($month)) {
+ $found = $month;
+ } else if (is_array($month)) {
+ if (isset($month['month']) === true) {
+ $month = $month['month'];
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("no month given in array");
+ }
+ } else {
+ $monthlist = Zend_Locale_Data::getList($locale, 'month');
+ $monthlist2 = Zend_Locale_Data::getList($locale, 'month', array('gregorian', 'format', 'abbreviated'));
+
+ $monthlist = array_merge($monthlist, $monthlist2);
+ $found = 0;
+ $cnt = 0;
+ foreach ($monthlist as $key => $value) {
+ if (strtoupper($value) == strtoupper($month)) {
+ $found = ($key % 12) + 1;
+ break;
+ }
+ ++$cnt;
+ }
+ if ($found == 0) {
+ foreach ($monthlist2 as $key => $value) {
+ if (strtoupper(iconv_substr($value, 0, 1, 'UTF-8')) == strtoupper($month)) {
+ $found = $key + 1;
+ break;
+ }
+ ++$cnt;
+ }
+ }
+ if ($found == 0) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("unknown month name ($month)", 0, null, $month);
+ }
+ }
+ }
+ $return = $this->_calcdetail($calc, $found, self::MONTH_SHORT, $locale);
+ if ($calc != 'cmp') {
+ return $this;
+ }
+ return $return;
+ }
+
+
+ /**
+ * Sets a new month
+ * The month can be a number or a string. Setting months lower then 0 and greater then 12
+ * will result in adding or subtracting the relevant year. (12 months equal one year)
+ * If a localized monthname is given it will be parsed with the default locale or the optional
+ * set locale.
+ * Returned is the new date object
+ *
+ * @param string|integer|array|Zend_Date $month Month to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setMonth($month, $locale = null)
+ {
+ return $this->_month('set', $month, $locale);
+ }
+
+
+ /**
+ * Adds months to the existing date object.
+ * The month can be a number or a string. Adding months lower then 0 and greater then 12
+ * will result in adding or subtracting the relevant year. (12 months equal one year)
+ * If a localized monthname is given it will be parsed with the default locale or the optional
+ * set locale.
+ * Returned is the new date object
+ *
+ * @param string|integer|array|Zend_Date $month Month to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addMonth($month, $locale = null)
+ {
+ return $this->_month('add', $month, $locale);
+ }
+
+
+ /**
+ * Subtracts months from the existing date object.
+ * The month can be a number or a string. Subtracting months lower then 0 and greater then 12
+ * will result in adding or subtracting the relevant year. (12 months equal one year)
+ * If a localized monthname is given it will be parsed with the default locale or the optional
+ * set locale.
+ * Returned is the new date object
+ *
+ * @param string|integer|array|Zend_Date $month Month to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subMonth($month, $locale = null)
+ {
+ return $this->_month('sub', $month, $locale);
+ }
+
+
+ /**
+ * Compares the month with the existing date object, ignoring other date parts.
+ * For example: 10.03.2000 -> 15.03.1950 -> true
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $month Month to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareMonth($month, $locale = null)
+ {
+ return $this->_month('cmp', $month, $locale);
+ }
+
+
+ /**
+ * Returns the day as new date object
+ * Example: 20.May.1986 -> 20.Jan.1970 00:00:00
+ *
+ * @param Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getDay($locale = null)
+ {
+ return $this->copyPart(self::DAY_SHORT, $locale);
+ }
+
+
+ /**
+ * Returns the calculated day
+ *
+ * @param string $calc Type of calculation to make
+ * @param Zend_Date $day Day to calculate, when null the actual day is calculated
+ * @param Zend_Locale $locale Locale for parsing input
+ * @return Zend_Date|integer
+ */
+ private function _day($calc, $day, $locale)
+ {
+ if ($day === null) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('parameter $day must be set, null is not allowed');
+ }
+
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ if ($day instanceof Zend_Date) {
+ $day = $day->toString(self::DAY_SHORT, 'iso', $locale);
+ }
+
+ if (is_numeric($day)) {
+ $type = self::DAY_SHORT;
+ } else if (is_array($day)) {
+ if (isset($day['day']) === true) {
+ $day = $day['day'];
+ $type = self::WEEKDAY;
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("no day given in array");
+ }
+ } else {
+ switch (iconv_strlen($day, 'UTF-8')) {
+ case 1 :
+ $type = self::WEEKDAY_NARROW;
+ break;
+ case 2:
+ $type = self::WEEKDAY_NAME;
+ break;
+ case 3:
+ $type = self::WEEKDAY_SHORT;
+ break;
+ default:
+ $type = self::WEEKDAY;
+ break;
+ }
+ }
+ $return = $this->_calcdetail($calc, $day, $type, $locale);
+ if ($calc != 'cmp') {
+ return $this;
+ }
+ return $return;
+ }
+
+
+ /**
+ * Sets a new day
+ * The day can be a number or a string. Setting days lower then 0 or greater than the number of this months days
+ * will result in adding or subtracting the relevant month.
+ * If a localized dayname is given it will be parsed with the default locale or the optional
+ * set locale.
+ * Returned is the new date object
+ * Example: setDay('Montag', 'de_AT'); will set the monday of this week as day.
+ *
+ * @param string|integer|array|Zend_Date $month Day to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setDay($day, $locale = null)
+ {
+ return $this->_day('set', $day, $locale);
+ }
+
+
+ /**
+ * Adds days to the existing date object.
+ * The day can be a number or a string. Adding days lower then 0 or greater than the number of this months days
+ * will result in adding or subtracting the relevant month.
+ * If a localized dayname is given it will be parsed with the default locale or the optional
+ * set locale.
+ *
+ * @param string|integer|array|Zend_Date $month Day to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addDay($day, $locale = null)
+ {
+ return $this->_day('add', $day, $locale);
+ }
+
+
+ /**
+ * Subtracts days from the existing date object.
+ * The day can be a number or a string. Subtracting days lower then 0 or greater than the number of this months days
+ * will result in adding or subtracting the relevant month.
+ * If a localized dayname is given it will be parsed with the default locale or the optional
+ * set locale.
+ *
+ * @param string|integer|array|Zend_Date $month Day to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subDay($day, $locale = null)
+ {
+ return $this->_day('sub', $day, $locale);
+ }
+
+
+ /**
+ * Compares the day with the existing date object, ignoring other date parts.
+ * For example: 'Monday', 'en' -> 08.Jan.2007 -> 0
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $day Day to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareDay($day, $locale = null)
+ {
+ return $this->_day('cmp', $day, $locale);
+ }
+
+
+ /**
+ * Returns the weekday as new date object
+ * Weekday is always from 1-7
+ * Example: 09-Jan-2007 -> 2 = Tuesday -> 02-Jan-1970 (when 02.01.1970 is also Tuesday)
+ *
+ * @param Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getWeekday($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'l';
+ } else {
+ $format = self::WEEKDAY;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+
+ /**
+ * Returns the calculated weekday
+ *
+ * @param string $calc Type of calculation to make
+ * @param Zend_Date $weekday Weekday to calculate, when null the actual weekday is calculated
+ * @param Zend_Locale $locale Locale for parsing input
+ * @return Zend_Date|integer
+ * @throws Zend_Date_Exception
+ */
+ private function _weekday($calc, $weekday, $locale)
+ {
+ if ($weekday === null) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('parameter $weekday must be set, null is not allowed');
+ }
+
+ if ($locale === null) {
+ $locale = $this->getLocale();
+ }
+
+ if ($weekday instanceof Zend_Date) {
+ $weekday = $weekday->toString(self::WEEKDAY_8601, 'iso', $locale);
+ }
+
+ if (is_numeric($weekday)) {
+ $type = self::WEEKDAY_8601;
+ } else if (is_array($weekday)) {
+ if (isset($weekday['weekday']) === true) {
+ $weekday = $weekday['weekday'];
+ $type = self::WEEKDAY;
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("no weekday given in array");
+ }
+ } else {
+ switch(iconv_strlen($weekday, 'UTF-8')) {
+ case 1:
+ $type = self::WEEKDAY_NARROW;
+ break;
+ case 2:
+ $type = self::WEEKDAY_NAME;
+ break;
+ case 3:
+ $type = self::WEEKDAY_SHORT;
+ break;
+ default:
+ $type = self::WEEKDAY;
+ break;
+ }
+ }
+ $return = $this->_calcdetail($calc, $weekday, $type, $locale);
+ if ($calc != 'cmp') {
+ return $this;
+ }
+ return $return;
+ }
+
+
+ /**
+ * Sets a new weekday
+ * The weekday can be a number or a string. If a localized weekday name is given,
+ * then it will be parsed as a date in $locale (defaults to the same locale as $this).
+ * Returned is the new date object.
+ * Example: setWeekday(3); will set the wednesday of this week as day.
+ *
+ * @param string|integer|array|Zend_Date $month Weekday to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setWeekday($weekday, $locale = null)
+ {
+ return $this->_weekday('set', $weekday, $locale);
+ }
+
+
+ /**
+ * Adds weekdays to the existing date object.
+ * The weekday can be a number or a string.
+ * If a localized dayname is given it will be parsed with the default locale or the optional
+ * set locale.
+ * Returned is the new date object
+ * Example: addWeekday(3); will add the difference of days from the begining of the month until
+ * wednesday.
+ *
+ * @param string|integer|array|Zend_Date $month Weekday to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addWeekday($weekday, $locale = null)
+ {
+ return $this->_weekday('add', $weekday, $locale);
+ }
+
+
+ /**
+ * Subtracts weekdays from the existing date object.
+ * The weekday can be a number or a string.
+ * If a localized dayname is given it will be parsed with the default locale or the optional
+ * set locale.
+ * Returned is the new date object
+ * Example: subWeekday(3); will subtract the difference of days from the begining of the month until
+ * wednesday.
+ *
+ * @param string|integer|array|Zend_Date $month Weekday to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subWeekday($weekday, $locale = null)
+ {
+ return $this->_weekday('sub', $weekday, $locale);
+ }
+
+
+ /**
+ * Compares the weekday with the existing date object, ignoring other date parts.
+ * For example: 'Monday', 'en' -> 08.Jan.2007 -> 0
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $weekday Weekday to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareWeekday($weekday, $locale = null)
+ {
+ return $this->_weekday('cmp', $weekday, $locale);
+ }
+
+
+ /**
+ * Returns the day of year as new date object
+ * Example: 02.Feb.1986 10:00:00 -> 02.Feb.1970 00:00:00
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getDayOfYear($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'D';
+ } else {
+ $format = self::DAY_OF_YEAR;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+
+ /**
+ * Sets a new day of year
+ * The day of year is always a number.
+ * Returned is the new date object
+ * Example: 04.May.2004 -> setDayOfYear(10) -> 10.Jan.2004
+ *
+ * @param string|integer|array|Zend_Date $day Day of Year to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setDayOfYear($day, $locale = null)
+ {
+ return $this->_calcvalue('set', $day, 'day of year', self::DAY_OF_YEAR, $locale);
+ }
+
+
+ /**
+ * Adds a day of year to the existing date object.
+ * The day of year is always a number.
+ * Returned is the new date object
+ * Example: addDayOfYear(10); will add 10 days to the existing date object.
+ *
+ * @param string|integer|array|Zend_Date $day Day of Year to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addDayOfYear($day, $locale = null)
+ {
+ return $this->_calcvalue('add', $day, 'day of year', self::DAY_OF_YEAR, $locale);
+ }
+
+
+ /**
+ * Subtracts a day of year from the existing date object.
+ * The day of year is always a number.
+ * Returned is the new date object
+ * Example: subDayOfYear(10); will subtract 10 days from the existing date object.
+ *
+ * @param string|integer|array|Zend_Date $day Day of Year to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subDayOfYear($day, $locale = null)
+ {
+ return $this->_calcvalue('sub', $day, 'day of year', self::DAY_OF_YEAR, $locale);
+ }
+
+
+ /**
+ * Compares the day of year with the existing date object.
+ * For example: compareDayOfYear(33) -> 02.Feb.2007 -> 0
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $day Day of Year to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareDayOfYear($day, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $day, 'day of year', self::DAY_OF_YEAR, $locale);
+ }
+
+
+ /**
+ * Returns the hour as new date object
+ * Example: 02.Feb.1986 10:30:25 -> 01.Jan.1970 10:00:00
+ *
+ * @param Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getHour($locale = null)
+ {
+ return $this->copyPart(self::HOUR, $locale);
+ }
+
+
+ /**
+ * Sets a new hour
+ * The hour is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> setHour(7); -> 04.May.1993 07:07:25
+ *
+ * @param string|integer|array|Zend_Date $hour Hour to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setHour($hour, $locale = null)
+ {
+ return $this->_calcvalue('set', $hour, 'hour', self::HOUR_SHORT, $locale);
+ }
+
+
+ /**
+ * Adds hours to the existing date object.
+ * The hour is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> addHour(12); -> 05.May.1993 01:07:25
+ *
+ * @param string|integer|array|Zend_Date $hour Hour to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addHour($hour, $locale = null)
+ {
+ return $this->_calcvalue('add', $hour, 'hour', self::HOUR_SHORT, $locale);
+ }
+
+
+ /**
+ * Subtracts hours from the existing date object.
+ * The hour is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> subHour(6); -> 05.May.1993 07:07:25
+ *
+ * @param string|integer|array|Zend_Date $hour Hour to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subHour($hour, $locale = null)
+ {
+ return $this->_calcvalue('sub', $hour, 'hour', self::HOUR_SHORT, $locale);
+ }
+
+
+ /**
+ * Compares the hour with the existing date object.
+ * For example: 10:30:25 -> compareHour(10) -> 0
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $hour Hour to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareHour($hour, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $hour, 'hour', self::HOUR_SHORT, $locale);
+ }
+
+
+ /**
+ * Returns the minute as new date object
+ * Example: 02.Feb.1986 10:30:25 -> 01.Jan.1970 00:30:00
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getMinute($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'i';
+ } else {
+ $format = self::MINUTE;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+
+ /**
+ * Sets a new minute
+ * The minute is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> setMinute(29); -> 04.May.1993 13:29:25
+ *
+ * @param string|integer|array|Zend_Date $minute Minute to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setMinute($minute, $locale = null)
+ {
+ return $this->_calcvalue('set', $minute, 'minute', self::MINUTE_SHORT, $locale);
+ }
+
+
+ /**
+ * Adds minutes to the existing date object.
+ * The minute is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> addMinute(65); -> 04.May.1993 13:12:25
+ *
+ * @param string|integer|array|Zend_Date $minute Minute to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addMinute($minute, $locale = null)
+ {
+ return $this->_calcvalue('add', $minute, 'minute', self::MINUTE_SHORT, $locale);
+ }
+
+
+ /**
+ * Subtracts minutes from the existing date object.
+ * The minute is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> subMinute(9); -> 04.May.1993 12:58:25
+ *
+ * @param string|integer|array|Zend_Date $minute Minute to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subMinute($minute, $locale = null)
+ {
+ return $this->_calcvalue('sub', $minute, 'minute', self::MINUTE_SHORT, $locale);
+ }
+
+
+ /**
+ * Compares the minute with the existing date object.
+ * For example: 10:30:25 -> compareMinute(30) -> 0
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $minute Hour to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareMinute($minute, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $minute, 'minute', self::MINUTE_SHORT, $locale);
+ }
+
+
+ /**
+ * Returns the second as new date object
+ * Example: 02.Feb.1986 10:30:25 -> 01.Jan.1970 00:00:25
+ *
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getSecond($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 's';
+ } else {
+ $format = self::SECOND;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+
+ /**
+ * Sets new seconds to the existing date object.
+ * The second is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> setSecond(100); -> 04.May.1993 13:08:40
+ *
+ * @param string|integer|array|Zend_Date $second Second to set
+ * @param string|Zend_Locale $locale (Optional) Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setSecond($second, $locale = null)
+ {
+ return $this->_calcvalue('set', $second, 'second', self::SECOND_SHORT, $locale);
+ }
+
+
+ /**
+ * Adds seconds to the existing date object.
+ * The second is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> addSecond(65); -> 04.May.1993 13:08:30
+ *
+ * @param string|integer|array|Zend_Date $second Second to add
+ * @param string|Zend_Locale $locale (Optional) Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addSecond($second, $locale = null)
+ {
+ return $this->_calcvalue('add', $second, 'second', self::SECOND_SHORT, $locale);
+ }
+
+
+ /**
+ * Subtracts seconds from the existing date object.
+ * The second is always a number.
+ * Returned is the new date object
+ * Example: 04.May.1993 13:07:25 -> subSecond(10); -> 04.May.1993 13:07:15
+ *
+ * @param string|integer|array|Zend_Date $second Second to sub
+ * @param string|Zend_Locale $locale (Optional) Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subSecond($second, $locale = null)
+ {
+ return $this->_calcvalue('sub', $second, 'second', self::SECOND_SHORT, $locale);
+ }
+
+
+ /**
+ * Compares the second with the existing date object.
+ * For example: 10:30:25 -> compareSecond(25) -> 0
+ * Returns if equal, earlier or later
+ *
+ * @param string|integer|array|Zend_Date $second Second to compare
+ * @param string|Zend_Locale $locale (Optional) Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ * @throws Zend_Date_Exception
+ */
+ public function compareSecond($second, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $second, 'second', self::SECOND_SHORT, $locale);
+ }
+
+
+ /**
+ * Returns the precision for fractional seconds
+ *
+ * @return integer
+ */
+ public function getFractionalPrecision()
+ {
+ return $this->_precision;
+ }
+
+
+ /**
+ * Sets a new precision for fractional seconds
+ *
+ * @param integer $precision Precision for the fractional datepart 3 = milliseconds
+ * @throws Zend_Date_Exception
+ * @return Zend_Date Provides fluid interface
+ */
+ public function setFractionalPrecision($precision)
+ {
+ if (!intval($precision) or ($precision < 0) or ($precision > 9)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("precision ($precision) must be a positive integer less than 10", 0, null, $precision);
+ }
+
+ $this->_precision = (int) $precision;
+ if ($this->_precision < strlen($this->_fractional)) {
+ $this->_fractional = substr($this->_fractional, 0, $this->_precision);
+ } else {
+ $this->_fractional = str_pad($this->_fractional, $this->_precision, '0', STR_PAD_RIGHT);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Returns the milliseconds of the date object
+ *
+ * @return string
+ */
+ public function getMilliSecond()
+ {
+ return $this->_fractional;
+ }
+
+
+ /**
+ * Sets new milliseconds for the date object
+ * Example: setMilliSecond(550, 2) -> equals +5 Sec +50 MilliSec
+ *
+ * @param integer|Zend_Date $milli (Optional) Millisecond to set, when null the actual millisecond is set
+ * @param integer $precision (Optional) Fraction precision of the given milliseconds
+ * @return Zend_Date Provides fluid interface
+ */
+ public function setMilliSecond($milli = null, $precision = null)
+ {
+ if ($milli === null) {
+ list($milli, $time) = explode(" ", microtime());
+ $milli = intval($milli);
+ $precision = 6;
+ } else if (!is_numeric($milli)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid milli second ($milli) operand", 0, null, $milli);
+ }
+
+ if ($precision === null) {
+ $precision = $this->_precision;
+ }
+
+ if (!is_int($precision) || $precision < 1 || $precision > 9) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("precision ($precision) must be a positive integer less than 10", 0, null, $precision);
+ }
+
+ $this->_fractional = 0;
+ $this->addMilliSecond($milli, $precision);
+ return $this;
+ }
+
+
+ /**
+ * Adds milliseconds to the date object
+ *
+ * @param integer|Zend_Date $milli (Optional) Millisecond to add, when null the actual millisecond is added
+ * @param integer $precision (Optional) Fractional precision for the given milliseconds
+ * @return Zend_Date Provides fluid interface
+ */
+ public function addMilliSecond($milli = null, $precision = null)
+ {
+ if ($milli === null) {
+ list($milli, $time) = explode(" ", microtime());
+ $milli = intval($milli);
+ } else if (!is_numeric($milli)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid milli second ($milli) operand", 0, null, $milli);
+ }
+
+ if ($precision === null) {
+ $precision = strlen($milli);
+ if ($milli < 0) {
+ --$precision;
+ }
+ }
+
+ if (!is_int($precision) || $precision < 1 || $precision > 9) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("precision ($precision) must be a positive integer less than 10", 0, null, $precision);
+ }
+
+ $this->_fractional += $milli;
+
+ // Add/sub milliseconds + add/sub seconds
+ $max = pow(10, $this->_precision);
+ // Milli includes seconds
+ if ($this->_fractional >= $max) {
+ while ($this->_fractional >= $max) {
+ $this->addSecond(1);
+ $this->_fractional -= $max;
+ }
+ }
+
+ if ($this->_fractional < 0) {
+ while ($this->_fractional < 0) {
+ $this->subSecond(1);
+ $this->_fractional += $max;
+ }
+ }
+
+ if ($this->_precision > strlen($this->_fractional)) {
+ $this->_fractional = str_pad($this->_fractional, $this->_precision, '0', STR_PAD_LEFT);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Subtracts a millisecond
+ *
+ * @param integer|Zend_Date $milli (Optional) Millisecond to sub, when null the actual millisecond is subtracted
+ * @param integer $precision (Optional) Fractional precision for the given milliseconds
+ * @return Zend_Date Provides fluid interface
+ */
+ public function subMilliSecond($milli = null, $precision = null)
+ {
+ $this->addMilliSecond(0 - $milli, $precision);
+ return $this;
+ }
+
+ /**
+ * Compares only the millisecond part, returning the difference
+ *
+ * @param integer|Zend_Date $milli OPTIONAL Millisecond to compare, when null the actual millisecond is compared
+ * @param integer $precision OPTIONAL Fractional precision for the given milliseconds
+ * @throws Zend_Date_Exception On invalid input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ */
+ public function compareMilliSecond($milli = null, $precision = null)
+ {
+ if ($milli === null) {
+ list($milli, $time) = explode(" ", microtime());
+ $milli = intval($milli);
+ } else if (is_numeric($milli) === false) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("invalid milli second ($milli) operand", 0, null, $milli);
+ }
+
+ if ($precision === null) {
+ $precision = strlen($milli);
+ } else if (!is_int($precision) || $precision < 1 || $precision > 9) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("precision ($precision) must be a positive integer less than 10", 0, null, $precision);
+ }
+
+ if ($precision === 0) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('precision is 0');
+ }
+
+ if ($precision != $this->_precision) {
+ if ($precision > $this->_precision) {
+ $diff = $precision - $this->_precision;
+ $milli = (int) ($milli / (10 * $diff));
+ } else {
+ $diff = $this->_precision - $precision;
+ $milli = (int) ($milli * (10 * $diff));
+ }
+ }
+
+ $comp = $this->_fractional - $milli;
+ if ($comp < 0) {
+ return -1;
+ } else if ($comp > 0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the week as new date object using monday as begining of the week
+ * Example: 12.Jan.2007 -> 08.Jan.1970 00:00:00
+ *
+ * @param Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date
+ */
+ public function getWeek($locale = null)
+ {
+ if (self::$_options['format_type'] == 'php') {
+ $format = 'W';
+ } else {
+ $format = self::WEEK;
+ }
+
+ return $this->copyPart($format, $locale);
+ }
+
+ /**
+ * Sets a new week. The week is always a number. The day of week is not changed.
+ * Returned is the new date object
+ * Example: 09.Jan.2007 13:07:25 -> setWeek(1); -> 02.Jan.2007 13:07:25
+ *
+ * @param string|integer|array|Zend_Date $week Week to set
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function setWeek($week, $locale = null)
+ {
+ return $this->_calcvalue('set', $week, 'week', self::WEEK, $locale);
+ }
+
+ /**
+ * Adds a week. The week is always a number. The day of week is not changed.
+ * Returned is the new date object
+ * Example: 09.Jan.2007 13:07:25 -> addWeek(1); -> 16.Jan.2007 13:07:25
+ *
+ * @param string|integer|array|Zend_Date $week Week to add
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function addWeek($week, $locale = null)
+ {
+ return $this->_calcvalue('add', $week, 'week', self::WEEK, $locale);
+ }
+
+ /**
+ * Subtracts a week. The week is always a number. The day of week is not changed.
+ * Returned is the new date object
+ * Example: 09.Jan.2007 13:07:25 -> subWeek(1); -> 02.Jan.2007 13:07:25
+ *
+ * @param string|integer|array|Zend_Date $week Week to sub
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return Zend_Date Provides fluid interface
+ * @throws Zend_Date_Exception
+ */
+ public function subWeek($week, $locale = null)
+ {
+ return $this->_calcvalue('sub', $week, 'week', self::WEEK, $locale);
+ }
+
+ /**
+ * Compares only the week part, returning the difference
+ * Returned is the new date object
+ * Returns if equal, earlier or later
+ * Example: 09.Jan.2007 13:07:25 -> compareWeek(2); -> 0
+ *
+ * @param string|integer|array|Zend_Date $week Week to compare
+ * @param string|Zend_Locale $locale OPTIONAL Locale for parsing input
+ * @return integer 0 = equal, 1 = later, -1 = earlier
+ */
+ public function compareWeek($week, $locale = null)
+ {
+ return $this->_calcvalue('cmp', $week, 'week', self::WEEK, $locale);
+ }
+
+ /**
+ * Sets a new standard locale for the date object.
+ * This locale will be used for all functions
+ * Returned is the really set locale.
+ * Example: 'de_XX' will be set to 'de' because 'de_XX' does not exist
+ * 'xx_YY' will be set to 'root' because 'xx' does not exist
+ *
+ * @param string|Zend_Locale $locale (Optional) Locale for parsing input
+ * @throws Zend_Date_Exception When the given locale does not exist
+ * @return Zend_Date Provides fluent interface
+ */
+ public function setLocale($locale = null)
+ {
+ try {
+ $this->_locale = Zend_Locale::findLocale($locale);
+ } catch (Zend_Locale_Exception $e) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception($e->getMessage(), 0, $e);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the actual set locale
+ *
+ * @return string
+ */
+ public function getLocale()
+ {
+ return $this->_locale;
+ }
+
+ /**
+ * Checks if the given date is a real date or datepart.
+ * Returns false if a expected datepart is missing or a datepart exceeds its possible border.
+ * But the check will only be done for the expected dateparts which are given by format.
+ * If no format is given the standard dateformat for the actual locale is used.
+ * f.e. 30.February.2007 will return false if format is 'dd.MMMM.YYYY'
+ *
+ * @param string|array|Zend_Date $date Date to parse for correctness
+ * @param string $format (Optional) Format for parsing the date string
+ * @param string|Zend_Locale $locale (Optional) Locale for parsing date parts
+ * @return boolean True when all date parts are correct
+ */
+ public static function isDate($date, $format = null, $locale = null)
+ {
+ if (!is_string($date) && !is_numeric($date) && !($date instanceof Zend_Date) &&
+ !is_array($date)) {
+ return false;
+ }
+
+ if (($format !== null) && ($format != 'ee') && ($format != 'ss') && ($format != 'GG') && ($format != 'MM') && ($format != 'EE') && ($format != 'TT')
+ && (Zend_Locale::isLocale($format, null, false))) {
+ $locale = $format;
+ $format = null;
+ }
+
+ $locale = Zend_Locale::findLocale($locale);
+
+ if ($format === null) {
+ $format = Zend_Locale_Format::getDateFormat($locale);
+ } else if ((self::$_options['format_type'] == 'php') && !defined($format)) {
+ $format = Zend_Locale_Format::convertPhpToIsoFormat($format);
+ }
+
+ $format = self::_getLocalizedToken($format, $locale);
+ if (!is_array($date)) {
+ try {
+ $parsed = Zend_Locale_Format::getDate($date, array('locale' => $locale,
+ 'date_format' => $format, 'format_type' => 'iso',
+ 'fix_date' => false));
+ } catch (Zend_Locale_Exception $e) {
+ // Date can not be parsed
+ return false;
+ }
+ } else {
+ $parsed = $date;
+ }
+
+ if (((strpos($format, 'Y') !== false) or (strpos($format, 'y') !== false)) and
+ (!isset($parsed['year']))) {
+ // Year expected but not found
+ return false;
+ }
+
+ if ((strpos($format, 'M') !== false) and (!isset($parsed['month']))) {
+ // Month expected but not found
+ return false;
+ }
+
+ if ((strpos($format, 'd') !== false) and (!isset($parsed['day']))) {
+ // Day expected but not found
+ return false;
+ }
+
+ if (((strpos($format, 'H') !== false) or (strpos($format, 'h') !== false)) and
+ (!isset($parsed['hour']))) {
+ // Hour expected but not found
+ return false;
+ }
+
+ if ((strpos($format, 'm') !== false) and (!isset($parsed['minute']))) {
+ // Minute expected but not found
+ return false;
+ }
+
+ if ((strpos($format, 's') !== false) and (!isset($parsed['second']))) {
+ // Second expected but not found
+ return false;
+ }
+
+ // Set not given dateparts
+ if (isset($parsed['hour']) === false) {
+ $parsed['hour'] = 12;
+ }
+
+ if (isset($parsed['minute']) === false) {
+ $parsed['minute'] = 0;
+ }
+
+ if (isset($parsed['second']) === false) {
+ $parsed['second'] = 0;
+ }
+
+ if (isset($parsed['month']) === false) {
+ $parsed['month'] = 1;
+ }
+
+ if (isset($parsed['day']) === false) {
+ $parsed['day'] = 1;
+ }
+
+ if (isset($parsed['year']) === false) {
+ $parsed['year'] = 1970;
+ }
+
+ if (self::isYearLeapYear($parsed['year'])) {
+ $parsed['year'] = 1972;
+ } else {
+ $parsed['year'] = 1971;
+ }
+
+ $date = new self($parsed, null, $locale);
+ $timestamp = $date->mktime($parsed['hour'], $parsed['minute'], $parsed['second'],
+ $parsed['month'], $parsed['day'], $parsed['year']);
+
+ if ($parsed['year'] != $date->date('Y', $timestamp)) {
+ // Given year differs from parsed year
+ return false;
+ }
+
+ if ($parsed['month'] != $date->date('n', $timestamp)) {
+ // Given month differs from parsed month
+ return false;
+ }
+
+ if ($parsed['day'] != $date->date('j', $timestamp)) {
+ // Given day differs from parsed day
+ return false;
+ }
+
+ if ($parsed['hour'] != $date->date('G', $timestamp)) {
+ // Given hour differs from parsed hour
+ return false;
+ }
+
+ if ($parsed['minute'] != $date->date('i', $timestamp)) {
+ // Given minute differs from parsed minute
+ return false;
+ }
+
+ if ($parsed['second'] != $date->date('s', $timestamp)) {
+ // Given second differs from parsed second
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the ISO Token for all localized constants
+ *
+ * @param string $token Token to normalize
+ * @param string $locale Locale to search
+ * @return string
+ */
+ protected static function _getLocalizedToken($token, $locale)
+ {
+ switch($token) {
+ case self::ISO_8601 :
+ return "yyyy-MM-ddThh:mm:ss";
+ break;
+ case self::RFC_2822 :
+ return "EEE, dd MMM yyyy HH:mm:ss";
+ break;
+ case self::DATES :
+ return Zend_Locale_Data::getContent($locale, 'date');
+ break;
+ case self::DATE_FULL :
+ return Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'full'));
+ break;
+ case self::DATE_LONG :
+ return Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'long'));
+ break;
+ case self::DATE_MEDIUM :
+ return Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'medium'));
+ break;
+ case self::DATE_SHORT :
+ return Zend_Locale_Data::getContent($locale, 'date', array('gregorian', 'short'));
+ break;
+ case self::TIMES :
+ return Zend_Locale_Data::getContent($locale, 'time');
+ break;
+ case self::TIME_FULL :
+ return Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'full'));
+ break;
+ case self::TIME_LONG :
+ return Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'long'));
+ break;
+ case self::TIME_MEDIUM :
+ return Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'medium'));
+ break;
+ case self::TIME_SHORT :
+ return Zend_Locale_Data::getContent($locale, 'time', array('gregorian', 'short'));
+ break;
+ case self::DATETIME :
+ return Zend_Locale_Data::getContent($locale, 'datetime');
+ break;
+ case self::DATETIME_FULL :
+ return Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'full'));
+ break;
+ case self::DATETIME_LONG :
+ return Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'long'));
+ break;
+ case self::DATETIME_MEDIUM :
+ return Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'medium'));
+ break;
+ case self::DATETIME_SHORT :
+ return Zend_Locale_Data::getContent($locale, 'datetime', array('gregorian', 'short'));
+ break;
+ case self::ATOM :
+ case self::RFC_3339 :
+ case self::W3C :
+ return "yyyy-MM-DD HH:mm:ss";
+ break;
+ case self::COOKIE :
+ case self::RFC_850 :
+ return "EEEE, dd-MM-yyyy HH:mm:ss";
+ break;
+ case self::RFC_822 :
+ case self::RFC_1036 :
+ case self::RFC_1123 :
+ case self::RSS :
+ return "EEE, dd MM yyyy HH:mm:ss";
+ break;
+ }
+
+ return $token;
+ }
+}
diff --git a/library/Zend/Date/Cities.php b/library/Zend/Date/Cities.php
new file mode 100644
index 0000000..bf71bce
--- /dev/null
+++ b/library/Zend/Date/Cities.php
@@ -0,0 +1,322 @@
+ array('latitude' => 5.3411111, 'longitude' => -4.0280556),
+ 'Abu Dhabi' => array('latitude' => 24.4666667, 'longitude' => 54.3666667),
+ 'Abuja' => array('latitude' => 9.1758333, 'longitude' => 7.1808333),
+ 'Accra' => array('latitude' => 5.55, 'longitude' => -0.2166667),
+ 'Adamstown' => array('latitude' => -25.0666667, 'longitude' => -130.0833333),
+ 'Addis Ababa' => array('latitude' => 9.0333333, 'longitude' => 38.7),
+ 'Adelaide' => array('latitude' => -34.9333333, 'longitude' => 138.6),
+ 'Algiers' => array('latitude' => 36.7630556, 'longitude' => 3.0505556),
+ 'Alofi' => array('latitude' => -19.0166667, 'longitude' => -169.9166667),
+ 'Amman' => array('latitude' => 31.95, 'longitude' => 35.9333333),
+ 'Amsterdam' => array('latitude' => 52.35, 'longitude' => 4.9166667),
+ 'Andorra la Vella' => array('latitude' => 42.5, 'longitude' => 1.5166667),
+ 'Ankara' => array('latitude' => 39.9272222, 'longitude' => 32.8644444),
+ 'Antananarivo' => array('latitude' => -18.9166667, 'longitude' => 47.5166667),
+ 'Apia' => array('latitude' => -13.8333333, 'longitude' => -171.7333333),
+ 'Ashgabat' => array('latitude' => 37.95, 'longitude' => 58.3833333),
+ 'Asmara' => array('latitude' => 15.3333333, 'longitude' => 38.9333333),
+ 'Astana' => array('latitude' => 51.1811111, 'longitude' => 71.4277778),
+ 'Asunción' => array('latitude' => -25.2666667, 'longitude' => -57.6666667),
+ 'Athens' => array('latitude' => 37.9833333, 'longitude' => 23.7333333),
+ 'Auckland' => array('latitude' => -36.8666667, 'longitude' => 174.7666667),
+ 'Avarua' => array('latitude' => -21.2, 'longitude' => -159.7666667),
+ 'Baghdad' => array('latitude' => 33.3386111, 'longitude' => 44.3938889),
+ 'Baku' => array('latitude' => 40.3952778, 'longitude' => 49.8822222),
+ 'Bamako' => array('latitude' => 12.65, 'longitude' => -8),
+ 'Bandar Seri Begawan' => array('latitude' => 4.8833333, 'longitude' => 114.9333333),
+ 'Bankok' => array('latitude' => 13.5833333, 'longitude' => 100.2166667),
+ 'Bangui' => array('latitude' => 4.3666667, 'longitude' => 18.5833333),
+ 'Banjul' => array('latitude' => 13.4530556, 'longitude' => -16.5775),
+ 'Basel' => array('latitude' => 47.5666667, 'longitude' => 7.6),
+ 'Basseterre' => array('latitude' => 17.3, 'longitude' => -62.7166667),
+ 'Beijing' => array('latitude' => 39.9288889, 'longitude' => 116.3883333),
+ 'Beirut' => array('latitude' => 33.8719444, 'longitude' => 35.5097222),
+ 'Belgrade' => array('latitude' => 44.8186111, 'longitude' => 20.4680556),
+ 'Belmopan' => array('latitude' => 17.25, 'longitude' => -88.7666667),
+ 'Berlin' => array('latitude' => 52.5166667, 'longitude' => 13.4),
+ 'Bern' => array('latitude' => 46.9166667, 'longitude' => 7.4666667),
+ 'Bishkek' => array('latitude' => 42.8730556, 'longitude' => 74.6002778),
+ 'Bissau' => array('latitude' => 11.85, 'longitude' => -15.5833333),
+ 'Bloemfontein' => array('latitude' => -29.1333333, 'longitude' => 26.2),
+ 'Bogotá' => array('latitude' => 4.6, 'longitude' => -74.0833333),
+ 'Brasilia' => array('latitude' => -15.7833333, 'longitude' => -47.9166667),
+ 'Bratislava' => array('latitude' => 48.15, 'longitude' => 17.1166667),
+ 'Brazzaville' => array('latitude' => -4.2591667, 'longitude' => 15.2847222),
+ 'Bridgetown' => array('latitude' => 13.1, 'longitude' => -59.6166667),
+ 'Brisbane' => array('latitude' => -27.5, 'longitude' => 153.0166667),
+ 'Brussels' => array('latitude' => 50.8333333, 'longitude' => 4.3333333),
+ 'Bucharest' => array('latitude' => 44.4333333, 'longitude' => 26.1),
+ 'Budapest' => array('latitude' => 47.5, 'longitude' => 19.0833333),
+ 'Buenos Aires' => array('latitude' => -34.5875, 'longitude' => -58.6725),
+ 'Bujumbura' => array('latitude' => -3.3761111, 'longitude' => 29.36),
+ 'Cairo' => array('latitude' => 30.05, 'longitude' => 31.25),
+ 'Calgary' => array('latitude' => 51.0833333, 'longitude' => -114.0833333),
+ 'Canberra' => array('latitude' => -35.2833333, 'longitude' => 149.2166667),
+ 'Cape Town' => array('latitude' => -33.9166667, 'longitude' => 18.4166667),
+ 'Caracas' => array('latitude' => 10.5, 'longitude' => -66.9166667),
+ 'Castries' => array('latitude' => 14, 'longitude' => -61),
+ 'Charlotte Amalie' => array('latitude' => 18.34389, 'longitude' => -64.93111),
+ 'Chicago' => array('latitude' => 41.85, 'longitude' => -87.65),
+ 'Chisinau' => array('latitude' => 47.055556, 'longitude' => 28.8575),
+ 'Cockburn Town' => array('latitude' => 21.4666667, 'longitude' => -71.1333333),
+ 'Colombo' => array('latitude' => 6.9319444, 'longitude' => 79.8477778),
+ 'Conakry' => array('latitude' => 9.5091667, 'longitude' => -13.7122222),
+ 'Copenhagen' => array('latitude' => 55.6666667, 'longitude' => 12.5833333),
+ 'Cotonou' => array('latitude' => 6.35, 'longitude' => 2.4333333),
+ 'Dakar' => array('latitude' => 14.6708333, 'longitude' => -17.4380556),
+ 'Damascus' => array('latitude' => 33.5, 'longitude' => 36.3),
+ 'Dar es Salaam' => array('latitude' => -6.8, 'longitude' => 39.2833333),
+ 'Dhaka' => array('latitude' => 23.7230556, 'longitude' => 90.4086111),
+ 'Dili' => array('latitude' => -8.5586111, 'longitude' => 125.5736111),
+ 'Djibouti' => array('latitude' => 11.595, 'longitude' => 43.1480556),
+ 'Dodoma' => array('latitude' => -6.1833333, 'longitude' => 35.75),
+ 'Doha' => array('latitude' => 25.2866667, 'longitude' => 51.5333333),
+ 'Dubai' => array('latitude' => 25.2522222, 'longitude' => 55.28),
+ 'Dublin' => array('latitude' => 53.3330556, 'longitude' => -6.2488889),
+ 'Dushanbe' => array('latitude' => 38.56, 'longitude' => 68.7738889 ),
+ 'Fagatogo' => array('latitude' => -14.2825, 'longitude' => -170.69),
+ 'Fongafale' => array('latitude' => -8.5166667, 'longitude' => 179.2166667),
+ 'Freetown' => array('latitude' => 8.49, 'longitude' => -13.2341667),
+ 'Gaborone' => array('latitude' => -24.6463889, 'longitude' => 25.9119444),
+ 'Geneva' => array('latitude' => 46.2, 'longitude' => 6.1666667),
+ 'George Town' => array('latitude' => 19.3, 'longitude' => -81.3833333),
+ 'Georgetown' => array('latitude' => 6.8, 'longitude' => -58.1666667),
+ 'Gibraltar' => array('latitude' => 36.1333333, 'longitude' => -5.35),
+ 'Glasgow' => array('latitude' => 55.8333333, 'longitude' => -4.25),
+ 'Guatemala la Nueva' => array('latitude' => 14.6211111, 'longitude' => -90.5269444),
+ 'Hagatna' => array('latitude' => 13.47417, 'longitude' => 144.74778),
+ 'The Hague' => array('latitude' => 52.0833333, 'longitude' => 4.3),
+ 'Hamilton' => array('latitude' => 32.2941667, 'longitude' => -64.7838889),
+ 'Hanoi' => array('latitude' => 21.0333333, 'longitude' => 105.85),
+ 'Harare' => array('latitude' => -17.8177778, 'longitude' => 31.0447222),
+ 'Havana' => array('latitude' => 23.1319444, 'longitude' => -82.3641667),
+ 'Helsinki' => array('latitude' => 60.1755556, 'longitude' => 24.9341667),
+ 'Honiara' => array('latitude' => -9.4333333, 'longitude' => 159.95),
+ 'Islamabad' => array('latitude' => 30.8486111, 'longitude' => 72.4944444),
+ 'Istanbul' => array('latitude' => 41.0186111, 'longitude' => 28.9647222),
+ 'Jakarta' => array('latitude' => -6.1744444, 'longitude' => 106.8294444),
+ 'Jamestown' => array('latitude' => -15.9333333, 'longitude' => -5.7166667),
+ 'Jerusalem' => array('latitude' => 31.7666667, 'longitude' => 35.2333333),
+ 'Johannesburg' => array('latitude' => -26.2, 'longitude' => 28.0833333),
+ 'Kabul' => array('latitude' => 34.5166667, 'longitude' => 69.1833333),
+ 'Kampala' => array('latitude' => 0.3155556, 'longitude' => 32.5655556),
+ 'Kathmandu' => array('latitude' => 27.7166667, 'longitude' => 85.3166667),
+ 'Khartoum' => array('latitude' => 15.5880556, 'longitude' => 32.5341667),
+ 'Kigali' => array('latitude' => -1.9536111, 'longitude' => 30.0605556),
+ 'Kingston' => array('latitude' => -29.05, 'longitude' => 167.95),
+ 'Kingstown' => array('latitude' => 13.1333333, 'longitude' => -61.2166667),
+ 'Kinshasa' => array('latitude' => -4.3, 'longitude' => 15.3),
+ 'Kolkata' => array('latitude' => 22.5697222, 'longitude' => 88.3697222),
+ 'Kuala Lumpur' => array('latitude' => 3.1666667, 'longitude' => 101.7),
+ 'Kuwait City' => array('latitude' => 29.3697222, 'longitude' => 47.9783333),
+ 'Kiev' => array('latitude' => 50.4333333, 'longitude' => 30.5166667),
+ 'La Paz' => array('latitude' => -16.5, 'longitude' => -68.15),
+ 'Libreville' => array('latitude' => 0.3833333, 'longitude' => 9.45),
+ 'Lilongwe' => array('latitude' => -13.9833333, 'longitude' => 33.7833333),
+ 'Lima' => array('latitude' => -12.05, 'longitude' => -77.05),
+ 'Lisbon' => array('latitude' => 38.7166667, 'longitude' => -9.1333333),
+ 'Ljubljana' => array('latitude' => 46.0552778, 'longitude' => 14.5144444),
+ 'Lobamba' => array('latitude' => -26.4666667, 'longitude' => 31.2),
+ 'Lomé' => array('latitude' => 9.7166667, 'longitude' => 38.3),
+ 'London' => array('latitude' => 51.5, 'longitude' => -0.1166667),
+ 'Los Angeles' => array('latitude' => 34.05222, 'longitude' => -118.24278),
+ 'Luanda' => array('latitude' => -8.8383333, 'longitude' => 13.2344444),
+ 'Lusaka' => array('latitude' => -15.4166667, 'longitude' => 28.2833333),
+ 'Luxembourg' => array('latitude' => 49.6116667, 'longitude' => 6.13),
+ 'Madrid' => array('latitude' => 40.4, 'longitude' => -3.6833333),
+ 'Majuro' => array('latitude' => 7.1, 'longitude' => 171.3833333),
+ 'Malabo' => array('latitude' => 3.75, 'longitude' => 8.7833333),
+ 'Managua' => array('latitude' => 12.1508333, 'longitude' => -86.2683333),
+ 'Manama' => array('latitude' => 26.2361111, 'longitude' => 50.5830556),
+ 'Manila' => array('latitude' => 14.6041667, 'longitude' => 120.9822222),
+ 'Maputo' => array('latitude' => -25.9652778, 'longitude' => 32.5891667),
+ 'Maseru' => array('latitude' => -29.3166667, 'longitude' => 27.4833333),
+ 'Mbabane' => array('latitude' => -26.3166667, 'longitude' => 31.1333333),
+ 'Melbourne' => array('latitude' => -37.8166667, 'longitude' => 144.9666667),
+ 'Melekeok' => array('latitude' => 7.4933333, 'longitude' => 134.6341667),
+ 'Mexiko City' => array('latitude' => 19.4341667, 'longitude' => -99.1386111),
+ 'Minsk' => array('latitude' => 53.9, 'longitude' => 27.5666667),
+ 'Mogadishu' => array('latitude' => 2.0666667, 'longitude' => 45.3666667),
+ 'Monaco' => array('latitude' => 43.7333333, 'longitude' => 7.4166667),
+ 'Monrovia' => array('latitude' => 6.3105556, 'longitude' => -10.8047222),
+ 'Montevideo' => array('latitude' => -34.8580556, 'longitude' => -56.1708333),
+ 'Montreal' => array('latitude' => 45.5, 'longitude' => -73.5833333),
+ 'Moroni' => array('latitude' => -11.7041667, 'longitude' => 43.2402778),
+ 'Moscow' => array('latitude' => 55.7522222, 'longitude' => 37.6155556),
+ 'Muscat' => array('latitude' => 23.6133333, 'longitude' => 58.5933333),
+ 'Nairobi' => array('latitude' => -1.3166667, 'longitude' => 36.8333333),
+ 'Nassau' => array('latitude' => 25.0833333, 'longitude' => -77.35),
+ 'N´Djamena' => array('latitude' => 12.1130556, 'longitude' => 15.0491667),
+ 'New Dehli' => array('latitude' => 28.6, 'longitude' => 77.2),
+ 'New York' => array('latitude' => 40.71417, 'longitude' => -74.00639),
+ 'Newcastle' => array('latitude' => -32.9166667, 'longitude' => 151.75),
+ 'Niamey' => array('latitude' => 13.6666667, 'longitude' => 1.7833333),
+ 'Nicosia' => array('latitude' => 35.1666667, 'longitude' => 33.3666667),
+ 'Nouakchott' => array('latitude' => 18.0863889, 'longitude' => -15.9752778),
+ 'Noumea' => array('latitude' => -22.2666667, 'longitude' => 166.45),
+ 'Nuku´alofa' => array('latitude' => -21.1333333, 'longitude' => -175.2),
+ 'Nuuk' => array('latitude' => 64.1833333, 'longitude' => -51.75),
+ 'Oranjestad' => array('latitude' => 12.5166667, 'longitude' => -70.0333333),
+ 'Oslo' => array('latitude' => 59.9166667, 'longitude' => 10.75),
+ 'Ouagadougou' => array('latitude' => 12.3702778, 'longitude' => -1.5247222),
+ 'Palikir' => array('latitude' => 6.9166667, 'longitude' => 158.15),
+ 'Panama City' => array('latitude' => 8.9666667, 'longitude' => -79.5333333),
+ 'Papeete' => array('latitude' => -17.5333333, 'longitude' => -149.5666667),
+ 'Paramaribo' => array('latitude' => 5.8333333, 'longitude' => -55.1666667),
+ 'Paris' => array('latitude' => 48.8666667, 'longitude' => 2.3333333),
+ 'Perth' => array('latitude' => -31.9333333, 'longitude' => 115.8333333),
+ 'Phnom Penh' => array('latitude' => 11.55, 'longitude' => 104.9166667),
+ 'Podgorica' => array('latitude' => 43.7752778, 'longitude' => 19.6827778),
+ 'Port Louis' => array('latitude' => -20.1666667, 'longitude' => 57.5),
+ 'Port Moresby' => array('latitude' => -9.4647222, 'longitude' => 147.1925),
+ 'Port-au-Prince' => array('latitude' => 18.5391667, 'longitude' => -72.335),
+ 'Port of Spain' => array('latitude' => 10.6666667, 'longitude' => -61.5),
+ 'Porto-Novo' => array('latitude' => 6.4833333, 'longitude' => 2.6166667),
+ 'Prague' => array('latitude' => 50.0833333, 'longitude' => 14.4666667),
+ 'Praia' => array('latitude' => 14.9166667, 'longitude' => -23.5166667),
+ 'Pretoria' => array('latitude' => -25.7069444, 'longitude' => 28.2294444),
+ 'Pyongyang' => array('latitude' => 39.0194444, 'longitude' => 125.7547222),
+ 'Quito' => array('latitude' => -0.2166667, 'longitude' => -78.5),
+ 'Rabat' => array('latitude' => 34.0252778, 'longitude' => -6.8361111),
+ 'Reykjavik' => array('latitude' => 64.15, 'longitude' => -21.95),
+ 'Riga' => array('latitude' => 56.95, 'longitude' => 24.1),
+ 'Rio de Janero' => array('latitude' => -22.9, 'longitude' => -43.2333333),
+ 'Road Town' => array('latitude' => 18.4166667, 'longitude' => -64.6166667),
+ 'Rome' => array('latitude' => 41.9, 'longitude' => 12.4833333),
+ 'Roseau' => array('latitude' => 15.3, 'longitude' => -61.4),
+ 'Rotterdam' => array('latitude' => 51.9166667, 'longitude' => 4.5),
+ 'Salvador' => array('latitude' => -12.9833333, 'longitude' => -38.5166667),
+ 'San José' => array('latitude' => 9.9333333, 'longitude' => -84.0833333),
+ 'San Juan' => array('latitude' => 18.46833, 'longitude' => -66.10611),
+ 'San Marino' => array('latitude' => 43.5333333, 'longitude' => 12.9666667),
+ 'San Salvador' => array('latitude' => 13.7086111, 'longitude' => -89.2030556),
+ 'Sanaá' => array('latitude' => 15.3547222, 'longitude' => 44.2066667),
+ 'Santa Cruz' => array('latitude' => -17.8, 'longitude' => -63.1666667),
+ 'Santiago' => array('latitude' => -33.45, 'longitude' => -70.6666667),
+ 'Santo Domingo' => array('latitude' => 18.4666667, 'longitude' => -69.9),
+ 'Sao Paulo' => array('latitude' => -23.5333333, 'longitude' => -46.6166667),
+ 'Sarajevo' => array('latitude' => 43.85, 'longitude' => 18.3833333),
+ 'Seoul' => array('latitude' => 37.5663889, 'longitude' => 126.9997222),
+ 'Shanghai' => array('latitude' => 31.2222222, 'longitude' => 121.4580556),
+ 'Sydney' => array('latitude' => -33.8833333, 'longitude' => 151.2166667),
+ 'Singapore' => array('latitude' => 1.2930556, 'longitude' => 103.8558333),
+ 'Skopje' => array('latitude' => 42, 'longitude' => 21.4333333),
+ 'Sofia' => array('latitude' => 42.6833333, 'longitude' => 23.3166667),
+ 'St. George´s' => array('latitude' => 12.05, 'longitude' => -61.75),
+ 'St. John´s' => array('latitude' => 17.1166667, 'longitude' => -61.85),
+ 'Stanley' => array('latitude' => -51.7, 'longitude' => -57.85),
+ 'Stockholm' => array('latitude' => 59.3333333, 'longitude' => 18.05),
+ 'Suva' => array('latitude' => -18.1333333, 'longitude' => 178.4166667),
+ 'Taipei' => array('latitude' => 25.0166667, 'longitude' => 121.45),
+ 'Tallinn' => array('latitude' => 59.4338889, 'longitude' => 24.7280556),
+ 'Tashkent' => array('latitude' => 41.3166667, 'longitude' => 69.25),
+ 'Tbilisi' => array('latitude' => 41.725, 'longitude' => 44.7908333),
+ 'Tegucigalpa' => array('latitude' => 14.1, 'longitude' => -87.2166667),
+ 'Tehran' => array('latitude' => 35.6719444, 'longitude' => 51.4244444),
+ 'The Hague' => array('latitude' => 52.0833333, 'longitude' => 4.3),
+ 'Thimphu' => array('latitude' => 27.4833333, 'longitude' => 89.6),
+ 'Tirana' => array('latitude' => 41.3275, 'longitude' => 19.8188889),
+ 'Tiraspol' => array('latitude' => 46.8402778, 'longitude' => 29.6433333),
+ 'Tokyo' => array('latitude' => 35.685, 'longitude' => 139.7513889),
+ 'Toronto' => array('latitude' => 43.6666667, 'longitude' => -79.4166667),
+ 'Tórshavn' => array('latitude' => 62.0166667, 'longitude' => -6.7666667),
+ 'Tripoli' => array('latitude' => 32.8925, 'longitude' => 13.18),
+ 'Tunis' => array('latitude' => 36.8027778, 'longitude' => 10.1797222),
+ 'Ulaanbaatar' => array('latitude' => 47.9166667, 'longitude' => 106.9166667),
+ 'Vaduz' => array('latitude' => 47.1333333, 'longitude' => 9.5166667),
+ 'Valletta' => array('latitude' => 35.8997222, 'longitude' => 14.5147222),
+ 'Valparaiso' => array('latitude' => -33.0477778, 'longitude' => -71.6011111),
+ 'Vancouver' => array('latitude' => 49.25, 'longitude' => -123.1333333),
+ 'Vatican City' => array('latitude' => 41.9, 'longitude' => 12.4833333),
+ 'Victoria' => array('latitude' => -4.6166667, 'longitude' => 55.45),
+ 'Vienna' => array('latitude' => 48.2, 'longitude' => 16.3666667),
+ 'Vientaine' => array('latitude' => 17.9666667, 'longitude' => 102.6),
+ 'Vilnius' => array('latitude' => 54.6833333, 'longitude' => 25.3166667),
+ 'Warsaw' => array('latitude' => 52.25, 'longitude' => 21),
+ 'Washington dc' => array('latitude' => 38.895, 'longitude' => -77.03667),
+ 'Wellington' => array('latitude' => -41.3, 'longitude' => 174.7833333),
+ 'Willemstad' => array('latitude' => 12.1, 'longitude' => -68.9166667),
+ 'Windhoek' => array('latitude' => -22.57, 'longitude' => 17.0836111),
+ 'Yamoussoukro' => array('latitude' => 6.8166667, 'longitude' => -5.2833333),
+ 'Yaoundé' => array('latitude' => 3.8666667, 'longitude' => 11.5166667),
+ 'Yerevan' => array('latitude' => 40.1811111, 'longitude' => 44.5136111),
+ 'Zürich' => array('latitude' => 47.3666667, 'longitude' => 8.55),
+ 'Zagreb' => array('latitude' => 45.8, 'longitude' => 16)
+ );
+
+ /**
+ * Returns the location from the selected city
+ *
+ * @param string $city City to get location for
+ * @param string $horizon Horizon to use :
+ * default: effective
+ * others are civil, nautic, astronomic
+ * @return array
+ * @throws Zend_Date_Exception When city is unknown
+ */
+ public static function City($city, $horizon = false)
+ {
+ foreach (self::$cities as $key => $value) {
+ if (strtolower($key) === strtolower($city)) {
+ $return = $value;
+ $return['horizon'] = $horizon;
+ return $return;
+ }
+ }
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('unknown city');
+ }
+
+ /**
+ * Return a list with all known cities
+ *
+ * @return array
+ */
+ public static function getCityList()
+ {
+ return array_keys(self::$cities);
+ }
+}
diff --git a/library/Zend/Date/DateObject.php b/library/Zend/Date/DateObject.php
new file mode 100644
index 0000000..2fc9033
--- /dev/null
+++ b/library/Zend/Date/DateObject.php
@@ -0,0 +1,1089 @@
+ 0, 1960 => -315619200, 1950 => -631152000,
+ 1940 => -946771200, 1930 => -1262304000, 1920 => -1577923200,
+ 1910 => -1893456000, 1900 => -2208988800, 1890 => -2524521600,
+ 1880 => -2840140800, 1870 => -3155673600, 1860 => -3471292800,
+ 1850 => -3786825600, 1840 => -4102444800, 1830 => -4417977600,
+ 1820 => -4733596800, 1810 => -5049129600, 1800 => -5364662400,
+ 1790 => -5680195200, 1780 => -5995814400, 1770 => -6311347200,
+ 1760 => -6626966400, 1750 => -6942499200, 1740 => -7258118400,
+ 1730 => -7573651200, 1720 => -7889270400, 1710 => -8204803200,
+ 1700 => -8520336000, 1690 => -8835868800, 1680 => -9151488000,
+ 1670 => -9467020800, 1660 => -9782640000, 1650 => -10098172800,
+ 1640 => -10413792000, 1630 => -10729324800, 1620 => -11044944000,
+ 1610 => -11360476800, 1600 => -11676096000);
+
+ /**
+ * Set this object to have a new UNIX timestamp.
+ *
+ * @param string|integer $timestamp OPTIONAL timestamp; defaults to local time using time()
+ * @return string|integer old timestamp
+ * @throws Zend_Date_Exception
+ */
+ protected function setUnixTimestamp($timestamp = null)
+ {
+ $old = $this->_unixTimestamp;
+
+ if (is_numeric($timestamp)) {
+ $this->_unixTimestamp = $timestamp;
+ } else if ($timestamp === null) {
+ $this->_unixTimestamp = time();
+ } else {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception('\'' . $timestamp . '\' is not a valid UNIX timestamp', 0, null, $timestamp);
+ }
+
+ return $old;
+ }
+
+ /**
+ * Returns this object's UNIX timestamp
+ * A timestamp greater then the integer range will be returned as string
+ * This function does not return the timestamp as object. Use copy() instead.
+ *
+ * @return integer|string timestamp
+ */
+ protected function getUnixTimestamp()
+ {
+ if ($this->_unixTimestamp === intval($this->_unixTimestamp)) {
+ return (int) $this->_unixTimestamp;
+ } else {
+ return (string) $this->_unixTimestamp;
+ }
+ }
+
+ /**
+ * Internal function.
+ * Returns time(). This method exists to allow unit tests to work-around methods that might otherwise
+ * be hard-coded to use time(). For example, this makes it possible to test isYesterday() in Date.php.
+ *
+ * @param integer $sync OPTIONAL time syncronisation value
+ * @return integer timestamp
+ */
+ protected function _getTime($sync = null)
+ {
+ if ($sync !== null) {
+ $this->_syncronised = round($sync);
+ }
+ return (time() + $this->_syncronised);
+ }
+
+ /**
+ * Internal mktime function used by Zend_Date.
+ * The timestamp returned by mktime() can exceed the precision of traditional UNIX timestamps,
+ * by allowing PHP to auto-convert to using a float value.
+ *
+ * Returns a timestamp relative to 1970/01/01 00:00:00 GMT/UTC.
+ * DST (Summer/Winter) is depriciated since php 5.1.0.
+ * Year has to be 4 digits otherwise it would be recognised as
+ * year 70 AD instead of 1970 AD as expected !!
+ *
+ * @param integer $hour
+ * @param integer $minute
+ * @param integer $second
+ * @param integer $month
+ * @param integer $day
+ * @param integer $year
+ * @param boolean $gmt OPTIONAL true = other arguments are for UTC time, false = arguments are for local time/date
+ * @return integer|float timestamp (number of seconds elapsed relative to 1970/01/01 00:00:00 GMT/UTC)
+ */
+ protected function mktime($hour, $minute, $second, $month, $day, $year, $gmt = false)
+ {
+ // complete date but in 32bit timestamp - use PHP internal
+ if ((1901 < $year) and ($year < 2038)) {
+
+ $oldzone = @date_default_timezone_get();
+ // Timezone also includes DST settings, therefor substracting the GMT offset is not enough
+ // We have to set the correct timezone to get the right value
+ if (($this->_timezone != $oldzone) and ($gmt === false)) {
+ date_default_timezone_set($this->_timezone);
+ }
+ $result = ($gmt) ? @gmmktime($hour, $minute, $second, $month, $day, $year)
+ : @mktime($hour, $minute, $second, $month, $day, $year);
+ date_default_timezone_set($oldzone);
+
+ return $result;
+ }
+
+ if ($gmt !== true) {
+ $second += $this->_offset;
+ }
+
+ if (isset(self::$_cache)) {
+ $id = strtr('Zend_DateObject_mkTime_' . $this->_offset . '_' . $year.$month.$day.'_'.$hour.$minute.$second . '_'.(int)$gmt, '-','_');
+ if ($result = self::$_cache->load($id)) {
+ return unserialize($result);
+ }
+ }
+
+ // date to integer
+ $day = intval($day);
+ $month = intval($month);
+ $year = intval($year);
+
+ // correct months > 12 and months < 1
+ if ($month > 12) {
+ $overlap = floor($month / 12);
+ $year += $overlap;
+ $month -= $overlap * 12;
+ } else {
+ $overlap = ceil((1 - $month) / 12);
+ $year -= $overlap;
+ $month += $overlap * 12;
+ }
+
+ $date = 0;
+ if ($year >= 1970) {
+
+ // Date is after UNIX epoch
+ // go through leapyears
+ // add months from latest given year
+ for ($count = 1970; $count <= $year; $count++) {
+
+ $leapyear = self::isYearLeapYear($count);
+ if ($count < $year) {
+
+ $date += 365;
+ if ($leapyear === true) {
+ $date++;
+ }
+
+ } else {
+
+ for ($mcount = 0; $mcount < ($month - 1); $mcount++) {
+ $date += self::$_monthTable[$mcount];
+ if (($leapyear === true) and ($mcount == 1)) {
+ $date++;
+ }
+
+ }
+ }
+ }
+
+ $date += $day - 1;
+ $date = (($date * 86400) + ($hour * 3600) + ($minute * 60) + $second);
+ } else {
+
+ // Date is before UNIX epoch
+ // go through leapyears
+ // add months from latest given year
+ for ($count = 1969; $count >= $year; $count--) {
+
+ $leapyear = self::isYearLeapYear($count);
+ if ($count > $year)
+ {
+ $date += 365;
+ if ($leapyear === true)
+ $date++;
+ } else {
+
+ for ($mcount = 11; $mcount > ($month - 1); $mcount--) {
+ $date += self::$_monthTable[$mcount];
+ if (($leapyear === true) and ($mcount == 2)) {
+ $date++;
+ }
+
+ }
+ }
+ }
+
+ $date += (self::$_monthTable[$month - 1] - $day);
+ $date = -(($date * 86400) + (86400 - (($hour * 3600) + ($minute * 60) + $second)));
+
+ // gregorian correction for 5.Oct.1582
+ if ($date < -12220185600) {
+ $date += 864000;
+ } else if ($date < -12219321600) {
+ $date = -12219321600;
+ }
+ }
+
+ if (isset(self::$_cache)) {
+ if (self::$_cacheTags) {
+ self::$_cache->save( serialize($date), $id, array('Zend_Date'));
+ } else {
+ self::$_cache->save( serialize($date), $id);
+ }
+ }
+
+ return $date;
+ }
+
+ /**
+ * Returns true, if given $year is a leap year.
+ *
+ * @param integer $year
+ * @return boolean true, if year is leap year
+ */
+ protected static function isYearLeapYear($year)
+ {
+ // all leapyears can be divided through 4
+ if (($year % 4) != 0) {
+ return false;
+ }
+
+ // all leapyears can be divided through 400
+ if ($year % 400 == 0) {
+ return true;
+ } else if (($year > 1582) and ($year % 100 == 0)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Internal mktime function used by Zend_Date for handling 64bit timestamps.
+ *
+ * Returns a formatted date for a given timestamp.
+ *
+ * @param string $format format for output
+ * @param mixed $timestamp
+ * @param boolean $gmt OPTIONAL true = other arguments are for UTC time, false = arguments are for local time/date
+ * @return string
+ */
+ protected function date($format, $timestamp = null, $gmt = false)
+ {
+ $oldzone = @date_default_timezone_get();
+ if ($this->_timezone != $oldzone) {
+ date_default_timezone_set($this->_timezone);
+ }
+
+ if ($timestamp === null) {
+ $result = ($gmt) ? @gmdate($format) : @date($format);
+ date_default_timezone_set($oldzone);
+ return $result;
+ }
+
+ if (abs($timestamp) <= 0x7FFFFFFF) {
+ $result = ($gmt) ? @gmdate($format, $timestamp) : @date($format, $timestamp);
+ date_default_timezone_set($oldzone);
+ return $result;
+ }
+
+ $jump = false;
+ $origstamp = $timestamp;
+ if (isset(self::$_cache)) {
+ $idstamp = strtr('Zend_DateObject_date_' . $this->_offset . '_'. $timestamp . '_'.(int)$gmt, '-','_');
+ if ($result2 = self::$_cache->load($idstamp)) {
+ $timestamp = unserialize($result2);
+ $jump = true;
+ }
+ }
+
+ // check on false or null alone fails
+ if (empty($gmt) and empty($jump)) {
+ $tempstamp = $timestamp;
+ if ($tempstamp > 0) {
+ while (abs($tempstamp) > 0x7FFFFFFF) {
+ $tempstamp -= (86400 * 23376);
+ }
+
+ $dst = date("I", $tempstamp);
+ if ($dst === 1) {
+ $timestamp += 3600;
+ }
+
+ $temp = date('Z', $tempstamp);
+ $timestamp += $temp;
+ }
+
+ if (isset(self::$_cache)) {
+ if (self::$_cacheTags) {
+ self::$_cache->save( serialize($timestamp), $idstamp, array('Zend_Date'));
+ } else {
+ self::$_cache->save( serialize($timestamp), $idstamp);
+ }
+ }
+ }
+
+ if (($timestamp < 0) and ($gmt !== true)) {
+ $timestamp -= $this->_offset;
+ }
+
+ date_default_timezone_set($oldzone);
+ $date = $this->getDateParts($timestamp, true);
+ $length = strlen($format);
+ $output = '';
+
+ for ($i = 0; $i < $length; $i++) {
+ switch($format[$i]) {
+ // day formats
+ case 'd': // day of month, 2 digits, with leading zero, 01 - 31
+ $output .= (($date['mday'] < 10) ? '0' . $date['mday'] : $date['mday']);
+ break;
+
+ case 'D': // day of week, 3 letters, Mon - Sun
+ $output .= date('D', 86400 * (3 + self::dayOfWeek($date['year'], $date['mon'], $date['mday'])));
+ break;
+
+ case 'j': // day of month, without leading zero, 1 - 31
+ $output .= $date['mday'];
+ break;
+
+ case 'l': // day of week, full string name, Sunday - Saturday
+ $output .= date('l', 86400 * (3 + self::dayOfWeek($date['year'], $date['mon'], $date['mday'])));
+ break;
+
+ case 'N': // ISO 8601 numeric day of week, 1 - 7
+ $day = self::dayOfWeek($date['year'], $date['mon'], $date['mday']);
+ if ($day == 0) {
+ $day = 7;
+ }
+ $output .= $day;
+ break;
+
+ case 'S': // english suffix for day of month, st nd rd th
+ if (($date['mday'] % 10) == 1) {
+ $output .= 'st';
+ } else if ((($date['mday'] % 10) == 2) and ($date['mday'] != 12)) {
+ $output .= 'nd';
+ } else if (($date['mday'] % 10) == 3) {
+ $output .= 'rd';
+ } else {
+ $output .= 'th';
+ }
+ break;
+
+ case 'w': // numeric day of week, 0 - 6
+ $output .= self::dayOfWeek($date['year'], $date['mon'], $date['mday']);
+ break;
+
+ case 'z': // day of year, 0 - 365
+ $output .= $date['yday'];
+ break;
+
+
+ // week formats
+ case 'W': // ISO 8601, week number of year
+ $output .= $this->weekNumber($date['year'], $date['mon'], $date['mday']);
+ break;
+
+
+ // month formats
+ case 'F': // string month name, january - december
+ $output .= date('F', mktime(0, 0, 0, $date['mon'], 2, 1971));
+ break;
+
+ case 'm': // number of month, with leading zeros, 01 - 12
+ $output .= (($date['mon'] < 10) ? '0' . $date['mon'] : $date['mon']);
+ break;
+
+ case 'M': // 3 letter month name, Jan - Dec
+ $output .= date('M',mktime(0, 0, 0, $date['mon'], 2, 1971));
+ break;
+
+ case 'n': // number of month, without leading zeros, 1 - 12
+ $output .= $date['mon'];
+ break;
+
+ case 't': // number of day in month
+ $output .= self::$_monthTable[$date['mon'] - 1];
+ break;
+
+
+ // year formats
+ case 'L': // is leap year ?
+ $output .= (self::isYearLeapYear($date['year'])) ? '1' : '0';
+ break;
+
+ case 'o': // ISO 8601 year number
+ $week = $this->weekNumber($date['year'], $date['mon'], $date['mday']);
+ if (($week > 50) and ($date['mon'] == 1)) {
+ $output .= ($date['year'] - 1);
+ } else {
+ $output .= $date['year'];
+ }
+ break;
+
+ case 'Y': // year number, 4 digits
+ $output .= $date['year'];
+ break;
+
+ case 'y': // year number, 2 digits
+ $output .= substr($date['year'], strlen($date['year']) - 2, 2);
+ break;
+
+
+ // time formats
+ case 'a': // lower case am/pm
+ $output .= (($date['hours'] >= 12) ? 'pm' : 'am');
+ break;
+
+ case 'A': // upper case am/pm
+ $output .= (($date['hours'] >= 12) ? 'PM' : 'AM');
+ break;
+
+ case 'B': // swatch internet time
+ $dayseconds = ($date['hours'] * 3600) + ($date['minutes'] * 60) + $date['seconds'];
+ if ($gmt === true) {
+ $dayseconds += 3600;
+ }
+ $output .= (int) (($dayseconds % 86400) / 86.4);
+ break;
+
+ case 'g': // hours without leading zeros, 12h format
+ if ($date['hours'] > 12) {
+ $hour = $date['hours'] - 12;
+ } else {
+ if ($date['hours'] == 0) {
+ $hour = '12';
+ } else {
+ $hour = $date['hours'];
+ }
+ }
+ $output .= $hour;
+ break;
+
+ case 'G': // hours without leading zeros, 24h format
+ $output .= $date['hours'];
+ break;
+
+ case 'h': // hours with leading zeros, 12h format
+ if ($date['hours'] > 12) {
+ $hour = $date['hours'] - 12;
+ } else {
+ if ($date['hours'] == 0) {
+ $hour = '12';
+ } else {
+ $hour = $date['hours'];
+ }
+ }
+ $output .= (($hour < 10) ? '0'.$hour : $hour);
+ break;
+
+ case 'H': // hours with leading zeros, 24h format
+ $output .= (($date['hours'] < 10) ? '0' . $date['hours'] : $date['hours']);
+ break;
+
+ case 'i': // minutes with leading zeros
+ $output .= (($date['minutes'] < 10) ? '0' . $date['minutes'] : $date['minutes']);
+ break;
+
+ case 's': // seconds with leading zeros
+ $output .= (($date['seconds'] < 10) ? '0' . $date['seconds'] : $date['seconds']);
+ break;
+
+
+ // timezone formats
+ case 'e': // timezone identifier
+ if ($gmt === true) {
+ $output .= gmdate('e', mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], 2000));
+ } else {
+ $output .= date('e', mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], 2000));
+ }
+ break;
+
+ case 'I': // daylight saving time or not
+ if ($gmt === true) {
+ $output .= gmdate('I', mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], 2000));
+ } else {
+ $output .= date('I', mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], 2000));
+ }
+ break;
+
+ case 'O': // difference to GMT in hours
+ $gmtstr = ($gmt === true) ? 0 : $this->getGmtOffset();
+ $output .= sprintf('%s%04d', ($gmtstr <= 0) ? '+' : '-', abs($gmtstr) / 36);
+ break;
+
+ case 'P': // difference to GMT with colon
+ $gmtstr = ($gmt === true) ? 0 : $this->getGmtOffset();
+ $gmtstr = sprintf('%s%04d', ($gmtstr <= 0) ? '+' : '-', abs($gmtstr) / 36);
+ $output = $output . substr($gmtstr, 0, 3) . ':' . substr($gmtstr, 3);
+ break;
+
+ case 'T': // timezone settings
+ if ($gmt === true) {
+ $output .= gmdate('T', mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], 2000));
+ } else {
+ $output .= date('T', mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], 2000));
+ }
+ break;
+
+ case 'Z': // timezone offset in seconds
+ $output .= ($gmt === true) ? 0 : -$this->getGmtOffset();
+ break;
+
+
+ // complete time formats
+ case 'c': // ISO 8601 date format
+ $difference = $this->getGmtOffset();
+ $difference = sprintf('%s%04d', ($difference <= 0) ? '+' : '-', abs($difference) / 36);
+ $difference = substr($difference, 0, 3) . ':' . substr($difference, 3);
+ $output .= $date['year'] . '-'
+ . (($date['mon'] < 10) ? '0' . $date['mon'] : $date['mon']) . '-'
+ . (($date['mday'] < 10) ? '0' . $date['mday'] : $date['mday']) . 'T'
+ . (($date['hours'] < 10) ? '0' . $date['hours'] : $date['hours']) . ':'
+ . (($date['minutes'] < 10) ? '0' . $date['minutes'] : $date['minutes']) . ':'
+ . (($date['seconds'] < 10) ? '0' . $date['seconds'] : $date['seconds'])
+ . $difference;
+ break;
+
+ case 'r': // RFC 2822 date format
+ $difference = $this->getGmtOffset();
+ $difference = sprintf('%s%04d', ($difference <= 0) ? '+' : '-', abs($difference) / 36);
+ $output .= gmdate('D', 86400 * (3 + self::dayOfWeek($date['year'], $date['mon'], $date['mday']))) . ', '
+ . (($date['mday'] < 10) ? '0' . $date['mday'] : $date['mday']) . ' '
+ . date('M', mktime(0, 0, 0, $date['mon'], 2, 1971)) . ' '
+ . $date['year'] . ' '
+ . (($date['hours'] < 10) ? '0' . $date['hours'] : $date['hours']) . ':'
+ . (($date['minutes'] < 10) ? '0' . $date['minutes'] : $date['minutes']) . ':'
+ . (($date['seconds'] < 10) ? '0' . $date['seconds'] : $date['seconds']) . ' '
+ . $difference;
+ break;
+
+ case 'U': // Unix timestamp
+ $output .= $origstamp;
+ break;
+
+
+ // special formats
+ case "\\": // next letter to print with no format
+ $i++;
+ if ($i < $length) {
+ $output .= $format[$i];
+ }
+ break;
+
+ default: // letter is no format so add it direct
+ $output .= $format[$i];
+ break;
+ }
+ }
+
+ return (string) $output;
+ }
+
+ /**
+ * Returns the day of week for a Gregorian calendar date.
+ * 0 = sunday, 6 = saturday
+ *
+ * @param integer $year
+ * @param integer $month
+ * @param integer $day
+ * @return integer dayOfWeek
+ */
+ protected static function dayOfWeek($year, $month, $day)
+ {
+ if ((1901 < $year) and ($year < 2038)) {
+ return (int) date('w', mktime(0, 0, 0, $month, $day, $year));
+ }
+
+ // gregorian correction
+ $correction = 0;
+ if (($year < 1582) or (($year == 1582) and (($month < 10) or (($month == 10) && ($day < 15))))) {
+ $correction = 3;
+ }
+
+ if ($month > 2) {
+ $month -= 2;
+ } else {
+ $month += 10;
+ $year--;
+ }
+
+ $day = floor((13 * $month - 1) / 5) + $day + ($year % 100) + floor(($year % 100) / 4);
+ $day += floor(($year / 100) / 4) - 2 * floor($year / 100) + 77 + $correction;
+
+ return (int) ($day - 7 * floor($day / 7));
+ }
+
+ /**
+ * Internal getDateParts function for handling 64bit timestamps, similar to:
+ * http://www.php.net/getdate
+ *
+ * Returns an array of date parts for $timestamp, relative to 1970/01/01 00:00:00 GMT/UTC.
+ *
+ * $fast specifies ALL date parts should be returned (slower)
+ * Default is false, and excludes $dayofweek, weekday, month and timestamp from parts returned.
+ *
+ * @param mixed $timestamp
+ * @param boolean $fast OPTIONAL defaults to fast (false), resulting in fewer date parts
+ * @return array
+ */
+ protected function getDateParts($timestamp = null, $fast = null)
+ {
+
+ // actual timestamp
+ if (!is_numeric($timestamp)) {
+ return getdate();
+ }
+
+ // 32bit timestamp
+ if (abs($timestamp) <= 0x7FFFFFFF) {
+ return @getdate((int) $timestamp);
+ }
+
+ if (isset(self::$_cache)) {
+ $id = strtr('Zend_DateObject_getDateParts_' . $timestamp.'_'.(int)$fast, '-','_');
+ if ($result = self::$_cache->load($id)) {
+ return unserialize($result);
+ }
+ }
+
+ $otimestamp = $timestamp;
+ $numday = 0;
+ $month = 0;
+ // gregorian correction
+ if ($timestamp < -12219321600) {
+ $timestamp -= 864000;
+ }
+
+ // timestamp lower 0
+ if ($timestamp < 0) {
+ $sec = 0;
+ $act = 1970;
+
+ // iterate through 10 years table, increasing speed
+ foreach(self::$_yearTable as $year => $seconds) {
+ if ($timestamp >= $seconds) {
+ $i = $act;
+ break;
+ }
+ $sec = $seconds;
+ $act = $year;
+ }
+
+ $timestamp -= $sec;
+ if (!isset($i)) {
+ $i = $act;
+ }
+
+ // iterate the max last 10 years
+ do {
+ --$i;
+ $day = $timestamp;
+
+ $timestamp += 31536000;
+ $leapyear = self::isYearLeapYear($i);
+ if ($leapyear === true) {
+ $timestamp += 86400;
+ }
+
+ if ($timestamp >= 0) {
+ $year = $i;
+ break;
+ }
+ } while ($timestamp < 0);
+
+ $secondsPerYear = 86400 * ($leapyear ? 366 : 365) + $day;
+
+ $timestamp = $day;
+ // iterate through months
+ for ($i = 12; --$i >= 0;) {
+ $day = $timestamp;
+
+ $timestamp += self::$_monthTable[$i] * 86400;
+ if (($leapyear === true) and ($i == 1)) {
+ $timestamp += 86400;
+ }
+
+ if ($timestamp >= 0) {
+ $month = $i;
+ $numday = self::$_monthTable[$i];
+ if (($leapyear === true) and ($i == 1)) {
+ ++$numday;
+ }
+ break;
+ }
+ }
+
+ $timestamp = $day;
+ $numberdays = $numday + ceil(($timestamp + 1) / 86400);
+
+ $timestamp += ($numday - $numberdays + 1) * 86400;
+ $hours = floor($timestamp / 3600);
+ } else {
+
+ // iterate through years
+ for ($i = 1970;;$i++) {
+ $day = $timestamp;
+
+ $timestamp -= 31536000;
+ $leapyear = self::isYearLeapYear($i);
+ if ($leapyear === true) {
+ $timestamp -= 86400;
+ }
+
+ if ($timestamp < 0) {
+ $year = $i;
+ break;
+ }
+ }
+
+ $secondsPerYear = $day;
+
+ $timestamp = $day;
+ // iterate through months
+ for ($i = 0; $i <= 11; $i++) {
+ $day = $timestamp;
+ $timestamp -= self::$_monthTable[$i] * 86400;
+
+ if (($leapyear === true) and ($i == 1)) {
+ $timestamp -= 86400;
+ }
+
+ if ($timestamp < 0) {
+ $month = $i;
+ $numday = self::$_monthTable[$i];
+ if (($leapyear === true) and ($i == 1)) {
+ ++$numday;
+ }
+ break;
+ }
+ }
+
+ $timestamp = $day;
+ $numberdays = ceil(($timestamp + 1) / 86400);
+ $timestamp = $timestamp - ($numberdays - 1) * 86400;
+ $hours = floor($timestamp / 3600);
+ }
+
+ $timestamp -= $hours * 3600;
+
+ $month += 1;
+ $minutes = floor($timestamp / 60);
+ $seconds = $timestamp - $minutes * 60;
+
+ if ($fast === true) {
+ $array = array(
+ 'seconds' => $seconds,
+ 'minutes' => $minutes,
+ 'hours' => $hours,
+ 'mday' => $numberdays,
+ 'mon' => $month,
+ 'year' => $year,
+ 'yday' => floor($secondsPerYear / 86400),
+ );
+ } else {
+
+ $dayofweek = self::dayOfWeek($year, $month, $numberdays);
+ $array = array(
+ 'seconds' => $seconds,
+ 'minutes' => $minutes,
+ 'hours' => $hours,
+ 'mday' => $numberdays,
+ 'wday' => $dayofweek,
+ 'mon' => $month,
+ 'year' => $year,
+ 'yday' => floor($secondsPerYear / 86400),
+ 'weekday' => gmdate('l', 86400 * (3 + $dayofweek)),
+ 'month' => gmdate('F', mktime(0, 0, 0, $month, 1, 1971)),
+ 0 => $otimestamp
+ );
+ }
+
+ if (isset(self::$_cache)) {
+ if (self::$_cacheTags) {
+ self::$_cache->save( serialize($array), $id, array('Zend_Date'));
+ } else {
+ self::$_cache->save( serialize($array), $id);
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Internal getWeekNumber function for handling 64bit timestamps
+ *
+ * Returns the ISO 8601 week number of a given date
+ *
+ * @param integer $year
+ * @param integer $month
+ * @param integer $day
+ * @return integer
+ */
+ protected function weekNumber($year, $month, $day)
+ {
+ if ((1901 < $year) and ($year < 2038)) {
+ return (int) date('W', mktime(0, 0, 0, $month, $day, $year));
+ }
+
+ $dayofweek = self::dayOfWeek($year, $month, $day);
+ $firstday = self::dayOfWeek($year, 1, 1);
+ if (($month == 1) and (($firstday < 1) or ($firstday > 4)) and ($day < 4)) {
+ $firstday = self::dayOfWeek($year - 1, 1, 1);
+ $month = 12;
+ $day = 31;
+
+ } else if (($month == 12) and ((self::dayOfWeek($year + 1, 1, 1) < 5) and
+ (self::dayOfWeek($year + 1, 1, 1) > 0))) {
+ return 1;
+ }
+
+ return intval (((self::dayOfWeek($year, 1, 1) < 5) and (self::dayOfWeek($year, 1, 1) > 0)) +
+ 4 * ($month - 1) + (2 * ($month - 1) + ($day - 1) + $firstday - $dayofweek + 6) * 36 / 256);
+ }
+
+ /**
+ * Internal _range function
+ * Sets the value $a to be in the range of [0, $b]
+ *
+ * @param float $a - value to correct
+ * @param float $b - maximum range to set
+ */
+ private function _range($a, $b) {
+ while ($a < 0) {
+ $a += $b;
+ }
+ while ($a >= $b) {
+ $a -= $b;
+ }
+ return $a;
+ }
+
+ /**
+ * Calculates the sunrise or sunset based on a location
+ *
+ * @param array $location Location for calculation MUST include 'latitude', 'longitude', 'horizon'
+ * @param bool $horizon true: sunrise; false: sunset
+ * @return mixed - false: midnight sun, integer:
+ */
+ protected function calcSun($location, $horizon, $rise = false)
+ {
+ // timestamp within 32bit
+ if (abs($this->_unixTimestamp) <= 0x7FFFFFFF) {
+ if ($rise === false) {
+ return date_sunset($this->_unixTimestamp, SUNFUNCS_RET_TIMESTAMP, $location['latitude'],
+ $location['longitude'], 90 + $horizon, $this->getGmtOffset() / 3600);
+ }
+ return date_sunrise($this->_unixTimestamp, SUNFUNCS_RET_TIMESTAMP, $location['latitude'],
+ $location['longitude'], 90 + $horizon, $this->getGmtOffset() / 3600);
+ }
+
+ // self calculation - timestamp bigger than 32bit
+ // fix circle values
+ $quarterCircle = 0.5 * M_PI;
+ $halfCircle = M_PI;
+ $threeQuarterCircle = 1.5 * M_PI;
+ $fullCircle = 2 * M_PI;
+
+ // radiant conversion for coordinates
+ $radLatitude = $location['latitude'] * $halfCircle / 180;
+ $radLongitude = $location['longitude'] * $halfCircle / 180;
+
+ // get solar coordinates
+ $tmpRise = $rise ? $quarterCircle : $threeQuarterCircle;
+ $radDay = $this->date('z',$this->_unixTimestamp) + ($tmpRise - $radLongitude) / $fullCircle;
+
+ // solar anomoly and longitude
+ $solAnomoly = $radDay * 0.017202 - 0.0574039;
+ $solLongitude = $solAnomoly + 0.0334405 * sin($solAnomoly);
+ $solLongitude += 4.93289 + 3.49066E-4 * sin(2 * $solAnomoly);
+
+ // get quadrant
+ $solLongitude = $this->_range($solLongitude, $fullCircle);
+
+ if (($solLongitude / $quarterCircle) - intval($solLongitude / $quarterCircle) == 0) {
+ $solLongitude += 4.84814E-6;
+ }
+
+ // solar ascension
+ $solAscension = sin($solLongitude) / cos($solLongitude);
+ $solAscension = atan2(0.91746 * $solAscension, 1);
+
+ // adjust quadrant
+ if ($solLongitude > $threeQuarterCircle) {
+ $solAscension += $fullCircle;
+ } else if ($solLongitude > $quarterCircle) {
+ $solAscension += $halfCircle;
+ }
+
+ // solar declination
+ $solDeclination = 0.39782 * sin($solLongitude);
+ $solDeclination /= sqrt(-$solDeclination * $solDeclination + 1);
+ $solDeclination = atan2($solDeclination, 1);
+
+ $solHorizon = $horizon - sin($solDeclination) * sin($radLatitude);
+ $solHorizon /= cos($solDeclination) * cos($radLatitude);
+
+ // midnight sun, always night
+ if (abs($solHorizon) > 1) {
+ return false;
+ }
+
+ $solHorizon /= sqrt(-$solHorizon * $solHorizon + 1);
+ $solHorizon = $quarterCircle - atan2($solHorizon, 1);
+
+ if ($rise) {
+ $solHorizon = $fullCircle - $solHorizon;
+ }
+
+ // time calculation
+ $localTime = $solHorizon + $solAscension - 0.0172028 * $radDay - 1.73364;
+ $universalTime = $localTime - $radLongitude;
+
+ // determinate quadrant
+ $universalTime = $this->_range($universalTime, $fullCircle);
+
+ // radiant to hours
+ $universalTime *= 24 / $fullCircle;
+
+ // convert to time
+ $hour = intval($universalTime);
+ $universalTime = ($universalTime - $hour) * 60;
+ $min = intval($universalTime);
+ $universalTime = ($universalTime - $min) * 60;
+ $sec = intval($universalTime);
+
+ return $this->mktime($hour, $min, $sec, $this->date('m', $this->_unixTimestamp),
+ $this->date('j', $this->_unixTimestamp), $this->date('Y', $this->_unixTimestamp),
+ -1, true);
+ }
+
+ /**
+ * Sets a new timezone for calculation of $this object's gmt offset.
+ * For a list of supported timezones look here: http://php.net/timezones
+ * If no timezone can be detected or the given timezone is wrong UTC will be set.
+ *
+ * @param string $zone OPTIONAL timezone for date calculation; defaults to date_default_timezone_get()
+ * @return Zend_Date_DateObject Provides fluent interface
+ * @throws Zend_Date_Exception
+ */
+ public function setTimezone($zone = null)
+ {
+ $oldzone = @date_default_timezone_get();
+ if ($zone === null) {
+ $zone = $oldzone;
+ }
+
+ // throw an error on false input, but only if the new date extension is available
+ if (function_exists('timezone_open')) {
+ if (!@timezone_open($zone)) {
+ require_once 'Zend/Date/Exception.php';
+ throw new Zend_Date_Exception("timezone ($zone) is not a known timezone", 0, null, $zone);
+ }
+ }
+ // this can generate an error if the date extension is not available and a false timezone is given
+ $result = @date_default_timezone_set($zone);
+ if ($result === true) {
+ $this->_offset = mktime(0, 0, 0, 1, 2, 1970) - gmmktime(0, 0, 0, 1, 2, 1970);
+ $this->_timezone = $zone;
+ }
+ date_default_timezone_set($oldzone);
+
+ if (($zone == 'UTC') or ($zone == 'GMT')) {
+ $this->_dst = false;
+ } else {
+ $this->_dst = true;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return the timezone of $this object.
+ * The timezone is initially set when the object is instantiated.
+ *
+ * @return string actual set timezone string
+ */
+ public function getTimezone()
+ {
+ return $this->_timezone;
+ }
+
+ /**
+ * Return the offset to GMT of $this object's timezone.
+ * The offset to GMT is initially set when the object is instantiated using the currently,
+ * in effect, default timezone for PHP functions.
+ *
+ * @return integer seconds difference between GMT timezone and timezone when object was instantiated
+ */
+ public function getGmtOffset()
+ {
+ $date = $this->getDateParts($this->getUnixTimestamp(), true);
+ $zone = @date_default_timezone_get();
+ $result = @date_default_timezone_set($this->_timezone);
+ if ($result === true) {
+ $offset = $this->mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], $date['year'], false)
+ - $this->mktime($date['hours'], $date['minutes'], $date['seconds'],
+ $date['mon'], $date['mday'], $date['year'], true);
+ }
+ date_default_timezone_set($zone);
+
+ return $offset;
+ }
+
+ /**
+ * Internal method to check if the given cache supports tags
+ *
+ * @param Zend_Cache $cache
+ */
+ protected static function _getTagSupportForCache()
+ {
+ $backend = self::$_cache->getBackend();
+ if ($backend instanceof Zend_Cache_Backend_ExtendedInterface) {
+ $cacheOptions = $backend->getCapabilities();
+ self::$_cacheTags = $cacheOptions['tags'];
+ } else {
+ self::$_cacheTags = false;
+ }
+
+ return self::$_cacheTags;
+ }
+}
diff --git a/library/Zend/Date/Exception.php b/library/Zend/Date/Exception.php
new file mode 100644
index 0000000..1f7e4d9
--- /dev/null
+++ b/library/Zend/Date/Exception.php
@@ -0,0 +1,49 @@
+operand = $op;
+ parent::__construct($message, $code, $e);
+ }
+
+ public function getOperand()
+ {
+ return $this->operand;
+ }
+}
diff --git a/library/Zend/Db.php b/library/Zend/Db.php
new file mode 100644
index 0000000..f0c0eb9
--- /dev/null
+++ b/library/Zend/Db.php
@@ -0,0 +1,273 @@
+toArray();
+ }
+
+ /*
+ * Convert Zend_Config argument to plain string
+ * adapter name and separate config object.
+ */
+ if ($adapter instanceof Zend_Config) {
+ if (isset($adapter->params)) {
+ $config = $adapter->params->toArray();
+ }
+ if (isset($adapter->adapter)) {
+ $adapter = (string) $adapter->adapter;
+ } else {
+ $adapter = null;
+ }
+ }
+
+ /*
+ * Verify that adapter parameters are in an array.
+ */
+ if (!is_array($config)) {
+ /**
+ * @see Zend_Db_Exception
+ */
+ require_once 'Zend/Db/Exception.php';
+ throw new Zend_Db_Exception('Adapter parameters must be in an array or a Zend_Config object');
+ }
+
+ /*
+ * Verify that an adapter name has been specified.
+ */
+ if (!is_string($adapter) || empty($adapter)) {
+ /**
+ * @see Zend_Db_Exception
+ */
+ require_once 'Zend/Db/Exception.php';
+ throw new Zend_Db_Exception('Adapter name must be specified in a string');
+ }
+
+ /*
+ * Form full adapter class name
+ */
+ $adapterNamespace = 'Zend_Db_Adapter';
+ if (isset($config['adapterNamespace'])) {
+ if ($config['adapterNamespace'] != '') {
+ $adapterNamespace = $config['adapterNamespace'];
+ }
+ unset($config['adapterNamespace']);
+ }
+
+ // Adapter no longer normalized- see http://framework.zend.com/issues/browse/ZF-5606
+ $adapterName = $adapterNamespace . '_';
+ $adapterName .= str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($adapter))));
+
+ /*
+ * Load the adapter class. This throws an exception
+ * if the specified class cannot be loaded.
+ */
+ if (!class_exists($adapterName)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($adapterName);
+ }
+
+ /*
+ * Create an instance of the adapter class.
+ * Pass the config to the adapter class constructor.
+ */
+ $dbAdapter = new $adapterName($config);
+
+ /*
+ * Verify that the object created is a descendent of the abstract adapter type.
+ */
+ if (! $dbAdapter instanceof Zend_Db_Adapter_Abstract) {
+ /**
+ * @see Zend_Db_Exception
+ */
+ require_once 'Zend/Db/Exception.php';
+ throw new Zend_Db_Exception("Adapter class '$adapterName' does not extend Zend_Db_Adapter_Abstract");
+ }
+
+ return $dbAdapter;
+ }
+
+}
diff --git a/library/Zend/Db/Adapter/Abstract.php b/library/Zend/Db/Adapter/Abstract.php
new file mode 100644
index 0000000..f0340d6
--- /dev/null
+++ b/library/Zend/Db/Adapter/Abstract.php
@@ -0,0 +1,1280 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE
+ );
+
+ /** Weither or not that object can get serialized
+ *
+ * @var bool
+ */
+ protected $_allowSerialization = true;
+
+ /**
+ * Weither or not the database should be reconnected
+ * to that adapter when waking up
+ *
+ * @var bool
+ */
+ protected $_autoReconnectOnUnserialize = false;
+
+ /**
+ * Constructor.
+ *
+ * $config is an array of key/value pairs or an instance of Zend_Config
+ * containing configuration options. These options are common to most adapters:
+ *
+ * dbname => (string) The name of the database to user
+ * username => (string) Connect to the database as this username.
+ * password => (string) Password associated with the username.
+ * host => (string) What host to connect to, defaults to localhost
+ *
+ * Some options are used on a case-by-case basis by adapters:
+ *
+ * port => (string) The port of the database
+ * persistent => (boolean) Whether to use a persistent connection or not, defaults to false
+ * protocol => (string) The network protocol, defaults to TCPIP
+ * caseFolding => (int) style of case-alteration used for identifiers
+ *
+ * @param array|Zend_Config $config An array or instance of Zend_Config having configuration data
+ * @throws Zend_Db_Adapter_Exception
+ */
+ public function __construct($config)
+ {
+ /*
+ * Verify that adapter parameters are in an array.
+ */
+ if (!is_array($config)) {
+ /*
+ * Convert Zend_Config argument to a plain array.
+ */
+ if ($config instanceof Zend_Config) {
+ $config = $config->toArray();
+ } else {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception('Adapter parameters must be in an array or a Zend_Config object');
+ }
+ }
+
+ $this->_checkRequiredOptions($config);
+
+ $options = array(
+ Zend_Db::CASE_FOLDING => $this->_caseFolding,
+ Zend_Db::AUTO_QUOTE_IDENTIFIERS => $this->_autoQuoteIdentifiers,
+ Zend_Db::FETCH_MODE => $this->_fetchMode,
+ );
+ $driverOptions = array();
+
+ /*
+ * normalize the config and merge it with the defaults
+ */
+ if (array_key_exists('options', $config)) {
+ // can't use array_merge() because keys might be integers
+ foreach ((array) $config['options'] as $key => $value) {
+ $options[$key] = $value;
+ }
+ }
+ if (array_key_exists('driver_options', $config)) {
+ if (!empty($config['driver_options'])) {
+ // can't use array_merge() because keys might be integers
+ foreach ((array) $config['driver_options'] as $key => $value) {
+ $driverOptions[$key] = $value;
+ }
+ }
+ }
+
+ if (!isset($config['charset'])) {
+ $config['charset'] = null;
+ }
+
+ if (!isset($config['persistent'])) {
+ $config['persistent'] = false;
+ }
+
+ $this->_config = array_merge($this->_config, $config);
+ $this->_config['options'] = $options;
+ $this->_config['driver_options'] = $driverOptions;
+
+
+ // obtain the case setting, if there is one
+ if (array_key_exists(Zend_Db::CASE_FOLDING, $options)) {
+ $case = (int) $options[Zend_Db::CASE_FOLDING];
+ switch ($case) {
+ case Zend_Db::CASE_LOWER:
+ case Zend_Db::CASE_UPPER:
+ case Zend_Db::CASE_NATURAL:
+ $this->_caseFolding = $case;
+ break;
+ default:
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception('Case must be one of the following constants: '
+ . 'Zend_Db::CASE_NATURAL, Zend_Db::CASE_LOWER, Zend_Db::CASE_UPPER');
+ }
+ }
+
+ if (array_key_exists(Zend_Db::FETCH_MODE, $options)) {
+ if (is_string($options[Zend_Db::FETCH_MODE])) {
+ $constant = 'Zend_Db::FETCH_' . strtoupper($options[Zend_Db::FETCH_MODE]);
+ if(defined($constant)) {
+ $options[Zend_Db::FETCH_MODE] = constant($constant);
+ }
+ }
+ $this->setFetchMode((int) $options[Zend_Db::FETCH_MODE]);
+ }
+
+ // obtain quoting property if there is one
+ if (array_key_exists(Zend_Db::AUTO_QUOTE_IDENTIFIERS, $options)) {
+ $this->_autoQuoteIdentifiers = (bool) $options[Zend_Db::AUTO_QUOTE_IDENTIFIERS];
+ }
+
+ // obtain allow serialization property if there is one
+ if (array_key_exists(Zend_Db::ALLOW_SERIALIZATION, $options)) {
+ $this->_allowSerialization = (bool) $options[Zend_Db::ALLOW_SERIALIZATION];
+ }
+
+ // obtain auto reconnect on unserialize property if there is one
+ if (array_key_exists(Zend_Db::AUTO_RECONNECT_ON_UNSERIALIZE, $options)) {
+ $this->_autoReconnectOnUnserialize = (bool) $options[Zend_Db::AUTO_RECONNECT_ON_UNSERIALIZE];
+ }
+
+ // create a profiler object
+ $profiler = false;
+ if (array_key_exists(Zend_Db::PROFILER, $this->_config)) {
+ $profiler = $this->_config[Zend_Db::PROFILER];
+ unset($this->_config[Zend_Db::PROFILER]);
+ }
+ $this->setProfiler($profiler);
+ }
+
+ /**
+ * Check for config options that are mandatory.
+ * Throw exceptions if any are missing.
+ *
+ * @param array $config
+ * @throws Zend_Db_Adapter_Exception
+ */
+ protected function _checkRequiredOptions(array $config)
+ {
+ // we need at least a dbname
+ if (! array_key_exists('dbname', $config)) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'dbname' that names the database instance");
+ }
+
+ if (! array_key_exists('password', $config)) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'password' for login credentials");
+ }
+
+ if (! array_key_exists('username', $config)) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'username' for login credentials");
+ }
+ }
+
+ /**
+ * Returns the underlying database connection object or resource.
+ * If not presently connected, this initiates the connection.
+ *
+ * @return object|resource|null
+ */
+ public function getConnection()
+ {
+ $this->_connect();
+ return $this->_connection;
+ }
+
+ /**
+ * Returns the configuration variables in this adapter.
+ *
+ * @return array
+ */
+ public function getConfig()
+ {
+ return $this->_config;
+ }
+
+ /**
+ * Set the adapter's profiler object.
+ *
+ * The argument may be a boolean, an associative array, an instance of
+ * Zend_Db_Profiler, or an instance of Zend_Config.
+ *
+ * A boolean argument sets the profiler to enabled if true, or disabled if
+ * false. The profiler class is the adapter's default profiler class,
+ * Zend_Db_Profiler.
+ *
+ * An instance of Zend_Db_Profiler sets the adapter's instance to that
+ * object. The profiler is enabled and disabled separately.
+ *
+ * An associative array argument may contain any of the keys 'enabled',
+ * 'class', and 'instance'. The 'enabled' and 'instance' keys correspond to the
+ * boolean and object types documented above. The 'class' key is used to name a
+ * class to use for a custom profiler. The class must be Zend_Db_Profiler or a
+ * subclass. The class is instantiated with no constructor arguments. The 'class'
+ * option is ignored when the 'instance' option is supplied.
+ *
+ * An object of type Zend_Config may contain the properties 'enabled', 'class', and
+ * 'instance', just as if an associative array had been passed instead.
+ *
+ * @param Zend_Db_Profiler|Zend_Config|array|boolean $profiler
+ * @return Zend_Db_Adapter_Abstract Provides a fluent interface
+ * @throws Zend_Db_Profiler_Exception if the object instance or class specified
+ * is not Zend_Db_Profiler or an extension of that class.
+ */
+ public function setProfiler($profiler)
+ {
+ $enabled = null;
+ $profilerClass = $this->_defaultProfilerClass;
+ $profilerInstance = null;
+
+ if ($profilerIsObject = is_object($profiler)) {
+ if ($profiler instanceof Zend_Db_Profiler) {
+ $profilerInstance = $profiler;
+ } else if ($profiler instanceof Zend_Config) {
+ $profiler = $profiler->toArray();
+ } else {
+ /**
+ * @see Zend_Db_Profiler_Exception
+ */
+ require_once 'Zend/Db/Profiler/Exception.php';
+ throw new Zend_Db_Profiler_Exception('Profiler argument must be an instance of either Zend_Db_Profiler'
+ . ' or Zend_Config when provided as an object');
+ }
+ }
+
+ if (is_array($profiler)) {
+ if (isset($profiler['enabled'])) {
+ $enabled = (bool) $profiler['enabled'];
+ }
+ if (isset($profiler['class'])) {
+ $profilerClass = $profiler['class'];
+ }
+ if (isset($profiler['instance'])) {
+ $profilerInstance = $profiler['instance'];
+ }
+ } else if (!$profilerIsObject) {
+ $enabled = (bool) $profiler;
+ }
+
+ if ($profilerInstance === null) {
+ if (!class_exists($profilerClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($profilerClass);
+ }
+ $profilerInstance = new $profilerClass();
+ }
+
+ if (!$profilerInstance instanceof Zend_Db_Profiler) {
+ /** @see Zend_Db_Profiler_Exception */
+ require_once 'Zend/Db/Profiler/Exception.php';
+ throw new Zend_Db_Profiler_Exception('Class ' . get_class($profilerInstance) . ' does not extend '
+ . 'Zend_Db_Profiler');
+ }
+
+ if (null !== $enabled) {
+ $profilerInstance->setEnabled($enabled);
+ }
+
+ $this->_profiler = $profilerInstance;
+
+ return $this;
+ }
+
+
+ /**
+ * Returns the profiler for this adapter.
+ *
+ * @return Zend_Db_Profiler
+ */
+ public function getProfiler()
+ {
+ return $this->_profiler;
+ }
+
+ /**
+ * Get the default statement class.
+ *
+ * @return string
+ */
+ public function getStatementClass()
+ {
+ return $this->_defaultStmtClass;
+ }
+
+ /**
+ * Set the default statement class.
+ *
+ * @return Zend_Db_Adapter_Abstract Fluent interface
+ */
+ public function setStatementClass($class)
+ {
+ $this->_defaultStmtClass = $class;
+ return $this;
+ }
+
+ /**
+ * Prepares and executes an SQL statement with bound data.
+ *
+ * @param mixed $sql The SQL statement with placeholders.
+ * May be a string or Zend_Db_Select.
+ * @param mixed $bind An array of data to bind to the placeholders.
+ * @return Zend_Db_Statement_Interface
+ */
+ public function query($sql, $bind = array())
+ {
+ // connect to the database if needed
+ $this->_connect();
+
+ // is the $sql a Zend_Db_Select object?
+ if ($sql instanceof Zend_Db_Select) {
+ if (empty($bind)) {
+ $bind = $sql->getBind();
+ }
+
+ $sql = $sql->assemble();
+ }
+
+ // make sure $bind to an array;
+ // don't use (array) typecasting because
+ // because $bind may be a Zend_Db_Expr object
+ if (!is_array($bind)) {
+ $bind = array($bind);
+ }
+
+ // prepare and execute the statement with profiling
+ $stmt = $this->prepare($sql);
+ $stmt->execute($bind);
+
+ // return the results embedded in the prepared statement object
+ $stmt->setFetchMode($this->_fetchMode);
+ return $stmt;
+ }
+
+ /**
+ * Leave autocommit mode and begin a transaction.
+ *
+ * @return Zend_Db_Adapter_Abstract
+ */
+ public function beginTransaction()
+ {
+ $this->_connect();
+ $q = $this->_profiler->queryStart('begin', Zend_Db_Profiler::TRANSACTION);
+ $this->_beginTransaction();
+ $this->_profiler->queryEnd($q);
+ return $this;
+ }
+
+ /**
+ * Commit a transaction and return to autocommit mode.
+ *
+ * @return Zend_Db_Adapter_Abstract
+ */
+ public function commit()
+ {
+ $this->_connect();
+ $q = $this->_profiler->queryStart('commit', Zend_Db_Profiler::TRANSACTION);
+ $this->_commit();
+ $this->_profiler->queryEnd($q);
+ return $this;
+ }
+
+ /**
+ * Roll back a transaction and return to autocommit mode.
+ *
+ * @return Zend_Db_Adapter_Abstract
+ */
+ public function rollBack()
+ {
+ $this->_connect();
+ $q = $this->_profiler->queryStart('rollback', Zend_Db_Profiler::TRANSACTION);
+ $this->_rollBack();
+ $this->_profiler->queryEnd($q);
+ return $this;
+ }
+
+ /**
+ * Inserts a table row with specified data.
+ *
+ * @param mixed $table The table to insert data into.
+ * @param array $bind Column-value pairs.
+ * @return int The number of affected rows.
+ * @throws Zend_Db_Adapter_Exception
+ */
+ public function insert($table, array $bind)
+ {
+ // extract and quote col names from the array keys
+ $cols = array();
+ $vals = array();
+ $i = 0;
+ foreach ($bind as $col => $val) {
+ $cols[] = $this->quoteIdentifier($col, true);
+ if ($val instanceof Zend_Db_Expr) {
+ $vals[] = $val->__toString();
+ unset($bind[$col]);
+ } else {
+ if ($this->supportsParameters('positional')) {
+ $vals[] = '?';
+ } else {
+ if ($this->supportsParameters('named')) {
+ unset($bind[$col]);
+ $bind[':col'.$i] = $val;
+ $vals[] = ':col'.$i;
+ $i++;
+ } else {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception(get_class($this) ." doesn't support positional or named binding");
+ }
+ }
+ }
+ }
+
+ // build the statement
+ $sql = "INSERT INTO "
+ . $this->quoteIdentifier($table, true)
+ . ' (' . implode(', ', $cols) . ') '
+ . 'VALUES (' . implode(', ', $vals) . ')';
+
+ // execute the statement and return the number of affected rows
+ if ($this->supportsParameters('positional')) {
+ $bind = array_values($bind);
+ }
+ $stmt = $this->query($sql, $bind);
+ $result = $stmt->rowCount();
+ return $result;
+ }
+
+ /**
+ * Updates table rows with specified data based on a WHERE clause.
+ *
+ * @param mixed $table The table to update.
+ * @param array $bind Column-value pairs.
+ * @param mixed $where UPDATE WHERE clause(s).
+ * @return int The number of affected rows.
+ * @throws Zend_Db_Adapter_Exception
+ */
+ public function update($table, array $bind, $where = '')
+ {
+ /**
+ * Build "col = ?" pairs for the statement,
+ * except for Zend_Db_Expr which is treated literally.
+ */
+ $set = array();
+ $i = 0;
+ foreach ($bind as $col => $val) {
+ if ($val instanceof Zend_Db_Expr) {
+ $val = $val->__toString();
+ unset($bind[$col]);
+ } else {
+ if ($this->supportsParameters('positional')) {
+ $val = '?';
+ } else {
+ if ($this->supportsParameters('named')) {
+ unset($bind[$col]);
+ $bind[':col'.$i] = $val;
+ $val = ':col'.$i;
+ $i++;
+ } else {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception(get_class($this) ." doesn't support positional or named binding");
+ }
+ }
+ }
+ $set[] = $this->quoteIdentifier($col, true) . ' = ' . $val;
+ }
+
+ $where = $this->_whereExpr($where);
+
+ /**
+ * Build the UPDATE statement
+ */
+ $sql = "UPDATE "
+ . $this->quoteIdentifier($table, true)
+ . ' SET ' . implode(', ', $set)
+ . (($where) ? " WHERE $where" : '');
+
+ /**
+ * Execute the statement and return the number of affected rows
+ */
+ if ($this->supportsParameters('positional')) {
+ $stmt = $this->query($sql, array_values($bind));
+ } else {
+ $stmt = $this->query($sql, $bind);
+ }
+ $result = $stmt->rowCount();
+ return $result;
+ }
+
+ /**
+ * Deletes table rows based on a WHERE clause.
+ *
+ * @param mixed $table The table to update.
+ * @param mixed $where DELETE WHERE clause(s).
+ * @return int The number of affected rows.
+ */
+ public function delete($table, $where = '')
+ {
+ $where = $this->_whereExpr($where);
+
+ /**
+ * Build the DELETE statement
+ */
+ $sql = "DELETE FROM "
+ . $this->quoteIdentifier($table, true)
+ . (($where) ? " WHERE $where" : '');
+
+ /**
+ * Execute the statement and return the number of affected rows
+ */
+ $stmt = $this->query($sql);
+ $result = $stmt->rowCount();
+ return $result;
+ }
+
+ /**
+ * Convert an array, string, or Zend_Db_Expr object
+ * into a string to put in a WHERE clause.
+ *
+ * @param mixed $where
+ * @return string
+ */
+ protected function _whereExpr($where)
+ {
+ if (empty($where)) {
+ return $where;
+ }
+ if (!is_array($where)) {
+ $where = array($where);
+ }
+ foreach ($where as $cond => &$term) {
+ // is $cond an int? (i.e. Not a condition)
+ if (is_int($cond)) {
+ // $term is the full condition
+ if ($term instanceof Zend_Db_Expr) {
+ $term = $term->__toString();
+ }
+ } else {
+ // $cond is the condition with placeholder,
+ // and $term is quoted into the condition
+ $term = $this->quoteInto($cond, $term);
+ }
+ $term = '(' . $term . ')';
+ }
+
+ $where = implode(' AND ', $where);
+ return $where;
+ }
+
+ /**
+ * Creates and returns a new Zend_Db_Select object for this adapter.
+ *
+ * @return Zend_Db_Select
+ */
+ public function select()
+ {
+ return new Zend_Db_Select($this);
+ }
+
+ /**
+ * Get the fetch mode.
+ *
+ * @return int
+ */
+ public function getFetchMode()
+ {
+ return $this->_fetchMode;
+ }
+
+ /**
+ * Fetches all SQL result rows as a sequential array.
+ * Uses the current fetchMode for the adapter.
+ *
+ * @param string|Zend_Db_Select $sql An SQL SELECT statement.
+ * @param mixed $bind Data to bind into SELECT placeholders.
+ * @param mixed $fetchMode Override current fetch mode.
+ * @return array
+ */
+ public function fetchAll($sql, $bind = array(), $fetchMode = null)
+ {
+ if ($fetchMode === null) {
+ $fetchMode = $this->_fetchMode;
+ }
+ $stmt = $this->query($sql, $bind);
+ $result = $stmt->fetchAll($fetchMode);
+ return $result;
+ }
+
+ /**
+ * Fetches the first row of the SQL result.
+ * Uses the current fetchMode for the adapter.
+ *
+ * @param string|Zend_Db_Select $sql An SQL SELECT statement.
+ * @param mixed $bind Data to bind into SELECT placeholders.
+ * @param mixed $fetchMode Override current fetch mode.
+ * @return array
+ */
+ public function fetchRow($sql, $bind = array(), $fetchMode = null)
+ {
+ if ($fetchMode === null) {
+ $fetchMode = $this->_fetchMode;
+ }
+ $stmt = $this->query($sql, $bind);
+ $result = $stmt->fetch($fetchMode);
+ return $result;
+ }
+
+ /**
+ * Fetches all SQL result rows as an associative array.
+ *
+ * The first column is the key, the entire row array is the
+ * value. You should construct the query to be sure that
+ * the first column contains unique values, or else
+ * rows with duplicate values in the first column will
+ * overwrite previous data.
+ *
+ * @param string|Zend_Db_Select $sql An SQL SELECT statement.
+ * @param mixed $bind Data to bind into SELECT placeholders.
+ * @return array
+ */
+ public function fetchAssoc($sql, $bind = array())
+ {
+ $stmt = $this->query($sql, $bind);
+ $data = array();
+ while ($row = $stmt->fetch(Zend_Db::FETCH_ASSOC)) {
+ $tmp = array_values(array_slice($row, 0, 1));
+ $data[$tmp[0]] = $row;
+ }
+ return $data;
+ }
+
+ /**
+ * Fetches the first column of all SQL result rows as an array.
+ *
+ * @param string|Zend_Db_Select $sql An SQL SELECT statement.
+ * @param mixed $bind Data to bind into SELECT placeholders.
+ * @return array
+ */
+ public function fetchCol($sql, $bind = array())
+ {
+ $stmt = $this->query($sql, $bind);
+ $result = $stmt->fetchAll(Zend_Db::FETCH_COLUMN, 0);
+ return $result;
+ }
+
+ /**
+ * Fetches all SQL result rows as an array of key-value pairs.
+ *
+ * The first column is the key, the second column is the
+ * value.
+ *
+ * @param string|Zend_Db_Select $sql An SQL SELECT statement.
+ * @param mixed $bind Data to bind into SELECT placeholders.
+ * @return array
+ */
+ public function fetchPairs($sql, $bind = array())
+ {
+ $stmt = $this->query($sql, $bind);
+ $data = array();
+ while ($row = $stmt->fetch(Zend_Db::FETCH_NUM)) {
+ $data[$row[0]] = $row[1];
+ }
+ return $data;
+ }
+
+ /**
+ * Fetches the first column of the first row of the SQL result.
+ *
+ * @param string|Zend_Db_Select $sql An SQL SELECT statement.
+ * @param mixed $bind Data to bind into SELECT placeholders.
+ * @return string
+ */
+ public function fetchOne($sql, $bind = array())
+ {
+ $stmt = $this->query($sql, $bind);
+ $result = $stmt->fetchColumn(0);
+ return $result;
+ }
+
+ /**
+ * Quote a raw string.
+ *
+ * @param string $value Raw string
+ * @return string Quoted string
+ */
+ protected function _quote($value)
+ {
+ if (is_int($value)) {
+ return $value;
+ } elseif (is_float($value)) {
+ return sprintf('%F', $value);
+ }
+ return "'" . addcslashes($value, "\000\n\r\\'\"\032") . "'";
+ }
+
+ /**
+ * Safely quotes a value for an SQL statement.
+ *
+ * If an array is passed as the value, the array values are quoted
+ * and then returned as a comma-separated string.
+ *
+ * @param mixed $value The value to quote.
+ * @param mixed $type OPTIONAL the SQL datatype name, or constant, or null.
+ * @return mixed An SQL-safe quoted value (or string of separated values).
+ */
+ public function quote($value, $type = null)
+ {
+ $this->_connect();
+
+ if ($value instanceof Zend_Db_Select) {
+ return '(' . $value->assemble() . ')';
+ }
+
+ if ($value instanceof Zend_Db_Expr) {
+ return $value->__toString();
+ }
+
+ if (is_array($value)) {
+ foreach ($value as &$val) {
+ $val = $this->quote($val, $type);
+ }
+ return implode(', ', $value);
+ }
+
+ if ($type !== null && array_key_exists($type = strtoupper($type), $this->_numericDataTypes)) {
+ $quotedValue = '0';
+ switch ($this->_numericDataTypes[$type]) {
+ case Zend_Db::INT_TYPE: // 32-bit integer
+ $quotedValue = (string) intval($value);
+ break;
+ case Zend_Db::BIGINT_TYPE: // 64-bit integer
+ // ANSI SQL-style hex literals (e.g. x'[\dA-F]+')
+ // are not supported here, because these are string
+ // literals, not numeric literals.
+ if (preg_match('/^(
+ [+-]? # optional sign
+ (?:
+ 0[Xx][\da-fA-F]+ # ODBC-style hexadecimal
+ |\d+ # decimal or octal, or MySQL ZEROFILL decimal
+ (?:[eE][+-]?\d+)? # optional exponent on decimals or octals
+ )
+ )/x',
+ (string) $value, $matches)) {
+ $quotedValue = $matches[1];
+ }
+ break;
+ case Zend_Db::FLOAT_TYPE: // float or decimal
+ $quotedValue = sprintf('%F', $value);
+ }
+ return $quotedValue;
+ }
+
+ return $this->_quote($value);
+ }
+
+ /**
+ * Quotes a value and places into a piece of text at a placeholder.
+ *
+ * The placeholder is a question-mark; all placeholders will be replaced
+ * with the quoted value. For example:
+ *
+ *
+ * $text = "WHERE date < ?";
+ * $date = "2005-01-02";
+ * $safe = $sql->quoteInto($text, $date);
+ * // $safe = "WHERE date < '2005-01-02'"
+ *
+ *
+ * @param string $text The text with a placeholder.
+ * @param mixed $value The value to quote.
+ * @param string $type OPTIONAL SQL datatype
+ * @param integer $count OPTIONAL count of placeholders to replace
+ * @return string An SQL-safe quoted value placed into the original text.
+ */
+ public function quoteInto($text, $value, $type = null, $count = null)
+ {
+ if ($count === null) {
+ return str_replace('?', $this->quote($value, $type), $text);
+ } else {
+ while ($count > 0) {
+ if (strpos($text, '?') !== false) {
+ $text = substr_replace($text, $this->quote($value, $type), strpos($text, '?'), 1);
+ }
+ --$count;
+ }
+ return $text;
+ }
+ }
+
+ /**
+ * Quotes an identifier.
+ *
+ * Accepts a string representing a qualified indentifier. For Example:
+ *
+ * $adapter->quoteIdentifier('myschema.mytable')
+ *
+ * Returns: "myschema"."mytable"
+ *
+ * Or, an array of one or more identifiers that may form a qualified identifier:
+ *
+ * $adapter->quoteIdentifier(array('myschema','my.table'))
+ *
+ * Returns: "myschema"."my.table"
+ *
+ * The actual quote character surrounding the identifiers may vary depending on
+ * the adapter.
+ *
+ * @param string|array|Zend_Db_Expr $ident The identifier.
+ * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option.
+ * @return string The quoted identifier.
+ */
+ public function quoteIdentifier($ident, $auto=false)
+ {
+ return $this->_quoteIdentifierAs($ident, null, $auto);
+ }
+
+ /**
+ * Quote a column identifier and alias.
+ *
+ * @param string|array|Zend_Db_Expr $ident The identifier or expression.
+ * @param string $alias An alias for the column.
+ * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option.
+ * @return string The quoted identifier and alias.
+ */
+ public function quoteColumnAs($ident, $alias, $auto=false)
+ {
+ return $this->_quoteIdentifierAs($ident, $alias, $auto);
+ }
+
+ /**
+ * Quote a table identifier and alias.
+ *
+ * @param string|array|Zend_Db_Expr $ident The identifier or expression.
+ * @param string $alias An alias for the table.
+ * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option.
+ * @return string The quoted identifier and alias.
+ */
+ public function quoteTableAs($ident, $alias = null, $auto = false)
+ {
+ return $this->_quoteIdentifierAs($ident, $alias, $auto);
+ }
+
+ /**
+ * Quote an identifier and an optional alias.
+ *
+ * @param string|array|Zend_Db_Expr $ident The identifier or expression.
+ * @param string $alias An optional alias.
+ * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option.
+ * @param string $as The string to add between the identifier/expression and the alias.
+ * @return string The quoted identifier and alias.
+ */
+ protected function _quoteIdentifierAs($ident, $alias = null, $auto = false, $as = ' AS ')
+ {
+ if ($ident instanceof Zend_Db_Expr) {
+ $quoted = $ident->__toString();
+ } elseif ($ident instanceof Zend_Db_Select) {
+ $quoted = '(' . $ident->assemble() . ')';
+ } else {
+ if (is_string($ident)) {
+ $ident = explode('.', $ident);
+ }
+ if (is_array($ident)) {
+ $segments = array();
+ foreach ($ident as $segment) {
+ if ($segment instanceof Zend_Db_Expr) {
+ $segments[] = $segment->__toString();
+ } else {
+ $segments[] = $this->_quoteIdentifier($segment, $auto);
+ }
+ }
+ if ($alias !== null && end($ident) == $alias) {
+ $alias = null;
+ }
+ $quoted = implode('.', $segments);
+ } else {
+ $quoted = $this->_quoteIdentifier($ident, $auto);
+ }
+ }
+ if ($alias !== null) {
+ $quoted .= $as . $this->_quoteIdentifier($alias, $auto);
+ }
+ return $quoted;
+ }
+
+ /**
+ * Quote an identifier.
+ *
+ * @param string $value The identifier or expression.
+ * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option.
+ * @return string The quoted identifier and alias.
+ */
+ protected function _quoteIdentifier($value, $auto=false)
+ {
+ if ($auto === false || $this->_autoQuoteIdentifiers === true) {
+ $q = $this->getQuoteIdentifierSymbol();
+ return ($q . str_replace("$q", "$q$q", $value) . $q);
+ }
+ return $value;
+ }
+
+ /**
+ * Returns the symbol the adapter uses for delimited identifiers.
+ *
+ * @return string
+ */
+ public function getQuoteIdentifierSymbol()
+ {
+ return '"';
+ }
+
+ /**
+ * Return the most recent value from the specified sequence in the database.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ return null;
+ }
+
+ /**
+ * Generate a new value from the specified sequence in the database, and return it.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ return null;
+ }
+
+ /**
+ * Helper method to change the case of the strings used
+ * when returning result sets in FETCH_ASSOC and FETCH_BOTH
+ * modes.
+ *
+ * This is not intended to be used by application code,
+ * but the method must be public so the Statement class
+ * can invoke it.
+ *
+ * @param string $key
+ * @return string
+ */
+ public function foldCase($key)
+ {
+ switch ($this->_caseFolding) {
+ case Zend_Db::CASE_LOWER:
+ $value = strtolower((string) $key);
+ break;
+ case Zend_Db::CASE_UPPER:
+ $value = strtoupper((string) $key);
+ break;
+ case Zend_Db::CASE_NATURAL:
+ default:
+ $value = (string) $key;
+ }
+ return $value;
+ }
+
+ /**
+ * called when object is getting serialized
+ * This disconnects the DB object that cant be serialized
+ *
+ * @throws Zend_Db_Adapter_Exception
+ * @return array
+ */
+ public function __sleep()
+ {
+ if ($this->_allowSerialization == false) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception(get_class($this) ." is not allowed to be serialized");
+ }
+ $this->_connection = false;
+ return array_keys(array_diff_key(get_object_vars($this), array('_connection'=>false)));
+ }
+
+ /**
+ * called when object is getting unserialized
+ *
+ * @return void
+ */
+ public function __wakeup()
+ {
+ if ($this->_autoReconnectOnUnserialize == true) {
+ $this->getConnection();
+ }
+ }
+
+ /**
+ * Abstract Methods
+ */
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ abstract public function listTables();
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ abstract public function describeTable($tableName, $schemaName = null);
+
+ /**
+ * Creates a connection to the database.
+ *
+ * @return void
+ */
+ abstract protected function _connect();
+
+ /**
+ * Test if a connection is active
+ *
+ * @return boolean
+ */
+ abstract public function isConnected();
+
+ /**
+ * Force the connection to close.
+ *
+ * @return void
+ */
+ abstract public function closeConnection();
+
+ /**
+ * Prepare a statement and return a PDOStatement-like object.
+ *
+ * @param string|Zend_Db_Select $sql SQL query
+ * @return Zend_Db_Statement|PDOStatement
+ */
+ abstract public function prepare($sql);
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ */
+ abstract public function lastInsertId($tableName = null, $primaryKey = null);
+
+ /**
+ * Begin a transaction.
+ */
+ abstract protected function _beginTransaction();
+
+ /**
+ * Commit a transaction.
+ */
+ abstract protected function _commit();
+
+ /**
+ * Roll-back a transaction.
+ */
+ abstract protected function _rollBack();
+
+ /**
+ * Set the fetch mode.
+ *
+ * @param integer $mode
+ * @return void
+ * @throws Zend_Db_Adapter_Exception
+ */
+ abstract public function setFetchMode($mode);
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param mixed $sql
+ * @param integer $count
+ * @param integer $offset
+ * @return string
+ */
+ abstract public function limit($sql, $count, $offset = 0);
+
+ /**
+ * Check if the adapter supports real SQL parameters.
+ *
+ * @param string $type 'positional' or 'named'
+ * @return bool
+ */
+ abstract public function supportsParameters($type);
+
+ /**
+ * Retrieve server version in PHP style
+ *
+ * @return string
+ */
+ abstract public function getServerVersion();
+}
diff --git a/library/Zend/Db/Adapter/Db2.php b/library/Zend/Db/Adapter/Db2.php
new file mode 100644
index 0000000..d8dca9b
--- /dev/null
+++ b/library/Zend/Db/Adapter/Db2.php
@@ -0,0 +1,840 @@
+ (string) Connect to the database as this username.
+ * password => (string) Password associated with the username.
+ * host => (string) What host to connect to (default 127.0.0.1)
+ * dbname => (string) The name of the database to user
+ * protocol => (string) Protocol to use, defaults to "TCPIP"
+ * port => (integer) Port number to use for TCP/IP if protocol is "TCPIP"
+ * persistent => (boolean) Set TRUE to use a persistent connection (db2_pconnect)
+ * os => (string) This should be set to 'i5' if the db is on an os400/i5
+ * schema => (string) The default schema the connection should use
+ *
+ * @var array
+ */
+ protected $_config = array(
+ 'dbname' => null,
+ 'username' => null,
+ 'password' => null,
+ 'host' => 'localhost',
+ 'port' => '50000',
+ 'protocol' => 'TCPIP',
+ 'persistent' => false,
+ 'os' => null,
+ 'schema' => null
+ );
+
+ /**
+ * Execution mode
+ *
+ * @var int execution flag (DB2_AUTOCOMMIT_ON or DB2_AUTOCOMMIT_OFF)
+ */
+ protected $_execute_mode = DB2_AUTOCOMMIT_ON;
+
+ /**
+ * Default class name for a DB statement.
+ *
+ * @var string
+ */
+ protected $_defaultStmtClass = 'Zend_Db_Statement_Db2';
+ protected $_isI5 = false;
+
+ /**
+ * Keys are UPPERCASE SQL datatypes or the constants
+ * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
+ *
+ * Values are:
+ * 0 = 32-bit integer
+ * 1 = 64-bit integer
+ * 2 = float or decimal
+ *
+ * @var array Associative array of datatypes to values 0, 1, or 2.
+ */
+ protected $_numericDataTypes = array(
+ Zend_Db::INT_TYPE => Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INTEGER' => Zend_Db::INT_TYPE,
+ 'SMALLINT' => Zend_Db::INT_TYPE,
+ 'BIGINT' => Zend_Db::BIGINT_TYPE,
+ 'DECIMAL' => Zend_Db::FLOAT_TYPE,
+ 'NUMERIC' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * Creates a connection resource.
+ *
+ * @return void
+ */
+ protected function _connect()
+ {
+ if (is_resource($this->_connection)) {
+ // connection already exists
+ return;
+ }
+
+ if (!extension_loaded('ibm_db2')) {
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception('The IBM DB2 extension is required for this adapter but the extension is not loaded');
+ }
+
+ $this->_determineI5();
+ if ($this->_config['persistent']) {
+ // use persistent connection
+ $conn_func_name = 'db2_pconnect';
+ } else {
+ // use "normal" connection
+ $conn_func_name = 'db2_connect';
+ }
+
+ if (!isset($this->_config['driver_options']['autocommit'])) {
+ // set execution mode
+ $this->_config['driver_options']['autocommit'] = &$this->_execute_mode;
+ }
+
+ if (isset($this->_config['options'][Zend_Db::CASE_FOLDING])) {
+ $caseAttrMap = array(
+ Zend_Db::CASE_NATURAL => DB2_CASE_NATURAL,
+ Zend_Db::CASE_UPPER => DB2_CASE_UPPER,
+ Zend_Db::CASE_LOWER => DB2_CASE_LOWER
+ );
+ $this->_config['driver_options']['DB2_ATTR_CASE'] = $caseAttrMap[$this->_config['options'][Zend_Db::CASE_FOLDING]];
+ }
+
+ if ($this->_isI5 && isset($this->_config['driver_options']['i5_naming'])) {
+ if ($this->_config['driver_options']['i5_naming']) {
+ $this->_config['driver_options']['i5_naming'] = DB2_I5_NAMING_ON;
+ } else {
+ $this->_config['driver_options']['i5_naming'] = DB2_I5_NAMING_OFF;
+ }
+ }
+
+ if ($this->_config['host'] !== 'localhost' && !$this->_isI5) {
+ // if the host isn't localhost, use extended connection params
+ $dbname = 'DRIVER={IBM DB2 ODBC DRIVER}' .
+ ';DATABASE=' . $this->_config['dbname'] .
+ ';HOSTNAME=' . $this->_config['host'] .
+ ';PORT=' . $this->_config['port'] .
+ ';PROTOCOL=' . $this->_config['protocol'] .
+ ';UID=' . $this->_config['username'] .
+ ';PWD=' . $this->_config['password'] .';';
+ $this->_connection = $conn_func_name(
+ $dbname,
+ null,
+ null,
+ $this->_config['driver_options']
+ );
+ } else {
+ // host is localhost, so use standard connection params
+ $this->_connection = $conn_func_name(
+ $this->_config['dbname'],
+ $this->_config['username'],
+ $this->_config['password'],
+ $this->_config['driver_options']
+ );
+ }
+
+ // check the connection
+ if (!$this->_connection) {
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception(db2_conn_errormsg(), db2_conn_error());
+ }
+ }
+
+ /**
+ * Test if a connection is active
+ *
+ * @return boolean
+ */
+ public function isConnected()
+ {
+ return ((bool) (is_resource($this->_connection)
+ && get_resource_type($this->_connection) == 'DB2 Connection'));
+ }
+
+ /**
+ * Force the connection to close.
+ *
+ * @return void
+ */
+ public function closeConnection()
+ {
+ if ($this->isConnected()) {
+ db2_close($this->_connection);
+ }
+ $this->_connection = null;
+ }
+
+ /**
+ * Returns an SQL statement for preparation.
+ *
+ * @param string $sql The SQL statement with placeholders.
+ * @return Zend_Db_Statement_Db2
+ */
+ public function prepare($sql)
+ {
+ $this->_connect();
+ $stmtClass = $this->_defaultStmtClass;
+ if (!class_exists($stmtClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($stmtClass);
+ }
+ $stmt = new $stmtClass($this, $sql);
+ $stmt->setFetchMode($this->_fetchMode);
+ return $stmt;
+ }
+
+ /**
+ * Gets the execution mode
+ *
+ * @return int the execution mode (DB2_AUTOCOMMIT_ON or DB2_AUTOCOMMIT_OFF)
+ */
+ public function _getExecuteMode()
+ {
+ return $this->_execute_mode;
+ }
+
+ /**
+ * @param integer $mode
+ * @return void
+ */
+ public function _setExecuteMode($mode)
+ {
+ switch ($mode) {
+ case DB2_AUTOCOMMIT_OFF:
+ case DB2_AUTOCOMMIT_ON:
+ $this->_execute_mode = $mode;
+ db2_autocommit($this->_connection, $mode);
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception("execution mode not supported");
+ break;
+ }
+ }
+
+ /**
+ * Quote a raw string.
+ *
+ * @param string $value Raw string
+ * @return string Quoted string
+ */
+ protected function _quote($value)
+ {
+ if (is_int($value) || is_float($value)) {
+ return $value;
+ }
+ /**
+ * Use db2_escape_string() if it is present in the IBM DB2 extension.
+ * But some supported versions of PHP do not include this function,
+ * so fall back to default quoting in the parent class.
+ */
+ if (function_exists('db2_escape_string')) {
+ return "'" . db2_escape_string($value) . "'";
+ }
+ return parent::_quote($value);
+ }
+
+ /**
+ * @return string
+ */
+ public function getQuoteIdentifierSymbol()
+ {
+ $this->_connect();
+ $info = db2_server_info($this->_connection);
+ if ($info) {
+ $identQuote = $info->IDENTIFIER_QUOTE_CHAR;
+ } else {
+ // db2_server_info() does not return result on some i5 OS version
+ if ($this->_isI5) {
+ $identQuote ="'";
+ }
+ }
+ return $identQuote;
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ * @param string $schema OPTIONAL
+ * @return array
+ */
+ public function listTables($schema = null)
+ {
+ $this->_connect();
+
+ if ($schema === null && $this->_config['schema'] != null) {
+ $schema = $this->_config['schema'];
+ }
+
+ $tables = array();
+
+ if (!$this->_isI5) {
+ if ($schema) {
+ $stmt = db2_tables($this->_connection, null, $schema);
+ } else {
+ $stmt = db2_tables($this->_connection);
+ }
+ while ($row = db2_fetch_assoc($stmt)) {
+ $tables[] = $row['TABLE_NAME'];
+ }
+ } else {
+ $tables = $this->_i5listTables($schema);
+ }
+
+ return $tables;
+ }
+
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * DB2 not supports UNSIGNED integer.
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ // Ensure the connection is made so that _isI5 is set
+ $this->_connect();
+
+ if ($schemaName === null && $this->_config['schema'] != null) {
+ $schemaName = $this->_config['schema'];
+ }
+
+ if (!$this->_isI5) {
+
+ $sql = "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno,
+ c.typename, c.default, c.nulls, c.length, c.scale,
+ c.identity, tc.type AS tabconsttype, k.colseq
+ FROM syscat.columns c
+ LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
+ ON (k.tabschema = tc.tabschema
+ AND k.tabname = tc.tabname
+ AND tc.type = 'P'))
+ ON (c.tabschema = k.tabschema
+ AND c.tabname = k.tabname
+ AND c.colname = k.colname)
+ WHERE "
+ . $this->quoteInto('UPPER(c.tabname) = UPPER(?)', $tableName);
+
+ if ($schemaName) {
+ $sql .= $this->quoteInto(' AND UPPER(c.tabschema) = UPPER(?)', $schemaName);
+ }
+
+ $sql .= " ORDER BY c.colno";
+
+ } else {
+
+ // DB2 On I5 specific query
+ $sql = "SELECT DISTINCT C.TABLE_SCHEMA, C.TABLE_NAME, C.COLUMN_NAME, C.ORDINAL_POSITION,
+ C.DATA_TYPE, C.COLUMN_DEFAULT, C.NULLS ,C.LENGTH, C.SCALE, LEFT(C.IDENTITY,1),
+ LEFT(tc.TYPE, 1) AS tabconsttype, k.COLSEQ
+ FROM QSYS2.SYSCOLUMNS C
+ LEFT JOIN (QSYS2.syskeycst k JOIN QSYS2.SYSCST tc
+ ON (k.TABLE_SCHEMA = tc.TABLE_SCHEMA
+ AND k.TABLE_NAME = tc.TABLE_NAME
+ AND LEFT(tc.type,1) = 'P'))
+ ON (C.TABLE_SCHEMA = k.TABLE_SCHEMA
+ AND C.TABLE_NAME = k.TABLE_NAME
+ AND C.COLUMN_NAME = k.COLUMN_NAME)
+ WHERE "
+ . $this->quoteInto('UPPER(C.TABLE_NAME) = UPPER(?)', $tableName);
+
+ if ($schemaName) {
+ $sql .= $this->quoteInto(' AND UPPER(C.TABLE_SCHEMA) = UPPER(?)', $schemaName);
+ }
+
+ $sql .= " ORDER BY C.ORDINAL_POSITION FOR FETCH ONLY";
+ }
+
+ $desc = array();
+ $stmt = $this->query($sql);
+
+ /**
+ * To avoid case issues, fetch using FETCH_NUM
+ */
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ /**
+ * The ordering of columns is defined by the query so we can map
+ * to variables to improve readability
+ */
+ $tabschema = 0;
+ $tabname = 1;
+ $colname = 2;
+ $colno = 3;
+ $typename = 4;
+ $default = 5;
+ $nulls = 6;
+ $length = 7;
+ $scale = 8;
+ $identityCol = 9;
+ $tabconstType = 10;
+ $colseq = 11;
+
+ foreach ($result as $key => $row) {
+ list ($primary, $primaryPosition, $identity) = array(false, null, false);
+ if ($row[$tabconstType] == 'P') {
+ $primary = true;
+ $primaryPosition = $row[$colseq];
+ }
+ /**
+ * In IBM DB2, an column can be IDENTITY
+ * even if it is not part of the PRIMARY KEY.
+ */
+ if ($row[$identityCol] == 'Y') {
+ $identity = true;
+ }
+
+ // only colname needs to be case adjusted
+ $desc[$this->foldCase($row[$colname])] = array(
+ 'SCHEMA_NAME' => $this->foldCase($row[$tabschema]),
+ 'TABLE_NAME' => $this->foldCase($row[$tabname]),
+ 'COLUMN_NAME' => $this->foldCase($row[$colname]),
+ 'COLUMN_POSITION' => (!$this->_isI5) ? $row[$colno]+1 : $row[$colno],
+ 'DATA_TYPE' => $row[$typename],
+ 'DEFAULT' => $row[$default],
+ 'NULLABLE' => (bool) ($row[$nulls] == 'Y'),
+ 'LENGTH' => $row[$length],
+ 'SCALE' => $row[$scale],
+ 'PRECISION' => ($row[$typename] == 'DECIMAL' ? $row[$length] : 0),
+ 'UNSIGNED' => false,
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+
+ return $desc;
+ }
+
+ /**
+ * Return the most recent value from the specified sequence in the database.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ $this->_connect();
+
+ if (!$this->_isI5) {
+ $quotedSequenceName = $this->quoteIdentifier($sequenceName, true);
+ $sql = 'SELECT PREVVAL FOR ' . $quotedSequenceName . ' AS VAL FROM SYSIBM.SYSDUMMY1';
+ } else {
+ $quotedSequenceName = $sequenceName;
+ $sql = 'SELECT PREVVAL FOR ' . $this->quoteIdentifier($sequenceName, true) . ' AS VAL FROM QSYS2.QSQPTABL';
+ }
+
+ $value = $this->fetchOne($sql);
+ return (string) $value;
+ }
+
+ /**
+ * Generate a new value from the specified sequence in the database, and return it.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ $this->_connect();
+ $sql = 'SELECT NEXTVAL FOR '.$this->quoteIdentifier($sequenceName, true).' AS VAL FROM SYSIBM.SYSDUMMY1';
+ $value = $this->fetchOne($sql);
+ return (string) $value;
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * The IDENTITY_VAL_LOCAL() function gives the last generated identity value
+ * in the current process, even if it was for a GENERATED column.
+ *
+ * @param string $tableName OPTIONAL
+ * @param string $primaryKey OPTIONAL
+ * @param string $idType OPTIONAL used for i5 platform to define sequence/idenity unique value
+ * @return string
+ */
+
+ public function lastInsertId($tableName = null, $primaryKey = null, $idType = null)
+ {
+ $this->_connect();
+
+ if ($this->_isI5) {
+ return (string) $this->_i5LastInsertId($tableName, $idType);
+ }
+
+ if ($tableName !== null) {
+ $sequenceName = $tableName;
+ if ($primaryKey) {
+ $sequenceName .= "_$primaryKey";
+ }
+ $sequenceName .= '_seq';
+ return $this->lastSequenceId($sequenceName);
+ }
+
+ $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM SYSIBM.SYSDUMMY1';
+ $value = $this->fetchOne($sql);
+ return (string) $value;
+ }
+
+ /**
+ * Begin a transaction.
+ *
+ * @return void
+ */
+ protected function _beginTransaction()
+ {
+ $this->_setExecuteMode(DB2_AUTOCOMMIT_OFF);
+ }
+
+ /**
+ * Commit a transaction.
+ *
+ * @return void
+ */
+ protected function _commit()
+ {
+ if (!db2_commit($this->_connection)) {
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception(
+ db2_conn_errormsg($this->_connection),
+ db2_conn_error($this->_connection));
+ }
+
+ $this->_setExecuteMode(DB2_AUTOCOMMIT_ON);
+ }
+
+ /**
+ * Rollback a transaction.
+ *
+ * @return void
+ */
+ protected function _rollBack()
+ {
+ if (!db2_rollback($this->_connection)) {
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception(
+ db2_conn_errormsg($this->_connection),
+ db2_conn_error($this->_connection));
+ }
+ $this->_setExecuteMode(DB2_AUTOCOMMIT_ON);
+ }
+
+ /**
+ * Set the fetch mode.
+ *
+ * @param integer $mode
+ * @return void
+ * @throws Zend_Db_Adapter_Db2_Exception
+ */
+ public function setFetchMode($mode)
+ {
+ switch ($mode) {
+ case Zend_Db::FETCH_NUM: // seq array
+ case Zend_Db::FETCH_ASSOC: // assoc array
+ case Zend_Db::FETCH_BOTH: // seq+assoc array
+ case Zend_Db::FETCH_OBJ: // object
+ $this->_fetchMode = $mode;
+ break;
+ case Zend_Db::FETCH_BOUND: // bound to PHP variable
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception('FETCH_BOUND is not supported yet');
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception("Invalid fetch mode '$mode' specified");
+ break;
+ }
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /**
+ * @see Zend_Db_Adapter_Db2_Exception
+ */
+ require_once 'Zend/Db/Adapter/Db2/Exception.php';
+ throw new Zend_Db_Adapter_Db2_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ if ($offset == 0) {
+ $limit_sql = $sql . " FETCH FIRST $count ROWS ONLY";
+ return $limit_sql;
+ }
+
+ /**
+ * DB2 does not implement the LIMIT clause as some RDBMS do.
+ * We have to simulate it with subqueries and ROWNUM.
+ * Unfortunately because we use the column wildcard "*",
+ * this puts an extra column into the query result set.
+ */
+ $limit_sql = "SELECT z2.*
+ FROM (
+ SELECT ROW_NUMBER() OVER() AS \"ZEND_DB_ROWNUM\", z1.*
+ FROM (
+ " . $sql . "
+ ) z1
+ ) z2
+ WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
+ return $limit_sql;
+ }
+
+ /**
+ * Check if the adapter supports real SQL parameters.
+ *
+ * @param string $type 'positional' or 'named'
+ * @return bool
+ */
+ public function supportsParameters($type)
+ {
+ if ($type == 'positional') {
+ return true;
+ }
+
+ // if its 'named' or anything else
+ return false;
+ }
+
+ /**
+ * Retrieve server version in PHP style
+ *
+ * @return string
+ */
+ public function getServerVersion()
+ {
+ $this->_connect();
+ $server_info = db2_server_info($this->_connection);
+ if ($server_info !== false) {
+ $version = $server_info->DBMS_VER;
+ if ($this->_isI5) {
+ $version = (int) substr($version, 0, 2) . '.' . (int) substr($version, 2, 2) . '.' . (int) substr($version, 4);
+ }
+ return $version;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return whether or not this is running on i5
+ *
+ * @return bool
+ */
+ public function isI5()
+ {
+ if ($this->_isI5 === null) {
+ $this->_determineI5();
+ }
+
+ return (bool) $this->_isI5;
+ }
+
+ /**
+ * Check the connection parameters according to verify
+ * type of used OS
+ *
+ * @return void
+ */
+ protected function _determineI5()
+ {
+ // first us the compiled flag.
+ $this->_isI5 = (php_uname('s') == 'OS400') ? true : false;
+
+ // if this is set, then us it
+ if (isset($this->_config['os'])){
+ if (strtolower($this->_config['os']) === 'i5') {
+ $this->_isI5 = true;
+ } else {
+ // any other value passed in, its null
+ $this->_isI5 = false;
+ }
+ }
+
+ }
+
+ /**
+ * Db2 On I5 specific method
+ *
+ * Returns a list of the tables in the database .
+ * Used only for DB2/400.
+ *
+ * @return array
+ */
+ protected function _i5listTables($schema = null)
+ {
+ //list of i5 libraries.
+ $tables = array();
+ if ($schema) {
+ $tablesStatement = db2_tables($this->_connection, null, $schema);
+ while ($rowTables = db2_fetch_assoc($tablesStatement) ) {
+ if ($rowTables['TABLE_NAME'] !== null) {
+ $tables[] = $rowTables['TABLE_NAME'];
+ }
+ }
+ } else {
+ $schemaStatement = db2_tables($this->_connection);
+ while ($schema = db2_fetch_assoc($schemaStatement)) {
+ if ($schema['TABLE_SCHEM'] !== null) {
+ // list of the tables which belongs to the selected library
+ $tablesStatement = db2_tables($this->_connection, NULL, $schema['TABLE_SCHEM']);
+ if (is_resource($tablesStatement)) {
+ while ($rowTables = db2_fetch_assoc($tablesStatement) ) {
+ if ($rowTables['TABLE_NAME'] !== null) {
+ $tables[] = $rowTables['TABLE_NAME'];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $tables;
+ }
+
+ protected function _i5LastInsertId($objectName = null, $idType = null)
+ {
+
+ if ($objectName === null) {
+ $sql = 'SELECT IDENTITY_VAL_LOCAL() AS VAL FROM QSYS2.QSQPTABL';
+ $value = $this->fetchOne($sql);
+ return $value;
+ }
+
+ if (strtoupper($idType) === 'S'){
+ //check i5_lib option
+ $sequenceName = $objectName;
+ return $this->lastSequenceId($sequenceName);
+ }
+
+ //returns last identity value for the specified table
+ //if (strtoupper($idType) === 'I') {
+ $tableName = $objectName;
+ return $this->fetchOne('SELECT IDENTITY_VAL_LOCAL() from ' . $this->quoteIdentifier($tableName));
+ }
+
+}
+
+
diff --git a/library/Zend/Db/Adapter/Db2/Exception.php b/library/Zend/Db/Adapter/Db2/Exception.php
new file mode 100644
index 0000000..dc43171
--- /dev/null
+++ b/library/Zend/Db/Adapter/Db2/Exception.php
@@ -0,0 +1,45 @@
+getCode();
+ }
+ parent::__construct($message, $code, $e);
+ }
+
+ public function hasChainedException()
+ {
+ return ($this->_previous !== null);
+ }
+
+ public function getChainedException()
+ {
+ return $this->getPrevious();
+ }
+
+}
diff --git a/library/Zend/Db/Adapter/Mysqli.php b/library/Zend/Db/Adapter/Mysqli.php
new file mode 100644
index 0000000..4fa3620
--- /dev/null
+++ b/library/Zend/Db/Adapter/Mysqli.php
@@ -0,0 +1,549 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INT' => Zend_Db::INT_TYPE,
+ 'INTEGER' => Zend_Db::INT_TYPE,
+ 'MEDIUMINT' => Zend_Db::INT_TYPE,
+ 'SMALLINT' => Zend_Db::INT_TYPE,
+ 'TINYINT' => Zend_Db::INT_TYPE,
+ 'BIGINT' => Zend_Db::BIGINT_TYPE,
+ 'SERIAL' => Zend_Db::BIGINT_TYPE,
+ 'DEC' => Zend_Db::FLOAT_TYPE,
+ 'DECIMAL' => Zend_Db::FLOAT_TYPE,
+ 'DOUBLE' => Zend_Db::FLOAT_TYPE,
+ 'DOUBLE PRECISION' => Zend_Db::FLOAT_TYPE,
+ 'FIXED' => Zend_Db::FLOAT_TYPE,
+ 'FLOAT' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * @var Zend_Db_Statement_Mysqli
+ */
+ protected $_stmt = null;
+
+ /**
+ * Default class name for a DB statement.
+ *
+ * @var string
+ */
+ protected $_defaultStmtClass = 'Zend_Db_Statement_Mysqli';
+
+ /**
+ * Quote a raw string.
+ *
+ * @param mixed $value Raw string
+ *
+ * @return string Quoted string
+ */
+ protected function _quote($value)
+ {
+ if (is_int($value) || is_float($value)) {
+ return $value;
+ }
+ $this->_connect();
+ return "'" . $this->_connection->real_escape_string($value) . "'";
+ }
+
+ /**
+ * Returns the symbol the adapter uses for delimiting identifiers.
+ *
+ * @return string
+ */
+ public function getQuoteIdentifierSymbol()
+ {
+ return "`";
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $result = array();
+ // Use mysqli extension API, because SHOW doesn't work
+ // well as a prepared statement on MySQL 4.1.
+ $sql = 'SHOW TABLES';
+ if ($queryResult = $this->getConnection()->query($sql)) {
+ while ($row = $queryResult->fetch_row()) {
+ $result[] = $row[0];
+ }
+ $queryResult->close();
+ } else {
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception($this->getConnection()->error);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ /**
+ * @todo use INFORMATION_SCHEMA someday when
+ * MySQL's implementation isn't too slow.
+ */
+
+ if ($schemaName) {
+ $sql = 'DESCRIBE ' . $this->quoteIdentifier("$schemaName.$tableName", true);
+ } else {
+ $sql = 'DESCRIBE ' . $this->quoteIdentifier($tableName, true);
+ }
+
+ /**
+ * Use mysqli extension API, because DESCRIBE doesn't work
+ * well as a prepared statement on MySQL 4.1.
+ */
+ if ($queryResult = $this->getConnection()->query($sql)) {
+ while ($row = $queryResult->fetch_assoc()) {
+ $result[] = $row;
+ }
+ $queryResult->close();
+ } else {
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception($this->getConnection()->error);
+ }
+
+ $desc = array();
+
+ $row_defaults = array(
+ 'Length' => null,
+ 'Scale' => null,
+ 'Precision' => null,
+ 'Unsigned' => null,
+ 'Primary' => false,
+ 'PrimaryPosition' => null,
+ 'Identity' => false
+ );
+ $i = 1;
+ $p = 1;
+ foreach ($result as $key => $row) {
+ $row = array_merge($row_defaults, $row);
+ if (preg_match('/unsigned/', $row['Type'])) {
+ $row['Unsigned'] = true;
+ }
+ if (preg_match('/^((?:var)?char)\((\d+)\)/', $row['Type'], $matches)) {
+ $row['Type'] = $matches[1];
+ $row['Length'] = $matches[2];
+ } else if (preg_match('/^decimal\((\d+),(\d+)\)/', $row['Type'], $matches)) {
+ $row['Type'] = 'decimal';
+ $row['Precision'] = $matches[1];
+ $row['Scale'] = $matches[2];
+ } else if (preg_match('/^float\((\d+),(\d+)\)/', $row['Type'], $matches)) {
+ $row['Type'] = 'float';
+ $row['Precision'] = $matches[1];
+ $row['Scale'] = $matches[2];
+ } else if (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)/', $row['Type'], $matches)) {
+ $row['Type'] = $matches[1];
+ /**
+ * The optional argument of a MySQL int type is not precision
+ * or length; it is only a hint for display width.
+ */
+ }
+ if (strtoupper($row['Key']) == 'PRI') {
+ $row['Primary'] = true;
+ $row['PrimaryPosition'] = $p;
+ if ($row['Extra'] == 'auto_increment') {
+ $row['Identity'] = true;
+ } else {
+ $row['Identity'] = false;
+ }
+ ++$p;
+ }
+ $desc[$this->foldCase($row['Field'])] = array(
+ 'SCHEMA_NAME' => null, // @todo
+ 'TABLE_NAME' => $this->foldCase($tableName),
+ 'COLUMN_NAME' => $this->foldCase($row['Field']),
+ 'COLUMN_POSITION' => $i,
+ 'DATA_TYPE' => $row['Type'],
+ 'DEFAULT' => $row['Default'],
+ 'NULLABLE' => (bool) ($row['Null'] == 'YES'),
+ 'LENGTH' => $row['Length'],
+ 'SCALE' => $row['Scale'],
+ 'PRECISION' => $row['Precision'],
+ 'UNSIGNED' => $row['Unsigned'],
+ 'PRIMARY' => $row['Primary'],
+ 'PRIMARY_POSITION' => $row['PrimaryPosition'],
+ 'IDENTITY' => $row['Identity']
+ );
+ ++$i;
+ }
+ return $desc;
+ }
+
+ /**
+ * Creates a connection to the database.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Mysqli_Exception
+ */
+ protected function _connect()
+ {
+ if ($this->_connection) {
+ return;
+ }
+
+ if (!extension_loaded('mysqli')) {
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception('The Mysqli extension is required for this adapter but the extension is not loaded');
+ }
+
+ if (isset($this->_config['port'])) {
+ $port = (integer) $this->_config['port'];
+ } else {
+ $port = null;
+ }
+
+ $this->_connection = mysqli_init();
+
+ if(!empty($this->_config['driver_options'])) {
+ foreach($this->_config['driver_options'] as $option=>$value) {
+ if(is_string($option)) {
+ // Suppress warnings here
+ // Ignore it if it's not a valid constant
+ $option = @constant(strtoupper($option));
+ if($option === null)
+ continue;
+ }
+ mysqli_options($this->_connection, $option, $value);
+ }
+ }
+
+ // Suppress connection warnings here.
+ // Throw an exception instead.
+ $_isConnected = @mysqli_real_connect(
+ $this->_connection,
+ $this->_config['host'],
+ $this->_config['username'],
+ $this->_config['password'],
+ $this->_config['dbname'],
+ $port
+ );
+
+ if ($_isConnected === false || mysqli_connect_errno()) {
+
+ $this->closeConnection();
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception(mysqli_connect_error());
+ }
+
+ if (!empty($this->_config['charset'])) {
+ mysqli_set_charset($this->_connection, $this->_config['charset']);
+ }
+ }
+
+ /**
+ * Test if a connection is active
+ *
+ * @return boolean
+ */
+ public function isConnected()
+ {
+ return ((bool) ($this->_connection instanceof mysqli));
+ }
+
+ /**
+ * Force the connection to close.
+ *
+ * @return void
+ */
+ public function closeConnection()
+ {
+ if ($this->isConnected()) {
+ $this->_connection->close();
+ }
+ $this->_connection = null;
+ }
+
+ /**
+ * Prepare a statement and return a PDOStatement-like object.
+ *
+ * @param string $sql SQL query
+ * @return Zend_Db_Statement_Mysqli
+ */
+ public function prepare($sql)
+ {
+ $this->_connect();
+ if ($this->_stmt) {
+ $this->_stmt->close();
+ }
+ $stmtClass = $this->_defaultStmtClass;
+ if (!class_exists($stmtClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($stmtClass);
+ }
+ $stmt = new $stmtClass($this, $sql);
+ if ($stmt === false) {
+ return false;
+ }
+ $stmt->setFetchMode($this->_fetchMode);
+ $this->_stmt = $stmt;
+ return $stmt;
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * MySQL does not support sequences, so $tableName and $primaryKey are ignored.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ * @todo Return value should be int?
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ $mysqli = $this->_connection;
+ return (string) $mysqli->insert_id;
+ }
+
+ /**
+ * Begin a transaction.
+ *
+ * @return void
+ */
+ protected function _beginTransaction()
+ {
+ $this->_connect();
+ $this->_connection->autocommit(false);
+ }
+
+ /**
+ * Commit a transaction.
+ *
+ * @return void
+ */
+ protected function _commit()
+ {
+ $this->_connect();
+ $this->_connection->commit();
+ $this->_connection->autocommit(true);
+ }
+
+ /**
+ * Roll-back a transaction.
+ *
+ * @return void
+ */
+ protected function _rollBack()
+ {
+ $this->_connect();
+ $this->_connection->rollback();
+ $this->_connection->autocommit(true);
+ }
+
+ /**
+ * Set the fetch mode.
+ *
+ * @param int $mode
+ * @return void
+ * @throws Zend_Db_Adapter_Mysqli_Exception
+ */
+ public function setFetchMode($mode)
+ {
+ switch ($mode) {
+ case Zend_Db::FETCH_LAZY:
+ case Zend_Db::FETCH_ASSOC:
+ case Zend_Db::FETCH_NUM:
+ case Zend_Db::FETCH_BOTH:
+ case Zend_Db::FETCH_NAMED:
+ case Zend_Db::FETCH_OBJ:
+ $this->_fetchMode = $mode;
+ break;
+ case Zend_Db::FETCH_BOUND: // bound to PHP variable
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception('FETCH_BOUND is not supported yet');
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception("Invalid fetch mode '$mode' specified");
+ }
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param int $count
+ * @param int $offset OPTIONAL
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /**
+ * @see Zend_Db_Adapter_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Adapter/Mysqli/Exception.php';
+ throw new Zend_Db_Adapter_Mysqli_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ $sql .= " LIMIT $count";
+ if ($offset > 0) {
+ $sql .= " OFFSET $offset";
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Check if the adapter supports real SQL parameters.
+ *
+ * @param string $type 'positional' or 'named'
+ * @return bool
+ */
+ public function supportsParameters($type)
+ {
+ switch ($type) {
+ case 'positional':
+ return true;
+ case 'named':
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Retrieve server version in PHP style
+ *
+ *@return string
+ */
+ public function getServerVersion()
+ {
+ $this->_connect();
+ $version = $this->_connection->server_version;
+ $major = (int) ($version / 10000);
+ $minor = (int) ($version % 10000 / 100);
+ $revision = (int) ($version % 100);
+ return $major . '.' . $minor . '.' . $revision;
+ }
+}
diff --git a/library/Zend/Db/Adapter/Mysqli/Exception.php b/library/Zend/Db/Adapter/Mysqli/Exception.php
new file mode 100644
index 0000000..95f09f1
--- /dev/null
+++ b/library/Zend/Db/Adapter/Mysqli/Exception.php
@@ -0,0 +1,40 @@
+ (string) Connect to the database as this username.
+ * password => (string) Password associated with the username.
+ * dbname => Either the name of the local Oracle instance, or the
+ * name of the entry in tnsnames.ora to which you want to connect.
+ * persistent => (boolean) Set TRUE to use a persistent connection
+ * @var array
+ */
+ protected $_config = array(
+ 'dbname' => null,
+ 'username' => null,
+ 'password' => null,
+ 'persistent' => false
+ );
+
+ /**
+ * Keys are UPPERCASE SQL datatypes or the constants
+ * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
+ *
+ * Values are:
+ * 0 = 32-bit integer
+ * 1 = 64-bit integer
+ * 2 = float or decimal
+ *
+ * @var array Associative array of datatypes to values 0, 1, or 2.
+ */
+ protected $_numericDataTypes = array(
+ Zend_Db::INT_TYPE => Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'BINARY_DOUBLE' => Zend_Db::FLOAT_TYPE,
+ 'BINARY_FLOAT' => Zend_Db::FLOAT_TYPE,
+ 'NUMBER' => Zend_Db::FLOAT_TYPE,
+ );
+
+ /**
+ * @var integer
+ */
+ protected $_execute_mode = null;
+
+ /**
+ * Default class name for a DB statement.
+ *
+ * @var string
+ */
+ protected $_defaultStmtClass = 'Zend_Db_Statement_Oracle';
+
+ /**
+ * Check if LOB field are returned as string
+ * instead of OCI-Lob object
+ *
+ * @var boolean
+ */
+ protected $_lobAsString = null;
+
+ /**
+ * Creates a connection resource.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Oracle_Exception
+ */
+ protected function _connect()
+ {
+ if (is_resource($this->_connection)) {
+ // connection already exists
+ return;
+ }
+
+ if (!extension_loaded('oci8')) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception('The OCI8 extension is required for this adapter but the extension is not loaded');
+ }
+
+ $this->_setExecuteMode(OCI_COMMIT_ON_SUCCESS);
+
+ $connectionFuncName = ($this->_config['persistent'] == true) ? 'oci_pconnect' : 'oci_connect';
+
+ $this->_connection = @$connectionFuncName(
+ $this->_config['username'],
+ $this->_config['password'],
+ $this->_config['dbname'],
+ $this->_config['charset']);
+
+ // check the connection
+ if (!$this->_connection) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception(oci_error());
+ }
+ }
+
+ /**
+ * Test if a connection is active
+ *
+ * @return boolean
+ */
+ public function isConnected()
+ {
+ return ((bool) (is_resource($this->_connection)
+ && (get_resource_type($this->_connection) == 'oci8 connection'
+ || get_resource_type($this->_connection) == 'oci8 persistent connection')));
+ }
+
+ /**
+ * Force the connection to close.
+ *
+ * @return void
+ */
+ public function closeConnection()
+ {
+ if ($this->isConnected()) {
+ oci_close($this->_connection);
+ }
+ $this->_connection = null;
+ }
+
+ /**
+ * Activate/deactivate return of LOB as string
+ *
+ * @param string $lob_as_string
+ * @return Zend_Db_Adapter_Oracle
+ */
+ public function setLobAsString($lobAsString)
+ {
+ $this->_lobAsString = (bool) $lobAsString;
+ return $this;
+ }
+
+ /**
+ * Return whether or not LOB are returned as string
+ *
+ * @return boolean
+ */
+ public function getLobAsString()
+ {
+ if ($this->_lobAsString === null) {
+ // if never set by user, we use driver option if it exists otherwise false
+ if (isset($this->_config['driver_options']) &&
+ isset($this->_config['driver_options']['lob_as_string'])) {
+ $this->_lobAsString = (bool) $this->_config['driver_options']['lob_as_string'];
+ } else {
+ $this->_lobAsString = false;
+ }
+ }
+ return $this->_lobAsString;
+ }
+
+ /**
+ * Returns an SQL statement for preparation.
+ *
+ * @param string $sql The SQL statement with placeholders.
+ * @return Zend_Db_Statement_Oracle
+ */
+ public function prepare($sql)
+ {
+ $this->_connect();
+ $stmtClass = $this->_defaultStmtClass;
+ if (!class_exists($stmtClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($stmtClass);
+ }
+ $stmt = new $stmtClass($this, $sql);
+ if ($stmt instanceof Zend_Db_Statement_Oracle) {
+ $stmt->setLobAsString($this->getLobAsString());
+ }
+ $stmt->setFetchMode($this->_fetchMode);
+ return $stmt;
+ }
+
+ /**
+ * Quote a raw string.
+ *
+ * @param string $value Raw string
+ * @return string Quoted string
+ */
+ protected function _quote($value)
+ {
+ if (is_int($value) || is_float($value)) {
+ return $value;
+ }
+ $value = str_replace("'", "''", $value);
+ return "'" . addcslashes($value, "\000\n\r\\\032") . "'";
+ }
+
+ /**
+ * Quote a table identifier and alias.
+ *
+ * @param string|array|Zend_Db_Expr $ident The identifier or expression.
+ * @param string $alias An alias for the table.
+ * @param boolean $auto If true, heed the AUTO_QUOTE_IDENTIFIERS config option.
+ * @return string The quoted identifier and alias.
+ */
+ public function quoteTableAs($ident, $alias = null, $auto = false)
+ {
+ // Oracle doesn't allow the 'AS' keyword between the table identifier/expression and alias.
+ return $this->_quoteIdentifierAs($ident, $alias, $auto, ' ');
+ }
+
+ /**
+ * Return the most recent value from the specified sequence in the database.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ $this->_connect();
+ $sql = 'SELECT '.$this->quoteIdentifier($sequenceName, true).'.CURRVAL FROM dual';
+ $value = $this->fetchOne($sql);
+ return $value;
+ }
+
+ /**
+ * Generate a new value from the specified sequence in the database, and return it.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ $this->_connect();
+ $sql = 'SELECT '.$this->quoteIdentifier($sequenceName, true).'.NEXTVAL FROM dual';
+ $value = $this->fetchOne($sql);
+ return $value;
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * Oracle does not support IDENTITY columns, so if the sequence is not
+ * specified, this method returns null.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ if ($tableName !== null) {
+ $sequenceName = $tableName;
+ if ($primaryKey) {
+ $sequenceName .= "_$primaryKey";
+ }
+ $sequenceName .= '_seq';
+ return $this->lastSequenceId($sequenceName);
+ }
+
+ // No support for IDENTITY columns; return null
+ return null;
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $this->_connect();
+ $data = $this->fetchCol('SELECT table_name FROM all_tables');
+ return $data;
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @todo Discover integer unsigned property.
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ $version = $this->getServerVersion();
+ if (($version === null) || version_compare($version, '9.0.0', '>=')) {
+ $sql = "SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE,
+ TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH,
+ TC.DATA_SCALE, TC.DATA_PRECISION, C.CONSTRAINT_TYPE, CC.POSITION
+ FROM ALL_TAB_COLUMNS TC
+ LEFT JOIN (ALL_CONS_COLUMNS CC JOIN ALL_CONSTRAINTS C
+ ON (CC.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND CC.TABLE_NAME = C.TABLE_NAME AND CC.OWNER = C.OWNER AND C.CONSTRAINT_TYPE = 'P'))
+ ON TC.TABLE_NAME = CC.TABLE_NAME AND TC.COLUMN_NAME = CC.COLUMN_NAME
+ WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)";
+ $bind[':TBNAME'] = $tableName;
+ if ($schemaName) {
+ $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)';
+ $bind[':SCNAME'] = $schemaName;
+ }
+ $sql .= ' ORDER BY TC.COLUMN_ID';
+ } else {
+ $subSql="SELECT AC.OWNER, AC.TABLE_NAME, ACC.COLUMN_NAME, AC.CONSTRAINT_TYPE, ACC.POSITION
+ from ALL_CONSTRAINTS AC, ALL_CONS_COLUMNS ACC
+ WHERE ACC.CONSTRAINT_NAME = AC.CONSTRAINT_NAME
+ AND ACC.TABLE_NAME = AC.TABLE_NAME
+ AND ACC.OWNER = AC.OWNER
+ AND AC.CONSTRAINT_TYPE = 'P'
+ AND UPPER(AC.TABLE_NAME) = UPPER(:TBNAME)";
+ $bind[':TBNAME'] = $tableName;
+ if ($schemaName) {
+ $subSql .= ' AND UPPER(ACC.OWNER) = UPPER(:SCNAME)';
+ $bind[':SCNAME'] = $schemaName;
+ }
+ $sql="SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE,
+ TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH,
+ TC.DATA_SCALE, TC.DATA_PRECISION, CC.CONSTRAINT_TYPE, CC.POSITION
+ FROM ALL_TAB_COLUMNS TC, ($subSql) CC
+ WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)
+ AND TC.OWNER = CC.OWNER(+) AND TC.TABLE_NAME = CC.TABLE_NAME(+) AND TC.COLUMN_NAME = CC.COLUMN_NAME(+)";
+ if ($schemaName) {
+ $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)';
+ }
+ $sql .= ' ORDER BY TC.COLUMN_ID';
+ }
+
+ $stmt = $this->query($sql, $bind);
+
+ /**
+ * Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection
+ */
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ $table_name = 0;
+ $owner = 1;
+ $column_name = 2;
+ $data_type = 3;
+ $data_default = 4;
+ $nullable = 5;
+ $column_id = 6;
+ $data_length = 7;
+ $data_scale = 8;
+ $data_precision = 9;
+ $constraint_type = 10;
+ $position = 11;
+
+ $desc = array();
+ foreach ($result as $key => $row) {
+ list ($primary, $primaryPosition, $identity) = array(false, null, false);
+ if ($row[$constraint_type] == 'P') {
+ $primary = true;
+ $primaryPosition = $row[$position];
+ /**
+ * Oracle does not support auto-increment keys.
+ */
+ $identity = false;
+ }
+ $desc[$this->foldCase($row[$column_name])] = array(
+ 'SCHEMA_NAME' => $this->foldCase($row[$owner]),
+ 'TABLE_NAME' => $this->foldCase($row[$table_name]),
+ 'COLUMN_NAME' => $this->foldCase($row[$column_name]),
+ 'COLUMN_POSITION' => $row[$column_id],
+ 'DATA_TYPE' => $row[$data_type],
+ 'DEFAULT' => $row[$data_default],
+ 'NULLABLE' => (bool) ($row[$nullable] == 'Y'),
+ 'LENGTH' => $row[$data_length],
+ 'SCALE' => $row[$data_scale],
+ 'PRECISION' => $row[$data_precision],
+ 'UNSIGNED' => null, // @todo
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+ return $desc;
+ }
+
+ /**
+ * Leave autocommit mode and begin a transaction.
+ *
+ * @return void
+ */
+ protected function _beginTransaction()
+ {
+ $this->_setExecuteMode(OCI_DEFAULT);
+ }
+
+ /**
+ * Commit a transaction and return to autocommit mode.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Oracle_Exception
+ */
+ protected function _commit()
+ {
+ if (!oci_commit($this->_connection)) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception(oci_error($this->_connection));
+ }
+ $this->_setExecuteMode(OCI_COMMIT_ON_SUCCESS);
+ }
+
+ /**
+ * Roll back a transaction and return to autocommit mode.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Oracle_Exception
+ */
+ protected function _rollBack()
+ {
+ if (!oci_rollback($this->_connection)) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception(oci_error($this->_connection));
+ }
+ $this->_setExecuteMode(OCI_COMMIT_ON_SUCCESS);
+ }
+
+ /**
+ * Set the fetch mode.
+ *
+ * @todo Support FETCH_CLASS and FETCH_INTO.
+ *
+ * @param integer $mode A fetch mode.
+ * @return void
+ * @throws Zend_Db_Adapter_Oracle_Exception
+ */
+ public function setFetchMode($mode)
+ {
+ switch ($mode) {
+ case Zend_Db::FETCH_NUM: // seq array
+ case Zend_Db::FETCH_ASSOC: // assoc array
+ case Zend_Db::FETCH_BOTH: // seq+assoc array
+ case Zend_Db::FETCH_OBJ: // object
+ $this->_fetchMode = $mode;
+ break;
+ case Zend_Db::FETCH_BOUND: // bound to PHP variable
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception('FETCH_BOUND is not supported yet');
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception("Invalid fetch mode '$mode' specified");
+ break;
+ }
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @return string
+ * @throws Zend_Db_Adapter_Oracle_Exception
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ /**
+ * Oracle does not implement the LIMIT clause as some RDBMS do.
+ * We have to simulate it with subqueries and ROWNUM.
+ * Unfortunately because we use the column wildcard "*",
+ * this puts an extra column into the query result set.
+ */
+ $limit_sql = "SELECT z2.*
+ FROM (
+ SELECT z1.*, ROWNUM AS \"zend_db_rownum\"
+ FROM (
+ " . $sql . "
+ ) z1
+ ) z2
+ WHERE z2.\"zend_db_rownum\" BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
+ return $limit_sql;
+ }
+
+ /**
+ * @param integer $mode
+ * @throws Zend_Db_Adapter_Oracle_Exception
+ */
+ private function _setExecuteMode($mode)
+ {
+ switch($mode) {
+ case OCI_COMMIT_ON_SUCCESS:
+ case OCI_DEFAULT:
+ case OCI_DESCRIBE_ONLY:
+ $this->_execute_mode = $mode;
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Adapter/Oracle/Exception.php';
+ throw new Zend_Db_Adapter_Oracle_Exception("Invalid execution mode '$mode' specified");
+ break;
+ }
+ }
+
+ /**
+ * @return int
+ */
+ public function _getExecuteMode()
+ {
+ return $this->_execute_mode;
+ }
+
+ /**
+ * Check if the adapter supports real SQL parameters.
+ *
+ * @param string $type 'positional' or 'named'
+ * @return bool
+ */
+ public function supportsParameters($type)
+ {
+ switch ($type) {
+ case 'named':
+ return true;
+ case 'positional':
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Retrieve server version in PHP style
+ *
+ * @return string
+ */
+ public function getServerVersion()
+ {
+ $this->_connect();
+ $version = oci_server_version($this->_connection);
+ if ($version !== false) {
+ $matches = null;
+ if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
+ return $matches[1];
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/library/Zend/Db/Adapter/Oracle/Exception.php b/library/Zend/Db/Adapter/Oracle/Exception.php
new file mode 100644
index 0000000..6bd64e3
--- /dev/null
+++ b/library/Zend/Db/Adapter/Oracle/Exception.php
@@ -0,0 +1,60 @@
+message = $error['code'] .' '. $error['message'];
+ } else {
+ $this->message = $error['code'] .' '. $error['message']." "
+ . substr($error['sqltext'], 0, $error['offset'])
+ . "*"
+ . substr($error['sqltext'], $error['offset']);
+ }
+ $this->code = $error['code'];
+ } else if (is_string($error)) {
+ $this->message = $error;
+ }
+ if (!$this->code && $code) {
+ $this->code = $code;
+ }
+ }
+}
diff --git a/library/Zend/Db/Adapter/Pdo/Abstract.php b/library/Zend/Db/Adapter/Pdo/Abstract.php
new file mode 100644
index 0000000..6dcc816
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Abstract.php
@@ -0,0 +1,401 @@
+_config settings.
+ *
+ * @return string
+ */
+ protected function _dsn()
+ {
+ // baseline of DSN parts
+ $dsn = $this->_config;
+
+ // don't pass the username, password, charset, persistent and driver_options in the DSN
+ unset($dsn['username']);
+ unset($dsn['password']);
+ unset($dsn['options']);
+ unset($dsn['charset']);
+ unset($dsn['persistent']);
+ unset($dsn['driver_options']);
+
+ // use all remaining parts in the DSN
+ foreach ($dsn as $key => $val) {
+ $dsn[$key] = "$key=$val";
+ }
+
+ return $this->_pdoType . ':' . implode(';', $dsn);
+ }
+
+ /**
+ * Creates a PDO object and connects to the database.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Exception
+ */
+ protected function _connect()
+ {
+ // if we already have a PDO object, no need to re-connect.
+ if ($this->_connection) {
+ return;
+ }
+
+ // get the dsn first, because some adapters alter the $_pdoType
+ $dsn = $this->_dsn();
+
+ // check for PDO extension
+ if (!extension_loaded('pdo')) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
+ }
+
+ // check the PDO driver is available
+ if (!in_array($this->_pdoType, PDO::getAvailableDrivers())) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception('The ' . $this->_pdoType . ' driver is not currently installed');
+ }
+
+ // create PDO connection
+ $q = $this->_profiler->queryStart('connect', Zend_Db_Profiler::CONNECT);
+
+ // add the persistence flag if we find it in our config array
+ if (isset($this->_config['persistent']) && ($this->_config['persistent'] == true)) {
+ $this->_config['driver_options'][PDO::ATTR_PERSISTENT] = true;
+ }
+
+ try {
+ $this->_connection = new PDO(
+ $dsn,
+ $this->_config['username'],
+ $this->_config['password'],
+ $this->_config['driver_options']
+ );
+
+ $this->_profiler->queryEnd($q);
+
+ // set the PDO connection to perform case-folding on array keys, or not
+ $this->_connection->setAttribute(PDO::ATTR_CASE, $this->_caseFolding);
+
+ // always use exceptions.
+ $this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+ } catch (PDOException $e) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+
+ }
+
+ /**
+ * Test if a connection is active
+ *
+ * @return boolean
+ */
+ public function isConnected()
+ {
+ return ((bool) ($this->_connection instanceof PDO));
+ }
+
+ /**
+ * Force the connection to close.
+ *
+ * @return void
+ */
+ public function closeConnection()
+ {
+ $this->_connection = null;
+ }
+
+ /**
+ * Prepares an SQL statement.
+ *
+ * @param string $sql The SQL statement with placeholders.
+ * @param array $bind An array of data to bind to the placeholders.
+ * @return PDOStatement
+ */
+ public function prepare($sql)
+ {
+ $this->_connect();
+ $stmtClass = $this->_defaultStmtClass;
+ if (!class_exists($stmtClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($stmtClass);
+ }
+ $stmt = new $stmtClass($this, $sql);
+ $stmt->setFetchMode($this->_fetchMode);
+ return $stmt;
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * On RDBMS brands that don't support sequences, $tableName and $primaryKey
+ * are ignored.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ $this->_connect();
+ return $this->_connection->lastInsertId();
+ }
+
+ /**
+ * Special handling for PDO query().
+ * All bind parameter names must begin with ':'
+ *
+ * @param string|Zend_Db_Select $sql The SQL statement with placeholders.
+ * @param array $bind An array of data to bind to the placeholders.
+ * @return Zend_Db_Statement_Pdo
+ * @throws Zend_Db_Adapter_Exception To re-throw PDOException.
+ */
+ public function query($sql, $bind = array())
+ {
+ if (empty($bind) && $sql instanceof Zend_Db_Select) {
+ $bind = $sql->getBind();
+ }
+
+ if (is_array($bind)) {
+ foreach ($bind as $name => $value) {
+ if (!is_int($name) && !preg_match('/^:/', $name)) {
+ $newName = ":$name";
+ unset($bind[$name]);
+ $bind[$newName] = $value;
+ }
+ }
+ }
+
+ try {
+ return parent::query($sql, $bind);
+ } catch (PDOException $e) {
+ /**
+ * @see Zend_Db_Statement_Exception
+ */
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Executes an SQL statement and return the number of affected rows
+ *
+ * @param mixed $sql The SQL statement with placeholders.
+ * May be a string or Zend_Db_Select.
+ * @return integer Number of rows that were modified
+ * or deleted by the SQL statement
+ */
+ public function exec($sql)
+ {
+ if ($sql instanceof Zend_Db_Select) {
+ $sql = $sql->assemble();
+ }
+
+ try {
+ $affected = $this->getConnection()->exec($sql);
+
+ if ($affected === false) {
+ $errorInfo = $this->getConnection()->errorInfo();
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception($errorInfo[2]);
+ }
+
+ return $affected;
+ } catch (PDOException $e) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Quote a raw string.
+ *
+ * @param string $value Raw string
+ * @return string Quoted string
+ */
+ protected function _quote($value)
+ {
+ if (is_int($value) || is_float($value)) {
+ return $value;
+ }
+ $this->_connect();
+ return $this->_connection->quote($value);
+ }
+
+ /**
+ * Begin a transaction.
+ */
+ protected function _beginTransaction()
+ {
+ $this->_connect();
+ $this->_connection->beginTransaction();
+ }
+
+ /**
+ * Commit a transaction.
+ */
+ protected function _commit()
+ {
+ $this->_connect();
+ $this->_connection->commit();
+ }
+
+ /**
+ * Roll-back a transaction.
+ */
+ protected function _rollBack() {
+ $this->_connect();
+ $this->_connection->rollBack();
+ }
+
+ /**
+ * Set the PDO fetch mode.
+ *
+ * @todo Support FETCH_CLASS and FETCH_INTO.
+ *
+ * @param int $mode A PDO fetch mode.
+ * @return void
+ * @throws Zend_Db_Adapter_Exception
+ */
+ public function setFetchMode($mode)
+ {
+ //check for PDO extension
+ if (!extension_loaded('pdo')) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception('The PDO extension is required for this adapter but the extension is not loaded');
+ }
+ switch ($mode) {
+ case PDO::FETCH_LAZY:
+ case PDO::FETCH_ASSOC:
+ case PDO::FETCH_NUM:
+ case PDO::FETCH_BOTH:
+ case PDO::FETCH_NAMED:
+ case PDO::FETCH_OBJ:
+ $this->_fetchMode = $mode;
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Invalid fetch mode '$mode' specified");
+ break;
+ }
+ }
+
+ /**
+ * Check if the adapter supports real SQL parameters.
+ *
+ * @param string $type 'positional' or 'named'
+ * @return bool
+ */
+ public function supportsParameters($type)
+ {
+ switch ($type) {
+ case 'positional':
+ case 'named':
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Retrieve server version in PHP style
+ *
+ * @return string
+ */
+ public function getServerVersion()
+ {
+ $this->_connect();
+ try {
+ $version = $this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
+ } catch (PDOException $e) {
+ // In case of the driver doesn't support getting attributes
+ return null;
+ }
+ $matches = null;
+ if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $version, $matches)) {
+ return $matches[1];
+ } else {
+ return null;
+ }
+ }
+}
+
diff --git a/library/Zend/Db/Adapter/Pdo/Ibm.php b/library/Zend/Db/Adapter/Pdo/Ibm.php
new file mode 100644
index 0000000..1769a15
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Ibm.php
@@ -0,0 +1,360 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INTEGER' => Zend_Db::INT_TYPE,
+ 'SMALLINT' => Zend_Db::INT_TYPE,
+ 'BIGINT' => Zend_Db::BIGINT_TYPE,
+ 'DECIMAL' => Zend_Db::FLOAT_TYPE,
+ 'DEC' => Zend_Db::FLOAT_TYPE,
+ 'REAL' => Zend_Db::FLOAT_TYPE,
+ 'NUMERIC' => Zend_Db::FLOAT_TYPE,
+ 'DOUBLE PRECISION' => Zend_Db::FLOAT_TYPE,
+ 'FLOAT' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * Creates a PDO object and connects to the database.
+ *
+ * The IBM data server is set.
+ * Current options are DB2 or IDS
+ * @todo also differentiate between z/OS and i/5
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Exception
+ */
+ public function _connect()
+ {
+ if ($this->_connection) {
+ return;
+ }
+ parent::_connect();
+
+ $this->getConnection()->setAttribute(Zend_Db::ATTR_STRINGIFY_FETCHES, true);
+
+ try {
+ if ($this->_serverType === null) {
+ $server = substr($this->getConnection()->getAttribute(PDO::ATTR_SERVER_INFO), 0, 3);
+
+ switch ($server) {
+ case 'DB2':
+ $this->_serverType = new Zend_Db_Adapter_Pdo_Ibm_Db2($this);
+
+ // Add DB2-specific numeric types
+ $this->_numericDataTypes['DECFLOAT'] = Zend_Db::FLOAT_TYPE;
+ $this->_numericDataTypes['DOUBLE'] = Zend_Db::FLOAT_TYPE;
+ $this->_numericDataTypes['NUM'] = Zend_Db::FLOAT_TYPE;
+
+ break;
+ case 'IDS':
+ $this->_serverType = new Zend_Db_Adapter_Pdo_Ibm_Ids($this);
+
+ // Add IDS-specific numeric types
+ $this->_numericDataTypes['SERIAL'] = Zend_Db::INT_TYPE;
+ $this->_numericDataTypes['SERIAL8'] = Zend_Db::BIGINT_TYPE;
+ $this->_numericDataTypes['INT8'] = Zend_Db::BIGINT_TYPE;
+ $this->_numericDataTypes['SMALLFLOAT'] = Zend_Db::FLOAT_TYPE;
+ $this->_numericDataTypes['MONEY'] = Zend_Db::FLOAT_TYPE;
+
+ break;
+ }
+ }
+ } catch (PDOException $e) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ $error = strpos($e->getMessage(), 'driver does not support that attribute');
+ if ($error) {
+ throw new Zend_Db_Adapter_Exception("PDO_IBM driver extension is downlevel. Please use driver release version 1.2.1 or later", 0, $e);
+ } else {
+ throw new Zend_Db_Adapter_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ }
+
+ /**
+ * Creates a PDO DSN for the adapter from $this->_config settings.
+ *
+ * @return string
+ */
+ protected function _dsn()
+ {
+ $this->_checkRequiredOptions($this->_config);
+
+ // check if using full connection string
+ if (array_key_exists('host', $this->_config)) {
+ $dsn = ';DATABASE=' . $this->_config['dbname']
+ . ';HOSTNAME=' . $this->_config['host']
+ . ';PORT=' . $this->_config['port']
+ // PDO_IBM supports only DB2 TCPIP protocol
+ . ';PROTOCOL=' . 'TCPIP;';
+ } else {
+ // catalogued connection
+ $dsn = $this->_config['dbname'];
+ }
+ return $this->_pdoType . ': ' . $dsn;
+ }
+
+ /**
+ * Checks required options
+ *
+ * @param array $config
+ * @throws Zend_Db_Adapter_Exception
+ * @return void
+ */
+ protected function _checkRequiredOptions(array $config)
+ {
+ parent::_checkRequiredOptions($config);
+
+ if (array_key_exists('host', $this->_config) &&
+ !array_key_exists('port', $config)) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration must have a key for 'port' when 'host' is specified");
+ }
+ }
+
+ /**
+ * Prepares an SQL statement.
+ *
+ * @param string $sql The SQL statement with placeholders.
+ * @param array $bind An array of data to bind to the placeholders.
+ * @return PDOStatement
+ */
+ public function prepare($sql)
+ {
+ $this->_connect();
+ $stmtClass = $this->_defaultStmtClass;
+ $stmt = new $stmtClass($this, $sql);
+ $stmt->setFetchMode($this->_fetchMode);
+ return $stmt;
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $this->_connect();
+ return $this->_serverType->listTables();
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ *
+ * @todo Discover integer unsigned property.
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ $this->_connect();
+ return $this->_serverType->describeTable($tableName, $schemaName);
+ }
+
+ /**
+ * Inserts a table row with specified data.
+ * Special handling for PDO_IBM
+ * remove empty slots
+ *
+ * @param mixed $table The table to insert data into.
+ * @param array $bind Column-value pairs.
+ * @return int The number of affected rows.
+ */
+ public function insert($table, array $bind)
+ {
+ $this->_connect();
+ $newbind = array();
+ if (is_array($bind)) {
+ foreach ($bind as $name => $value) {
+ if($value !== null) {
+ $newbind[$name] = $value;
+ }
+ }
+ }
+
+ return parent::insert($table, $newbind);
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $this->_connect();
+ return $this->_serverType->limit($sql, $count, $offset);
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT
+ * column.
+ *
+ * @param string $tableName OPTIONAL
+ * @param string $primaryKey OPTIONAL
+ * @return integer
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ $this->_connect();
+
+ if ($tableName !== null) {
+ $sequenceName = $tableName;
+ if ($primaryKey) {
+ $sequenceName .= "_$primaryKey";
+ }
+ $sequenceName .= '_seq';
+ return $this->lastSequenceId($sequenceName);
+ }
+
+ $id = $this->getConnection()->lastInsertId();
+
+ return $id;
+ }
+
+ /**
+ * Return the most recent value from the specified sequence in the database.
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ $this->_connect();
+ return $this->_serverType->lastSequenceId($sequenceName);
+ }
+
+ /**
+ * Generate a new value from the specified sequence in the database,
+ * and return it.
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ $this->_connect();
+ return $this->_serverType->nextSequenceId($sequenceName);
+ }
+
+ /**
+ * Retrieve server version in PHP style
+ * Pdo_Idm doesn't support getAttribute(PDO::ATTR_SERVER_VERSION)
+ * @return string
+ */
+ public function getServerVersion()
+ {
+ try {
+ $stmt = $this->query('SELECT service_level, fixpack_num FROM TABLE (sysproc.env_get_inst_info()) as INSTANCEINFO');
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+ if (count($result)) {
+ $matches = null;
+ if (preg_match('/((?:[0-9]{1,2}\.){1,3}[0-9]{1,2})/', $result[0][0], $matches)) {
+ return $matches[1];
+ } else {
+ return null;
+ }
+ }
+ return null;
+ } catch (PDOException $e) {
+ return null;
+ }
+ }
+}
diff --git a/library/Zend/Db/Adapter/Pdo/Ibm/Db2.php b/library/Zend/Db/Adapter/Pdo/Ibm/Db2.php
new file mode 100644
index 0000000..3048c50
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Ibm/Db2.php
@@ -0,0 +1,228 @@
+_adapter = $adapter;
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $sql = "SELECT tabname "
+ . "FROM SYSCAT.TABLES ";
+ return $this->_adapter->fetchCol($sql);
+ }
+
+ /**
+ * DB2 catalog lookup for describe table
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ $sql = "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno,
+ c.typename, c.default, c.nulls, c.length, c.scale,
+ c.identity, tc.type AS tabconsttype, k.colseq
+ FROM syscat.columns c
+ LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
+ ON (k.tabschema = tc.tabschema
+ AND k.tabname = tc.tabname
+ AND tc.type = 'P'))
+ ON (c.tabschema = k.tabschema
+ AND c.tabname = k.tabname
+ AND c.colname = k.colname)
+ WHERE "
+ . $this->_adapter->quoteInto('UPPER(c.tabname) = UPPER(?)', $tableName);
+ if ($schemaName) {
+ $sql .= $this->_adapter->quoteInto(' AND UPPER(c.tabschema) = UPPER(?)', $schemaName);
+ }
+ $sql .= " ORDER BY c.colno";
+
+ $desc = array();
+ $stmt = $this->_adapter->query($sql);
+
+ /**
+ * To avoid case issues, fetch using FETCH_NUM
+ */
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ /**
+ * The ordering of columns is defined by the query so we can map
+ * to variables to improve readability
+ */
+ $tabschema = 0;
+ $tabname = 1;
+ $colname = 2;
+ $colno = 3;
+ $typename = 4;
+ $default = 5;
+ $nulls = 6;
+ $length = 7;
+ $scale = 8;
+ $identityCol = 9;
+ $tabconstype = 10;
+ $colseq = 11;
+
+ foreach ($result as $key => $row) {
+ list ($primary, $primaryPosition, $identity) = array(false, null, false);
+ if ($row[$tabconstype] == 'P') {
+ $primary = true;
+ $primaryPosition = $row[$colseq];
+ }
+ /**
+ * In IBM DB2, an column can be IDENTITY
+ * even if it is not part of the PRIMARY KEY.
+ */
+ if ($row[$identityCol] == 'Y') {
+ $identity = true;
+ }
+
+ $desc[$this->_adapter->foldCase($row[$colname])] = array(
+ 'SCHEMA_NAME' => $this->_adapter->foldCase($row[$tabschema]),
+ 'TABLE_NAME' => $this->_adapter->foldCase($row[$tabname]),
+ 'COLUMN_NAME' => $this->_adapter->foldCase($row[$colname]),
+ 'COLUMN_POSITION' => $row[$colno]+1,
+ 'DATA_TYPE' => $row[$typename],
+ 'DEFAULT' => $row[$default],
+ 'NULLABLE' => (bool) ($row[$nulls] == 'Y'),
+ 'LENGTH' => $row[$length],
+ 'SCALE' => $row[$scale],
+ 'PRECISION' => ($row[$typename] == 'DECIMAL' ? $row[$length] : 0),
+ 'UNSIGNED' => false,
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+
+ return $desc;
+ }
+
+ /**
+ * Adds a DB2-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @throws Zend_Db_Adapter_Exception
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ } else {
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ if ($offset == 0 && $count > 0) {
+ $limit_sql = $sql . " FETCH FIRST $count ROWS ONLY";
+ return $limit_sql;
+ }
+ /**
+ * DB2 does not implement the LIMIT clause as some RDBMS do.
+ * We have to simulate it with subqueries and ROWNUM.
+ * Unfortunately because we use the column wildcard "*",
+ * this puts an extra column into the query result set.
+ */
+ $limit_sql = "SELECT z2.*
+ FROM (
+ SELECT ROW_NUMBER() OVER() AS \"ZEND_DB_ROWNUM\", z1.*
+ FROM (
+ " . $sql . "
+ ) z1
+ ) z2
+ WHERE z2.zend_db_rownum BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
+ }
+ return $limit_sql;
+ }
+
+ /**
+ * DB2-specific last sequence id
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ $sql = 'SELECT PREVVAL FOR '.$this->_adapter->quoteIdentifier($sequenceName).' AS VAL FROM SYSIBM.SYSDUMMY1';
+ $value = $this->_adapter->fetchOne($sql);
+ return $value;
+ }
+
+ /**
+ * DB2-specific sequence id value
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ $sql = 'SELECT NEXTVAL FOR '.$this->_adapter->quoteIdentifier($sequenceName).' AS VAL FROM SYSIBM.SYSDUMMY1';
+ $value = $this->_adapter->fetchOne($sql);
+ return $value;
+ }
+}
diff --git a/library/Zend/Db/Adapter/Pdo/Ibm/Ids.php b/library/Zend/Db/Adapter/Pdo/Ibm/Ids.php
new file mode 100644
index 0000000..a84064d
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Ibm/Ids.php
@@ -0,0 +1,301 @@
+_adapter = $adapter;
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $sql = "SELECT tabname "
+ . "FROM systables ";
+
+ return $this->_adapter->fetchCol($sql);
+ }
+
+ /**
+ * IDS catalog lookup for describe table
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ // this is still a work in progress
+
+ $sql= "SELECT DISTINCT t.owner, t.tabname, c.colname, c.colno, c.coltype,
+ d.default, c.collength, t.tabid
+ FROM syscolumns c
+ JOIN systables t ON c.tabid = t.tabid
+ LEFT JOIN sysdefaults d ON c.tabid = d.tabid AND c.colno = d.colno
+ WHERE "
+ . $this->_adapter->quoteInto('UPPER(t.tabname) = UPPER(?)', $tableName);
+ if ($schemaName) {
+ $sql .= $this->_adapter->quoteInto(' AND UPPER(t.owner) = UPPER(?)', $schemaName);
+ }
+ $sql .= " ORDER BY c.colno";
+
+ $desc = array();
+ $stmt = $this->_adapter->query($sql);
+
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ /**
+ * The ordering of columns is defined by the query so we can map
+ * to variables to improve readability
+ */
+ $tabschema = 0;
+ $tabname = 1;
+ $colname = 2;
+ $colno = 3;
+ $typename = 4;
+ $default = 5;
+ $length = 6;
+ $tabid = 7;
+
+ $primaryCols = null;
+
+ foreach ($result as $key => $row) {
+ $primary = false;
+ $primaryPosition = null;
+
+ if (!$primaryCols) {
+ $primaryCols = $this->_getPrimaryInfo($row[$tabid]);
+ }
+
+ if (array_key_exists($row[$colno], $primaryCols)) {
+ $primary = true;
+ $primaryPosition = $primaryCols[$row[$colno]];
+ }
+
+ $identity = false;
+ if ($row[$typename] == 6 + 256 ||
+ $row[$typename] == 18 + 256) {
+ $identity = true;
+ }
+
+ $desc[$this->_adapter->foldCase($row[$colname])] = array (
+ 'SCHEMA_NAME' => $this->_adapter->foldCase($row[$tabschema]),
+ 'TABLE_NAME' => $this->_adapter->foldCase($row[$tabname]),
+ 'COLUMN_NAME' => $this->_adapter->foldCase($row[$colname]),
+ 'COLUMN_POSITION' => $row[$colno],
+ 'DATA_TYPE' => $this->_getDataType($row[$typename]),
+ 'DEFAULT' => $row[$default],
+ 'NULLABLE' => (bool) !($row[$typename] - 256 >= 0),
+ 'LENGTH' => $row[$length],
+ 'SCALE' => ($row[$typename] == 5 ? $row[$length]&255 : 0),
+ 'PRECISION' => ($row[$typename] == 5 ? (int)($row[$length]/256) : 0),
+ 'UNSIGNED' => false,
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+
+ return $desc;
+ }
+
+ /**
+ * Map number representation of a data type
+ * to a string
+ *
+ * @param int $typeNo
+ * @return string
+ */
+ protected function _getDataType($typeNo)
+ {
+ $typemap = array(
+ 0 => "CHAR",
+ 1 => "SMALLINT",
+ 2 => "INTEGER",
+ 3 => "FLOAT",
+ 4 => "SMALLFLOAT",
+ 5 => "DECIMAL",
+ 6 => "SERIAL",
+ 7 => "DATE",
+ 8 => "MONEY",
+ 9 => "NULL",
+ 10 => "DATETIME",
+ 11 => "BYTE",
+ 12 => "TEXT",
+ 13 => "VARCHAR",
+ 14 => "INTERVAL",
+ 15 => "NCHAR",
+ 16 => "NVARCHAR",
+ 17 => "INT8",
+ 18 => "SERIAL8",
+ 19 => "SET",
+ 20 => "MULTISET",
+ 21 => "LIST",
+ 22 => "Unnamed ROW",
+ 40 => "Variable-length opaque type",
+ 4118 => "Named ROW"
+ );
+
+ if ($typeNo - 256 >= 0) {
+ $typeNo = $typeNo - 256;
+ }
+
+ return $typemap[$typeNo];
+ }
+
+ /**
+ * Helper method to retrieve primary key column
+ * and column location
+ *
+ * @param int $tabid
+ * @return array
+ */
+ protected function _getPrimaryInfo($tabid)
+ {
+ $sql = "SELECT i.part1, i.part2, i.part3, i.part4, i.part5, i.part6,
+ i.part7, i.part8, i.part9, i.part10, i.part11, i.part12,
+ i.part13, i.part14, i.part15, i.part16
+ FROM sysindexes i
+ JOIN sysconstraints c ON c.idxname = i.idxname
+ WHERE i.tabid = " . $tabid . " AND c.constrtype = 'P'";
+
+ $stmt = $this->_adapter->query($sql);
+ $results = $stmt->fetchAll();
+
+ $cols = array();
+
+ // this should return only 1 row
+ // unless there is no primary key,
+ // in which case, the empty array is returned
+ if ($results) {
+ $row = $results[0];
+ } else {
+ return $cols;
+ }
+
+ $position = 0;
+ foreach ($row as $key => $colno) {
+ $position++;
+ if ($colno == 0) {
+ return $cols;
+ } else {
+ $cols[$colno] = $position;
+ }
+ }
+ }
+
+ /**
+ * Adds an IDS-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @throws Zend_Db_Adapter_Exception
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ } else if ($count == 0) {
+ $limit_sql = str_ireplace("SELECT", "SELECT * FROM (SELECT", $sql);
+ $limit_sql .= ") WHERE 0 = 1";
+ } else {
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+ if ($offset == 0) {
+ $limit_sql = str_ireplace("SELECT", "SELECT FIRST $count", $sql);
+ } else {
+ $limit_sql = str_ireplace("SELECT", "SELECT SKIP $offset LIMIT $count", $sql);
+ }
+ }
+ return $limit_sql;
+ }
+
+ /**
+ * IDS-specific last sequence id
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ $sql = 'SELECT '.$this->_adapter->quoteIdentifier($sequenceName).'.CURRVAL FROM '
+ .'systables WHERE tabid = 1';
+ $value = $this->_adapter->fetchOne($sql);
+ return $value;
+ }
+
+ /**
+ * IDS-specific sequence id value
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ $sql = 'SELECT '.$this->_adapter->quoteIdentifier($sequenceName).'.NEXTVAL FROM '
+ .'systables WHERE tabid = 1';
+ $value = $this->_adapter->fetchOne($sql);
+ return $value;
+ }
+}
diff --git a/library/Zend/Db/Adapter/Pdo/Mssql.php b/library/Zend/Db/Adapter/Pdo/Mssql.php
new file mode 100644
index 0000000..f8d53a8
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Mssql.php
@@ -0,0 +1,423 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INT' => Zend_Db::INT_TYPE,
+ 'SMALLINT' => Zend_Db::INT_TYPE,
+ 'TINYINT' => Zend_Db::INT_TYPE,
+ 'BIGINT' => Zend_Db::BIGINT_TYPE,
+ 'DECIMAL' => Zend_Db::FLOAT_TYPE,
+ 'FLOAT' => Zend_Db::FLOAT_TYPE,
+ 'MONEY' => Zend_Db::FLOAT_TYPE,
+ 'NUMERIC' => Zend_Db::FLOAT_TYPE,
+ 'REAL' => Zend_Db::FLOAT_TYPE,
+ 'SMALLMONEY' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * Creates a PDO DSN for the adapter from $this->_config settings.
+ *
+ * @return string
+ */
+ protected function _dsn()
+ {
+ // baseline of DSN parts
+ $dsn = $this->_config;
+
+ // don't pass the username and password in the DSN
+ unset($dsn['username']);
+ unset($dsn['password']);
+ unset($dsn['options']);
+ unset($dsn['persistent']);
+ unset($dsn['driver_options']);
+
+ if (isset($dsn['port'])) {
+ $seperator = ':';
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ $seperator = ',';
+ }
+ $dsn['host'] .= $seperator . $dsn['port'];
+ unset($dsn['port']);
+ }
+
+ // this driver supports multiple DSN prefixes
+ // @see http://www.php.net/manual/en/ref.pdo-dblib.connection.php
+ if (isset($dsn['pdoType'])) {
+ switch (strtolower($dsn['pdoType'])) {
+ case 'freetds':
+ case 'sybase':
+ $this->_pdoType = 'sybase';
+ break;
+ case 'mssql':
+ $this->_pdoType = 'mssql';
+ break;
+ case 'dblib':
+ default:
+ $this->_pdoType = 'dblib';
+ break;
+ }
+ unset($dsn['pdoType']);
+ }
+
+ // use all remaining parts in the DSN
+ foreach ($dsn as $key => $val) {
+ $dsn[$key] = "$key=$val";
+ }
+
+ $dsn = $this->_pdoType . ':' . implode(';', $dsn);
+ return $dsn;
+ }
+
+ /**
+ * @return void
+ */
+ protected function _connect()
+ {
+ if ($this->_connection) {
+ return;
+ }
+ parent::_connect();
+ $this->_connection->exec('SET QUOTED_IDENTIFIER ON');
+ }
+
+ /**
+ * Begin a transaction.
+ *
+ * It is necessary to override the abstract PDO transaction functions here, as
+ * the PDO driver for MSSQL does not support transactions.
+ */
+ protected function _beginTransaction()
+ {
+ $this->_connect();
+ $this->_connection->exec('BEGIN TRANSACTION');
+ return true;
+ }
+
+ /**
+ * Commit a transaction.
+ *
+ * It is necessary to override the abstract PDO transaction functions here, as
+ * the PDO driver for MSSQL does not support transactions.
+ */
+ protected function _commit()
+ {
+ $this->_connect();
+ $this->_connection->exec('COMMIT TRANSACTION');
+ return true;
+ }
+
+ /**
+ * Roll-back a transaction.
+ *
+ * It is necessary to override the abstract PDO transaction functions here, as
+ * the PDO driver for MSSQL does not support transactions.
+ */
+ protected function _rollBack() {
+ $this->_connect();
+ $this->_connection->exec('ROLLBACK TRANSACTION');
+ return true;
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
+ return $this->fetchCol($sql);
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * PRIMARY_AUTO => integer; position of auto-generated column in primary key
+ *
+ * @todo Discover column primary key position.
+ * @todo Discover integer unsigned property.
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ if ($schemaName != null) {
+ if (strpos($schemaName, '.') !== false) {
+ $result = explode('.', $schemaName);
+ $schemaName = $result[1];
+ }
+ }
+ /**
+ * Discover metadata information about this table.
+ */
+ $sql = "exec sp_columns @table_name = " . $this->quoteIdentifier($tableName, true);
+ if ($schemaName != null) {
+ $sql .= ", @table_owner = " . $this->quoteIdentifier($schemaName, true);
+ }
+
+ $stmt = $this->query($sql);
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ $table_name = 2;
+ $column_name = 3;
+ $type_name = 5;
+ $precision = 6;
+ $length = 7;
+ $scale = 8;
+ $nullable = 10;
+ $column_def = 12;
+ $column_position = 16;
+
+ /**
+ * Discover primary key column(s) for this table.
+ */
+ $sql = "exec sp_pkeys @table_name = " . $this->quoteIdentifier($tableName, true);
+ if ($schemaName != null) {
+ $sql .= ", @table_owner = " . $this->quoteIdentifier($schemaName, true);
+ }
+
+ $stmt = $this->query($sql);
+ $primaryKeysResult = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+ $primaryKeyColumn = array();
+ $pkey_column_name = 3;
+ $pkey_key_seq = 4;
+ foreach ($primaryKeysResult as $pkeysRow) {
+ $primaryKeyColumn[$pkeysRow[$pkey_column_name]] = $pkeysRow[$pkey_key_seq];
+ }
+
+ $desc = array();
+ $p = 1;
+ foreach ($result as $key => $row) {
+ $identity = false;
+ $words = explode(' ', $row[$type_name], 2);
+ if (isset($words[0])) {
+ $type = $words[0];
+ if (isset($words[1])) {
+ $identity = (bool) preg_match('/identity/', $words[1]);
+ }
+ }
+
+ $isPrimary = array_key_exists($row[$column_name], $primaryKeyColumn);
+ if ($isPrimary) {
+ $primaryPosition = $primaryKeyColumn[$row[$column_name]];
+ } else {
+ $primaryPosition = null;
+ }
+
+ $desc[$this->foldCase($row[$column_name])] = array(
+ 'SCHEMA_NAME' => null, // @todo
+ 'TABLE_NAME' => $this->foldCase($row[$table_name]),
+ 'COLUMN_NAME' => $this->foldCase($row[$column_name]),
+ 'COLUMN_POSITION' => (int) $row[$column_position],
+ 'DATA_TYPE' => $type,
+ 'DEFAULT' => $row[$column_def],
+ 'NULLABLE' => (bool) $row[$nullable],
+ 'LENGTH' => $row[$length],
+ 'SCALE' => $row[$scale],
+ 'PRECISION' => $row[$precision],
+ 'UNSIGNED' => null, // @todo
+ 'PRIMARY' => $isPrimary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+ return $desc;
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @link http://lists.bestpractical.com/pipermail/rt-devel/2005-June/007339.html
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @throws Zend_Db_Adapter_Exception
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ $sql = preg_replace(
+ '/^SELECT\s+(DISTINCT\s)?/i',
+ 'SELECT $1TOP ' . ($count+$offset) . ' ',
+ $sql
+ );
+
+ if ($offset > 0) {
+ $orderby = stristr($sql, 'ORDER BY');
+
+ if ($orderby !== false) {
+ $orderParts = explode(',', substr($orderby, 8));
+ $pregReplaceCount = null;
+ $orderbyInverseParts = array();
+ foreach ($orderParts as $orderPart) {
+ $orderPart = rtrim($orderPart);
+ $inv = preg_replace('/\s+desc$/i', ' ASC', $orderPart, 1, $pregReplaceCount);
+ if ($pregReplaceCount) {
+ $orderbyInverseParts[] = $inv;
+ continue;
+ }
+ $inv = preg_replace('/\s+asc$/i', ' DESC', $orderPart, 1, $pregReplaceCount);
+ if ($pregReplaceCount) {
+ $orderbyInverseParts[] = $inv;
+ continue;
+ } else {
+ $orderbyInverseParts[] = $orderPart . ' DESC';
+ }
+ }
+
+ $orderbyInverse = 'ORDER BY ' . implode(', ', $orderbyInverseParts);
+ }
+
+
+
+
+ $sql = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $sql . ') AS inner_tbl';
+ if ($orderby !== false) {
+ $sql .= ' ' . $orderbyInverse . ' ';
+ }
+ $sql .= ') AS outer_tbl';
+ if ($orderby !== false) {
+ $sql .= ' ' . $orderby;
+ }
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * Microsoft SQL Server does not support sequences, so the arguments to
+ * this method are ignored.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ * @throws Zend_Db_Adapter_Exception
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ $sql = 'SELECT SCOPE_IDENTITY()';
+ return (int)$this->fetchOne($sql);
+ }
+
+ /**
+ * Retrieve server version in PHP style
+ * Pdo_Mssql doesn't support getAttribute(PDO::ATTR_SERVER_VERSION)
+ * @return string
+ */
+ public function getServerVersion()
+ {
+ try {
+ $stmt = $this->query("SELECT SERVERPROPERTY('productversion')");
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+ if (count($result)) {
+ return $result[0][0];
+ }
+ return null;
+ } catch (PDOException $e) {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/Zend/Db/Adapter/Pdo/Mysql.php b/library/Zend/Db/Adapter/Pdo/Mysql.php
new file mode 100644
index 0000000..fecc9e1
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Mysql.php
@@ -0,0 +1,270 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INT' => Zend_Db::INT_TYPE,
+ 'INTEGER' => Zend_Db::INT_TYPE,
+ 'MEDIUMINT' => Zend_Db::INT_TYPE,
+ 'SMALLINT' => Zend_Db::INT_TYPE,
+ 'TINYINT' => Zend_Db::INT_TYPE,
+ 'BIGINT' => Zend_Db::BIGINT_TYPE,
+ 'SERIAL' => Zend_Db::BIGINT_TYPE,
+ 'DEC' => Zend_Db::FLOAT_TYPE,
+ 'DECIMAL' => Zend_Db::FLOAT_TYPE,
+ 'DOUBLE' => Zend_Db::FLOAT_TYPE,
+ 'DOUBLE PRECISION' => Zend_Db::FLOAT_TYPE,
+ 'FIXED' => Zend_Db::FLOAT_TYPE,
+ 'FLOAT' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * Override _dsn() and ensure that charset is incorporated in mysql
+ * @see Zend_Db_Adapter_Pdo_Abstract::_dsn()
+ */
+ protected function _dsn()
+ {
+ $dsn = parent::_dsn();
+ if (isset($this->_config['charset'])) {
+ $dsn .= ';charset=' . $this->_config['charset'];
+ }
+ return $dsn;
+ }
+
+ /**
+ * Creates a PDO object and connects to the database.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Exception
+ */
+ protected function _connect()
+ {
+ if ($this->_connection) {
+ return;
+ }
+
+ if (!empty($this->_config['charset'])) {
+ $initCommand = "SET NAMES '" . $this->_config['charset'] . "'";
+ $this->_config['driver_options'][1002] = $initCommand; // 1002 = PDO::MYSQL_ATTR_INIT_COMMAND
+ }
+
+ parent::_connect();
+ }
+
+ /**
+ * @return string
+ */
+ public function getQuoteIdentifierSymbol()
+ {
+ return "`";
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ return $this->fetchCol('SHOW TABLES');
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ // @todo use INFORMATION_SCHEMA someday when MySQL's
+ // implementation has reasonably good performance and
+ // the version with this improvement is in wide use.
+
+ if ($schemaName) {
+ $sql = 'DESCRIBE ' . $this->quoteIdentifier("$schemaName.$tableName", true);
+ } else {
+ $sql = 'DESCRIBE ' . $this->quoteIdentifier($tableName, true);
+ }
+ $stmt = $this->query($sql);
+
+ // Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ $field = 0;
+ $type = 1;
+ $null = 2;
+ $key = 3;
+ $default = 4;
+ $extra = 5;
+
+ $desc = array();
+ $i = 1;
+ $p = 1;
+ foreach ($result as $row) {
+ list($length, $scale, $precision, $unsigned, $primary, $primaryPosition, $identity)
+ = array(null, null, null, null, false, null, false);
+ if (preg_match('/unsigned/', $row[$type])) {
+ $unsigned = true;
+ }
+ if (preg_match('/^((?:var)?char)\((\d+)\)/', $row[$type], $matches)) {
+ $row[$type] = $matches[1];
+ $length = $matches[2];
+ } else if (preg_match('/^decimal\((\d+),(\d+)\)/', $row[$type], $matches)) {
+ $row[$type] = 'decimal';
+ $precision = $matches[1];
+ $scale = $matches[2];
+ } else if (preg_match('/^float\((\d+),(\d+)\)/', $row[$type], $matches)) {
+ $row[$type] = 'float';
+ $precision = $matches[1];
+ $scale = $matches[2];
+ } else if (preg_match('/^((?:big|medium|small|tiny)?int)\((\d+)\)/', $row[$type], $matches)) {
+ $row[$type] = $matches[1];
+ // The optional argument of a MySQL int type is not precision
+ // or length; it is only a hint for display width.
+ }
+ if (strtoupper($row[$key]) == 'PRI') {
+ $primary = true;
+ $primaryPosition = $p;
+ if ($row[$extra] == 'auto_increment') {
+ $identity = true;
+ } else {
+ $identity = false;
+ }
+ ++$p;
+ }
+ $desc[$this->foldCase($row[$field])] = array(
+ 'SCHEMA_NAME' => null, // @todo
+ 'TABLE_NAME' => $this->foldCase($tableName),
+ 'COLUMN_NAME' => $this->foldCase($row[$field]),
+ 'COLUMN_POSITION' => $i,
+ 'DATA_TYPE' => $row[$type],
+ 'DEFAULT' => $row[$default],
+ 'NULLABLE' => (bool) ($row[$null] == 'YES'),
+ 'LENGTH' => $length,
+ 'SCALE' => $scale,
+ 'PRECISION' => $precision,
+ 'UNSIGNED' => $unsigned,
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ ++$i;
+ }
+ return $desc;
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @throws Zend_Db_Adapter_Exception
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ $sql .= " LIMIT $count";
+ if ($offset > 0) {
+ $sql .= " OFFSET $offset";
+ }
+
+ return $sql;
+ }
+
+}
diff --git a/library/Zend/Db/Adapter/Pdo/Oci.php b/library/Zend/Db/Adapter/Pdo/Oci.php
new file mode 100644
index 0000000..aa09f9f
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Oci.php
@@ -0,0 +1,378 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'BINARY_DOUBLE' => Zend_Db::FLOAT_TYPE,
+ 'BINARY_FLOAT' => Zend_Db::FLOAT_TYPE,
+ 'NUMBER' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * Creates a PDO DSN for the adapter from $this->_config settings.
+ *
+ * @return string
+ */
+ protected function _dsn()
+ {
+ // baseline of DSN parts
+ $dsn = $this->_config;
+
+ if (isset($dsn['host'])) {
+ $tns = 'dbname=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)' .
+ '(HOST=' . $dsn['host'] . ')';
+
+ if (isset($dsn['port'])) {
+ $tns .= '(PORT=' . $dsn['port'] . ')';
+ } else {
+ $tns .= '(PORT=1521)';
+ }
+
+ $tns .= '))(CONNECT_DATA=(SID=' . $dsn['dbname'] . ')))';
+ } else {
+ $tns = 'dbname=' . $dsn['dbname'];
+ }
+
+ if (isset($dsn['charset'])) {
+ $tns .= ';charset=' . $dsn['charset'];
+ }
+
+ return $this->_pdoType . ':' . $tns;
+ }
+
+ /**
+ * Quote a raw string.
+ * Most PDO drivers have an implementation for the quote() method,
+ * but the Oracle OCI driver must use the same implementation as the
+ * Zend_Db_Adapter_Abstract class.
+ *
+ * @param string $value Raw string
+ * @return string Quoted string
+ */
+ protected function _quote($value)
+ {
+ if (is_int($value) || is_float($value)) {
+ return $value;
+ }
+ $value = str_replace("'", "''", $value);
+ return "'" . addcslashes($value, "\000\n\r\\\032") . "'";
+ }
+
+ /**
+ * Quote a table identifier and alias.
+ *
+ * @param string|array|Zend_Db_Expr $ident The identifier or expression.
+ * @param string $alias An alias for the table.
+ * @return string The quoted identifier and alias.
+ */
+ public function quoteTableAs($ident, $alias = null, $auto = false)
+ {
+ // Oracle doesn't allow the 'AS' keyword between the table identifier/expression and alias.
+ return $this->_quoteIdentifierAs($ident, $alias, $auto, ' ');
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $data = $this->fetchCol('SELECT table_name FROM all_tables');
+ return $data;
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @todo Discover integer unsigned property.
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ $version = $this->getServerVersion();
+ if (($version === null) || version_compare($version, '9.0.0', '>=')) {
+ $sql = "SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE,
+ TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH,
+ TC.DATA_SCALE, TC.DATA_PRECISION, C.CONSTRAINT_TYPE, CC.POSITION
+ FROM ALL_TAB_COLUMNS TC
+ LEFT JOIN (ALL_CONS_COLUMNS CC JOIN ALL_CONSTRAINTS C
+ ON (CC.CONSTRAINT_NAME = C.CONSTRAINT_NAME AND CC.TABLE_NAME = C.TABLE_NAME AND CC.OWNER = C.OWNER AND C.CONSTRAINT_TYPE = 'P'))
+ ON TC.TABLE_NAME = CC.TABLE_NAME AND TC.COLUMN_NAME = CC.COLUMN_NAME
+ WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)";
+ $bind[':TBNAME'] = $tableName;
+ if ($schemaName) {
+ $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)';
+ $bind[':SCNAME'] = $schemaName;
+ }
+ $sql .= ' ORDER BY TC.COLUMN_ID';
+ } else {
+ $subSql="SELECT AC.OWNER, AC.TABLE_NAME, ACC.COLUMN_NAME, AC.CONSTRAINT_TYPE, ACC.POSITION
+ from ALL_CONSTRAINTS AC, ALL_CONS_COLUMNS ACC
+ WHERE ACC.CONSTRAINT_NAME = AC.CONSTRAINT_NAME
+ AND ACC.TABLE_NAME = AC.TABLE_NAME
+ AND ACC.OWNER = AC.OWNER
+ AND AC.CONSTRAINT_TYPE = 'P'
+ AND UPPER(AC.TABLE_NAME) = UPPER(:TBNAME)";
+ $bind[':TBNAME'] = $tableName;
+ if ($schemaName) {
+ $subSql .= ' AND UPPER(ACC.OWNER) = UPPER(:SCNAME)';
+ $bind[':SCNAME'] = $schemaName;
+ }
+ $sql="SELECT TC.TABLE_NAME, TC.OWNER, TC.COLUMN_NAME, TC.DATA_TYPE,
+ TC.DATA_DEFAULT, TC.NULLABLE, TC.COLUMN_ID, TC.DATA_LENGTH,
+ TC.DATA_SCALE, TC.DATA_PRECISION, CC.CONSTRAINT_TYPE, CC.POSITION
+ FROM ALL_TAB_COLUMNS TC, ($subSql) CC
+ WHERE UPPER(TC.TABLE_NAME) = UPPER(:TBNAME)
+ AND TC.OWNER = CC.OWNER(+) AND TC.TABLE_NAME = CC.TABLE_NAME(+) AND TC.COLUMN_NAME = CC.COLUMN_NAME(+)";
+ if ($schemaName) {
+ $sql .= ' AND UPPER(TC.OWNER) = UPPER(:SCNAME)';
+ }
+ $sql .= ' ORDER BY TC.COLUMN_ID';
+ }
+
+ $stmt = $this->query($sql, $bind);
+
+ /**
+ * Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection
+ */
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ $table_name = 0;
+ $owner = 1;
+ $column_name = 2;
+ $data_type = 3;
+ $data_default = 4;
+ $nullable = 5;
+ $column_id = 6;
+ $data_length = 7;
+ $data_scale = 8;
+ $data_precision = 9;
+ $constraint_type = 10;
+ $position = 11;
+
+ $desc = array();
+ foreach ($result as $key => $row) {
+ list ($primary, $primaryPosition, $identity) = array(false, null, false);
+ if ($row[$constraint_type] == 'P') {
+ $primary = true;
+ $primaryPosition = $row[$position];
+ /**
+ * Oracle does not support auto-increment keys.
+ */
+ $identity = false;
+ }
+ $desc[$this->foldCase($row[$column_name])] = array(
+ 'SCHEMA_NAME' => $this->foldCase($row[$owner]),
+ 'TABLE_NAME' => $this->foldCase($row[$table_name]),
+ 'COLUMN_NAME' => $this->foldCase($row[$column_name]),
+ 'COLUMN_POSITION' => $row[$column_id],
+ 'DATA_TYPE' => $row[$data_type],
+ 'DEFAULT' => $row[$data_default],
+ 'NULLABLE' => (bool) ($row[$nullable] == 'Y'),
+ 'LENGTH' => $row[$data_length],
+ 'SCALE' => $row[$data_scale],
+ 'PRECISION' => $row[$data_precision],
+ 'UNSIGNED' => null, // @todo
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+ return $desc;
+ }
+
+ /**
+ * Return the most recent value from the specified sequence in the database.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ $this->_connect();
+ $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.CURRVAL FROM dual');
+ return $value;
+ }
+
+ /**
+ * Generate a new value from the specified sequence in the database, and return it.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return integer
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ $this->_connect();
+ $value = $this->fetchOne('SELECT '.$this->quoteIdentifier($sequenceName, true).'.NEXTVAL FROM dual');
+ return $value;
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * Oracle does not support IDENTITY columns, so if the sequence is not
+ * specified, this method returns null.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ * @throws Zend_Db_Adapter_Oracle_Exception
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ if ($tableName !== null) {
+ $sequenceName = $tableName;
+ if ($primaryKey) {
+ $sequenceName .= $this->foldCase("_$primaryKey");
+ }
+ $sequenceName .= $this->foldCase('_seq');
+ return $this->lastSequenceId($sequenceName);
+ }
+ // No support for IDENTITY columns; return null
+ return null;
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset
+ * @throws Zend_Db_Adapter_Exception
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ /**
+ * Oracle does not implement the LIMIT clause as some RDBMS do.
+ * We have to simulate it with subqueries and ROWNUM.
+ * Unfortunately because we use the column wildcard "*",
+ * this puts an extra column into the query result set.
+ */
+ $limit_sql = "SELECT z2.*
+ FROM (
+ SELECT z1.*, ROWNUM AS \"zend_db_rownum\"
+ FROM (
+ " . $sql . "
+ ) z1
+ ) z2
+ WHERE z2.\"zend_db_rownum\" BETWEEN " . ($offset+1) . " AND " . ($offset+$count);
+ return $limit_sql;
+ }
+
+}
diff --git a/library/Zend/Db/Adapter/Pdo/Pgsql.php b/library/Zend/Db/Adapter/Pdo/Pgsql.php
new file mode 100644
index 0000000..89944c4
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Pgsql.php
@@ -0,0 +1,336 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INTEGER' => Zend_Db::INT_TYPE,
+ 'SERIAL' => Zend_Db::INT_TYPE,
+ 'SMALLINT' => Zend_Db::INT_TYPE,
+ 'BIGINT' => Zend_Db::BIGINT_TYPE,
+ 'BIGSERIAL' => Zend_Db::BIGINT_TYPE,
+ 'DECIMAL' => Zend_Db::FLOAT_TYPE,
+ 'DOUBLE PRECISION' => Zend_Db::FLOAT_TYPE,
+ 'NUMERIC' => Zend_Db::FLOAT_TYPE,
+ 'REAL' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * Creates a PDO object and connects to the database.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Exception
+ */
+ protected function _connect()
+ {
+ if ($this->_connection) {
+ return;
+ }
+
+ parent::_connect();
+
+ if (!empty($this->_config['charset'])) {
+ $sql = "SET NAMES '" . $this->_config['charset'] . "'";
+ $this->_connection->exec($sql);
+ }
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ // @todo use a better query with joins instead of subqueries
+ $sql = "SELECT c.relname AS table_name "
+ . "FROM pg_class c, pg_user u "
+ . "WHERE c.relowner = u.usesysid AND c.relkind = 'r' "
+ . "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) "
+ . "AND c.relname !~ '^(pg_|sql_)' "
+ . "UNION "
+ . "SELECT c.relname AS table_name "
+ . "FROM pg_class c "
+ . "WHERE c.relkind = 'r' "
+ . "AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname) "
+ . "AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner) "
+ . "AND c.relname !~ '^pg_'";
+
+ return $this->fetchCol($sql);
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @todo Discover integer unsigned property.
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ $sql = "SELECT
+ a.attnum,
+ n.nspname,
+ c.relname,
+ a.attname AS colname,
+ t.typname AS type,
+ a.atttypmod,
+ FORMAT_TYPE(a.atttypid, a.atttypmod) AS complete_type,
+ d.adsrc AS default_value,
+ a.attnotnull AS notnull,
+ a.attlen AS length,
+ co.contype,
+ ARRAY_TO_STRING(co.conkey, ',') AS conkey
+ FROM pg_attribute AS a
+ JOIN pg_class AS c ON a.attrelid = c.oid
+ JOIN pg_namespace AS n ON c.relnamespace = n.oid
+ JOIN pg_type AS t ON a.atttypid = t.oid
+ LEFT OUTER JOIN pg_constraint AS co ON (co.conrelid = c.oid
+ AND a.attnum = ANY(co.conkey) AND co.contype = 'p')
+ LEFT OUTER JOIN pg_attrdef AS d ON d.adrelid = c.oid AND d.adnum = a.attnum
+ WHERE a.attnum > 0 AND c.relname = ".$this->quote($tableName);
+ if ($schemaName) {
+ $sql .= " AND n.nspname = ".$this->quote($schemaName);
+ }
+ $sql .= ' ORDER BY a.attnum';
+
+ $stmt = $this->query($sql);
+
+ // Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ $attnum = 0;
+ $nspname = 1;
+ $relname = 2;
+ $colname = 3;
+ $type = 4;
+ $atttypemod = 5;
+ $complete_type = 6;
+ $default_value = 7;
+ $notnull = 8;
+ $length = 9;
+ $contype = 10;
+ $conkey = 11;
+
+ $desc = array();
+ foreach ($result as $key => $row) {
+ $defaultValue = $row[$default_value];
+ if ($row[$type] == 'varchar' || $row[$type] == 'bpchar' ) {
+ if (preg_match('/character(?: varying)?(?:\((\d+)\))?/', $row[$complete_type], $matches)) {
+ if (isset($matches[1])) {
+ $row[$length] = $matches[1];
+ } else {
+ $row[$length] = null; // unlimited
+ }
+ }
+ if (preg_match("/^'(.*?)'::(?:character varying|bpchar)$/", $defaultValue, $matches)) {
+ $defaultValue = $matches[1];
+ }
+ }
+ list($primary, $primaryPosition, $identity) = array(false, null, false);
+ if ($row[$contype] == 'p') {
+ $primary = true;
+ $primaryPosition = array_search($row[$attnum], explode(',', $row[$conkey])) + 1;
+ $identity = (bool) (preg_match('/^nextval/', $row[$default_value]));
+ }
+ $desc[$this->foldCase($row[$colname])] = array(
+ 'SCHEMA_NAME' => $this->foldCase($row[$nspname]),
+ 'TABLE_NAME' => $this->foldCase($row[$relname]),
+ 'COLUMN_NAME' => $this->foldCase($row[$colname]),
+ 'COLUMN_POSITION' => $row[$attnum],
+ 'DATA_TYPE' => $row[$type],
+ 'DEFAULT' => $defaultValue,
+ 'NULLABLE' => (bool) ($row[$notnull] != 't'),
+ 'LENGTH' => $row[$length],
+ 'SCALE' => null, // @todo
+ 'PRECISION' => null, // @todo
+ 'UNSIGNED' => null, // @todo
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+ return $desc;
+ }
+
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ $sql .= " LIMIT $count";
+ if ($offset > 0) {
+ $sql .= " OFFSET $offset";
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Return the most recent value from the specified sequence in the database.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function lastSequenceId($sequenceName)
+ {
+ $this->_connect();
+ $sequenceName = str_replace($this->getQuoteIdentifierSymbol(), '', (string) $sequenceName);
+ $value = $this->fetchOne("SELECT CURRVAL("
+ . $this->quote($this->quoteIdentifier($sequenceName, true))
+ . ")");
+ return $value;
+ }
+
+ /**
+ * Generate a new value from the specified sequence in the database, and return it.
+ * This is supported only on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2). Other RDBMS brands return null.
+ *
+ * @param string $sequenceName
+ * @return string
+ */
+ public function nextSequenceId($sequenceName)
+ {
+ $this->_connect();
+ $sequenceName = str_replace($this->getQuoteIdentifierSymbol(), '', (string) $sequenceName);
+ $value = $this->fetchOne("SELECT NEXTVAL("
+ . $this->quote($this->quoteIdentifier($sequenceName, true))
+ . ")");
+ return $value;
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ if ($tableName !== null) {
+ $sequenceName = $tableName;
+ if ($primaryKey) {
+ $sequenceName .= "_$primaryKey";
+ }
+ $sequenceName .= '_seq';
+ return $this->lastSequenceId($sequenceName);
+ }
+ return $this->_connection->lastInsertId($tableName);
+ }
+
+}
diff --git a/library/Zend/Db/Adapter/Pdo/Sqlite.php b/library/Zend/Db/Adapter/Pdo/Sqlite.php
new file mode 100644
index 0000000..7ba54ed
--- /dev/null
+++ b/library/Zend/Db/Adapter/Pdo/Sqlite.php
@@ -0,0 +1,297 @@
+ Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INTEGER' => Zend_Db::BIGINT_TYPE,
+ 'REAL' => Zend_Db::FLOAT_TYPE
+ );
+
+ /**
+ * Constructor.
+ *
+ * $config is an array of key/value pairs containing configuration
+ * options. Note that the SQLite options are different than most of
+ * the other PDO adapters in that no username or password are needed.
+ * Also, an extra config key "sqlite2" specifies compatibility mode.
+ *
+ * dbname => (string) The name of the database to user (required,
+ * use :memory: for memory-based database)
+ *
+ * sqlite2 => (boolean) PDO_SQLITE defaults to SQLite 3. For compatibility
+ * with an older SQLite 2 database, set this to TRUE.
+ *
+ * @param array $config An array of configuration keys.
+ */
+ public function __construct(array $config = array())
+ {
+ if (isset($config['sqlite2']) && $config['sqlite2']) {
+ $this->_pdoType = 'sqlite2';
+ }
+
+ // SQLite uses no username/password. Stub to satisfy parent::_connect()
+ $this->_config['username'] = null;
+ $this->_config['password'] = null;
+
+ return parent::__construct($config);
+ }
+
+ /**
+ * Check for config options that are mandatory.
+ * Throw exceptions if any are missing.
+ *
+ * @param array $config
+ * @throws Zend_Db_Adapter_Exception
+ */
+ protected function _checkRequiredOptions(array $config)
+ {
+ // we need at least a dbname
+ if (! array_key_exists('dbname', $config)) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'dbname' that names the database instance");
+ }
+ }
+
+ /**
+ * DSN builder
+ */
+ protected function _dsn()
+ {
+ return $this->_pdoType .':'. $this->_config['dbname'];
+ }
+
+ /**
+ * Special configuration for SQLite behavior: make sure that result sets
+ * contain keys like 'column' instead of 'table.column'.
+ *
+ * @throws Zend_Db_Adapter_Exception
+ */
+ protected function _connect()
+ {
+ /**
+ * if we already have a PDO object, no need to re-connect.
+ */
+ if ($this->_connection) {
+ return;
+ }
+
+ parent::_connect();
+
+ $retval = $this->_connection->exec('PRAGMA full_column_names=0');
+ if ($retval === false) {
+ $error = $this->_connection->errorInfo();
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception($error[2]);
+ }
+
+ $retval = $this->_connection->exec('PRAGMA short_column_names=1');
+ if ($retval === false) {
+ $error = $this->_connection->errorInfo();
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception($error[2]);
+ }
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $sql = "SELECT name FROM sqlite_master WHERE type='table' "
+ . "UNION ALL SELECT name FROM sqlite_temp_master "
+ . "WHERE type='table' ORDER BY name";
+
+ return $this->fetchCol($sql);
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of database or schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ $sql = 'PRAGMA ';
+
+ if ($schemaName) {
+ $sql .= $this->quoteIdentifier($schemaName) . '.';
+ }
+
+ $sql .= 'table_info('.$this->quoteIdentifier($tableName).')';
+
+ $stmt = $this->query($sql);
+
+ /**
+ * Use FETCH_NUM so we are not dependent on the CASE attribute of the PDO connection
+ */
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ $cid = 0;
+ $name = 1;
+ $type = 2;
+ $notnull = 3;
+ $dflt_value = 4;
+ $pk = 5;
+
+ $desc = array();
+
+ $p = 1;
+ foreach ($result as $key => $row) {
+ list($length, $scale, $precision, $primary, $primaryPosition, $identity) =
+ array(null, null, null, false, null, false);
+ if (preg_match('/^((?:var)?char)\((\d+)\)/i', $row[$type], $matches)) {
+ $row[$type] = $matches[1];
+ $length = $matches[2];
+ } else if (preg_match('/^decimal\((\d+),(\d+)\)/i', $row[$type], $matches)) {
+ $row[$type] = 'DECIMAL';
+ $precision = $matches[1];
+ $scale = $matches[2];
+ }
+ if ((bool) $row[$pk]) {
+ $primary = true;
+ $primaryPosition = $p;
+ /**
+ * SQLite INTEGER primary key is always auto-increment.
+ */
+ $identity = (bool) ($row[$type] == 'INTEGER');
+ ++$p;
+ }
+ $desc[$this->foldCase($row[$name])] = array(
+ 'SCHEMA_NAME' => $this->foldCase($schemaName),
+ 'TABLE_NAME' => $this->foldCase($tableName),
+ 'COLUMN_NAME' => $this->foldCase($row[$name]),
+ 'COLUMN_POSITION' => $row[$cid]+1,
+ 'DATA_TYPE' => $row[$type],
+ 'DEFAULT' => $row[$dflt_value],
+ 'NULLABLE' => ! (bool) $row[$notnull],
+ 'LENGTH' => $length,
+ 'SCALE' => $scale,
+ 'PRECISION' => $precision,
+ 'UNSIGNED' => null, // Sqlite3 does not support unsigned data
+ 'PRIMARY' => $primary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity
+ );
+ }
+ return $desc;
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @return string
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ $sql .= " LIMIT $count";
+ if ($offset > 0) {
+ $sql .= " OFFSET $offset";
+ }
+
+ return $sql;
+ }
+
+}
diff --git a/library/Zend/Db/Adapter/Sqlsrv.php b/library/Zend/Db/Adapter/Sqlsrv.php
new file mode 100644
index 0000000..ecb04e3
--- /dev/null
+++ b/library/Zend/Db/Adapter/Sqlsrv.php
@@ -0,0 +1,673 @@
+ (string) Connect to the database as this username.
+ * password => (string) Password associated with the username.
+ * dbname => The name of the local SQL Server instance
+ *
+ * @var array
+ */
+ protected $_config = array(
+ 'dbname' => null,
+ 'username' => null,
+ 'password' => null,
+ );
+
+ /**
+ * Last insert id from INSERT query
+ *
+ * @var int
+ */
+ protected $_lastInsertId;
+
+ /**
+ * Query used to fetch last insert id
+ *
+ * @var string
+ */
+ protected $_lastInsertSQL = 'SELECT SCOPE_IDENTITY() as Current_Identity';
+
+ /**
+ * Keys are UPPERCASE SQL datatypes or the constants
+ * Zend_Db::INT_TYPE, Zend_Db::BIGINT_TYPE, or Zend_Db::FLOAT_TYPE.
+ *
+ * Values are:
+ * 0 = 32-bit integer
+ * 1 = 64-bit integer
+ * 2 = float or decimal
+ *
+ * @var array Associative array of datatypes to values 0, 1, or 2.
+ */
+ protected $_numericDataTypes = array(
+ Zend_Db::INT_TYPE => Zend_Db::INT_TYPE,
+ Zend_Db::BIGINT_TYPE => Zend_Db::BIGINT_TYPE,
+ Zend_Db::FLOAT_TYPE => Zend_Db::FLOAT_TYPE,
+ 'INT' => Zend_Db::INT_TYPE,
+ 'SMALLINT' => Zend_Db::INT_TYPE,
+ 'TINYINT' => Zend_Db::INT_TYPE,
+ 'BIGINT' => Zend_Db::BIGINT_TYPE,
+ 'DECIMAL' => Zend_Db::FLOAT_TYPE,
+ 'FLOAT' => Zend_Db::FLOAT_TYPE,
+ 'MONEY' => Zend_Db::FLOAT_TYPE,
+ 'NUMERIC' => Zend_Db::FLOAT_TYPE,
+ 'REAL' => Zend_Db::FLOAT_TYPE,
+ 'SMALLMONEY' => Zend_Db::FLOAT_TYPE,
+ );
+
+ /**
+ * Default class name for a DB statement.
+ *
+ * @var string
+ */
+ protected $_defaultStmtClass = 'Zend_Db_Statement_Sqlsrv';
+
+ /**
+ * Creates a connection resource.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ protected function _connect()
+ {
+ if (is_resource($this->_connection)) {
+ // connection already exists
+ return;
+ }
+
+ if (!extension_loaded('sqlsrv')) {
+ /**
+ * @see Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception('The Sqlsrv extension is required for this adapter but the extension is not loaded');
+ }
+
+ $serverName = $this->_config['host'];
+ if (isset($this->_config['port'])) {
+ $port = (integer) $this->_config['port'];
+ $serverName .= ', ' . $port;
+ }
+
+ $connectionInfo = array(
+ 'Database' => $this->_config['dbname'],
+ );
+
+ if (isset($this->_config['username']) && isset($this->_config['password']))
+ {
+ $connectionInfo += array(
+ 'UID' => $this->_config['username'],
+ 'PWD' => $this->_config['password'],
+ );
+ }
+ // else - windows authentication
+
+ if (!empty($this->_config['driver_options'])) {
+ foreach ($this->_config['driver_options'] as $option => $value) {
+ // A value may be a constant.
+ if (is_string($value)) {
+ $constantName = strtoupper($value);
+ if (defined($constantName)) {
+ $connectionInfo[$option] = constant($constantName);
+ } else {
+ $connectionInfo[$option] = $value;
+ }
+ }
+ }
+ }
+
+ $this->_connection = sqlsrv_connect($serverName, $connectionInfo);
+
+ if (!$this->_connection) {
+ /**
+ * @see Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
+ }
+ }
+
+ /**
+ * Check for config options that are mandatory.
+ * Throw exceptions if any are missing.
+ *
+ * @param array $config
+ * @throws Zend_Db_Adapter_Exception
+ */
+ protected function _checkRequiredOptions(array $config)
+ {
+ // we need at least a dbname
+ if (! array_key_exists('dbname', $config)) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'dbname' that names the database instance");
+ }
+
+ if (! array_key_exists('password', $config) && array_key_exists('username', $config)) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'password' for login credentials.
+ If Windows Authentication is desired, both keys 'username' and 'password' should be ommited from config.");
+ }
+
+ if (array_key_exists('password', $config) && !array_key_exists('username', $config)) {
+ /**
+ * @see Zend_Db_Adapter_Exception
+ */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("Configuration array must have a key for 'username' for login credentials.
+ If Windows Authentication is desired, both keys 'username' and 'password' should be ommited from config.");
+ }
+ }
+
+ /**
+ * Set the transaction isoltion level.
+ *
+ * @param integer|null $level A fetch mode from SQLSRV_TXN_*.
+ * @return true
+ * @throws Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ public function setTransactionIsolationLevel($level = null)
+ {
+ $this->_connect();
+ $sql = null;
+
+ // Default transaction level in sql server
+ if ($level === null)
+ {
+ $level = SQLSRV_TXN_READ_COMMITTED;
+ }
+
+ switch ($level) {
+ case SQLSRV_TXN_READ_UNCOMMITTED:
+ $sql = "READ UNCOMMITTED";
+ break;
+ case SQLSRV_TXN_READ_COMMITTED:
+ $sql = "READ COMMITTED";
+ break;
+ case SQLSRV_TXN_REPEATABLE_READ:
+ $sql = "REPEATABLE READ";
+ break;
+ case SQLSRV_TXN_SNAPSHOT:
+ $sql = "SNAPSHOT";
+ break;
+ case SQLSRV_TXN_SERIALIZABLE:
+ $sql = "SERIALIZABLE";
+ break;
+ default:
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception("Invalid transaction isolation level mode '$level' specified");
+ }
+
+ if (!sqlsrv_query($this->_connection, "SET TRANSACTION ISOLATION LEVEL $sql;")) {
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception("Transaction cannot be changed to '$level'");
+ }
+
+ return true;
+ }
+
+ /**
+ * Test if a connection is active
+ *
+ * @return boolean
+ */
+ public function isConnected()
+ {
+ return (is_resource($this->_connection)
+ && (get_resource_type($this->_connection) == 'SQL Server Connection')
+ );
+ }
+
+ /**
+ * Force the connection to close.
+ *
+ * @return void
+ */
+ public function closeConnection()
+ {
+ if ($this->isConnected()) {
+ sqlsrv_close($this->_connection);
+ }
+ $this->_connection = null;
+ }
+
+ /**
+ * Returns an SQL statement for preparation.
+ *
+ * @param string $sql The SQL statement with placeholders.
+ * @return Zend_Db_Statement_Sqlsrv
+ */
+ public function prepare($sql)
+ {
+ $this->_connect();
+ $stmtClass = $this->_defaultStmtClass;
+
+ if (!class_exists($stmtClass)) {
+ /**
+ * @see Zend_Loader
+ */
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($stmtClass);
+ }
+
+ $stmt = new $stmtClass($this, $sql);
+ $stmt->setFetchMode($this->_fetchMode);
+ return $stmt;
+ }
+
+ /**
+ * Quote a raw string.
+ *
+ * @param string $value Raw string
+ * @return string Quoted string
+ */
+ protected function _quote($value)
+ {
+ if (is_int($value)) {
+ return $value;
+ } elseif (is_float($value)) {
+ return sprintf('%F', $value);
+ }
+
+ return "'" . str_replace("'", "''", $value) . "'";
+ }
+
+ /**
+ * Gets the last ID generated automatically by an IDENTITY/AUTOINCREMENT column.
+ *
+ * As a convention, on RDBMS brands that support sequences
+ * (e.g. Oracle, PostgreSQL, DB2), this method forms the name of a sequence
+ * from the arguments and returns the last id generated by that sequence.
+ * On RDBMS brands that support IDENTITY/AUTOINCREMENT columns, this method
+ * returns the last value generated for such a column, and the table name
+ * argument is disregarded.
+ *
+ * @param string $tableName OPTIONAL Name of table.
+ * @param string $primaryKey OPTIONAL Name of primary key column.
+ * @return string
+ */
+ public function lastInsertId($tableName = null, $primaryKey = null)
+ {
+ if ($tableName) {
+ $tableName = $this->quote($tableName);
+ $sql = 'SELECT IDENT_CURRENT (' . $tableName . ') as Current_Identity';
+ return (string) $this->fetchOne($sql);
+ }
+
+ if ($this->_lastInsertId > 0) {
+ return (string) $this->_lastInsertId;
+ }
+
+ $sql = $this->_lastInsertSQL;
+ return (string) $this->fetchOne($sql);
+ }
+
+ /**
+ * Inserts a table row with specified data.
+ *
+ * @param mixed $table The table to insert data into.
+ * @param array $bind Column-value pairs.
+ * @return int The number of affected rows.
+ */
+ public function insert($table, array $bind)
+ {
+ // extract and quote col names from the array keys
+ $cols = array();
+ $vals = array();
+ foreach ($bind as $col => $val) {
+ $cols[] = $this->quoteIdentifier($col, true);
+ if ($val instanceof Zend_Db_Expr) {
+ $vals[] = $val->__toString();
+ unset($bind[$col]);
+ } else {
+ $vals[] = '?';
+ }
+ }
+
+ // build the statement
+ $sql = "INSERT INTO "
+ . $this->quoteIdentifier($table, true)
+ . ' (' . implode(', ', $cols) . ') '
+ . 'VALUES (' . implode(', ', $vals) . ')'
+ . ' ' . $this->_lastInsertSQL;
+
+ // execute the statement and return the number of affected rows
+ $stmt = $this->query($sql, array_values($bind));
+ $result = $stmt->rowCount();
+
+ $stmt->nextRowset();
+
+ $this->_lastInsertId = $stmt->fetchColumn();
+
+ return $result;
+ }
+
+ /**
+ * Returns a list of the tables in the database.
+ *
+ * @return array
+ */
+ public function listTables()
+ {
+ $this->_connect();
+ $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
+ return $this->fetchCol($sql);
+ }
+
+ /**
+ * Returns the column descriptions for a table.
+ *
+ * The return value is an associative array keyed by the column name,
+ * as returned by the RDBMS.
+ *
+ * The value of each array element is an associative array
+ * with the following keys:
+ *
+ * SCHEMA_NAME => string; name of schema
+ * TABLE_NAME => string;
+ * COLUMN_NAME => string; column name
+ * COLUMN_POSITION => number; ordinal position of column in table
+ * DATA_TYPE => string; SQL datatype name of column
+ * DEFAULT => string; default expression of column, null if none
+ * NULLABLE => boolean; true if column can have nulls
+ * LENGTH => number; length of CHAR/VARCHAR
+ * SCALE => number; scale of NUMERIC/DECIMAL
+ * PRECISION => number; precision of NUMERIC/DECIMAL
+ * UNSIGNED => boolean; unsigned property of an integer type
+ * PRIMARY => boolean; true if column is part of the primary key
+ * PRIMARY_POSITION => integer; position of column in primary key
+ * IDENTITY => integer; true if column is auto-generated with unique values
+ *
+ * @todo Discover integer unsigned property.
+ *
+ * @param string $tableName
+ * @param string $schemaName OPTIONAL
+ * @return array
+ */
+ public function describeTable($tableName, $schemaName = null)
+ {
+ /**
+ * Discover metadata information about this table.
+ */
+ $sql = "exec sp_columns @table_name = " . $this->quoteIdentifier($tableName, true);
+ $stmt = $this->query($sql);
+ $result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+
+ // ZF-7698
+ $stmt->closeCursor();
+
+ if (count($result) == 0) {
+ return array();
+ }
+
+ $owner = 1;
+ $table_name = 2;
+ $column_name = 3;
+ $type_name = 5;
+ $precision = 6;
+ $length = 7;
+ $scale = 8;
+ $nullable = 10;
+ $column_def = 12;
+ $column_position = 16;
+
+ /**
+ * Discover primary key column(s) for this table.
+ */
+ $tableOwner = $result[0][$owner];
+ $sql = "exec sp_pkeys @table_owner = " . $tableOwner
+ . ", @table_name = " . $this->quoteIdentifier($tableName, true);
+ $stmt = $this->query($sql);
+
+ $primaryKeysResult = $stmt->fetchAll(Zend_Db::FETCH_NUM);
+ $primaryKeyColumn = array();
+
+ // Per http://msdn.microsoft.com/en-us/library/ms189813.aspx,
+ // results from sp_keys stored procedure are:
+ // 0=TABLE_QUALIFIER 1=TABLE_OWNER 2=TABLE_NAME 3=COLUMN_NAME 4=KEY_SEQ 5=PK_NAME
+
+ $pkey_column_name = 3;
+ $pkey_key_seq = 4;
+ foreach ($primaryKeysResult as $pkeysRow) {
+ $primaryKeyColumn[$pkeysRow[$pkey_column_name]] = $pkeysRow[$pkey_key_seq];
+ }
+
+ $desc = array();
+ $p = 1;
+ foreach ($result as $key => $row) {
+ $identity = false;
+ $words = explode(' ', $row[$type_name], 2);
+ if (isset($words[0])) {
+ $type = $words[0];
+ if (isset($words[1])) {
+ $identity = (bool) preg_match('/identity/', $words[1]);
+ }
+ }
+
+ $isPrimary = array_key_exists($row[$column_name], $primaryKeyColumn);
+ if ($isPrimary) {
+ $primaryPosition = $primaryKeyColumn[$row[$column_name]];
+ } else {
+ $primaryPosition = null;
+ }
+
+ $desc[$this->foldCase($row[$column_name])] = array(
+ 'SCHEMA_NAME' => null, // @todo
+ 'TABLE_NAME' => $this->foldCase($row[$table_name]),
+ 'COLUMN_NAME' => $this->foldCase($row[$column_name]),
+ 'COLUMN_POSITION' => (int) $row[$column_position],
+ 'DATA_TYPE' => $type,
+ 'DEFAULT' => $row[$column_def],
+ 'NULLABLE' => (bool) $row[$nullable],
+ 'LENGTH' => $row[$length],
+ 'SCALE' => $row[$scale],
+ 'PRECISION' => $row[$precision],
+ 'UNSIGNED' => null, // @todo
+ 'PRIMARY' => $isPrimary,
+ 'PRIMARY_POSITION' => $primaryPosition,
+ 'IDENTITY' => $identity,
+ );
+ }
+
+ return $desc;
+ }
+
+ /**
+ * Leave autocommit mode and begin a transaction.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ protected function _beginTransaction()
+ {
+ if (!sqlsrv_begin_transaction($this->_connection)) {
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
+ }
+ }
+
+ /**
+ * Commit a transaction and return to autocommit mode.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ protected function _commit()
+ {
+ if (!sqlsrv_commit($this->_connection)) {
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
+ }
+ }
+
+ /**
+ * Roll back a transaction and return to autocommit mode.
+ *
+ * @return void
+ * @throws Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ protected function _rollBack()
+ {
+ if (!sqlsrv_rollback($this->_connection)) {
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception(sqlsrv_errors());
+ }
+ }
+
+ /**
+ * Set the fetch mode.
+ *
+ * @todo Support FETCH_CLASS and FETCH_INTO.
+ *
+ * @param integer $mode A fetch mode.
+ * @return void
+ * @throws Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ public function setFetchMode($mode)
+ {
+ switch ($mode) {
+ case Zend_Db::FETCH_NUM: // seq array
+ case Zend_Db::FETCH_ASSOC: // assoc array
+ case Zend_Db::FETCH_BOTH: // seq+assoc array
+ case Zend_Db::FETCH_OBJ: // object
+ $this->_fetchMode = $mode;
+ break;
+ case Zend_Db::FETCH_BOUND: // bound to PHP variable
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception('FETCH_BOUND is not supported yet');
+ break;
+ default:
+ require_once 'Zend/Db/Adapter/Sqlsrv/Exception.php';
+ throw new Zend_Db_Adapter_Sqlsrv_Exception("Invalid fetch mode '$mode' specified");
+ break;
+ }
+ }
+
+ /**
+ * Adds an adapter-specific LIMIT clause to the SELECT statement.
+ *
+ * @param string $sql
+ * @param integer $count
+ * @param integer $offset OPTIONAL
+ * @return string
+ * @throws Zend_Db_Adapter_Sqlsrv_Exception
+ */
+ public function limit($sql, $count, $offset = 0)
+ {
+ $count = intval($count);
+ if ($count <= 0) {
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument count=$count is not valid");
+ }
+
+ $offset = intval($offset);
+ if ($offset < 0) {
+ /** @see Zend_Db_Adapter_Exception */
+ require_once 'Zend/Db/Adapter/Exception.php';
+ throw new Zend_Db_Adapter_Exception("LIMIT argument offset=$offset is not valid");
+ }
+
+ if ($offset == 0) {
+ $sql = preg_replace('/^SELECT\s/i', 'SELECT TOP ' . $count . ' ', $sql);
+ } else {
+ $orderby = stristr($sql, 'ORDER BY');
+
+ if (!$orderby) {
+ $over = 'ORDER BY (SELECT 0)';
+ } else {
+ $over = preg_replace('/\"[^,]*\".\"([^,]*)\"/i', '"inner_tbl"."$1"', $orderby);
+ }
+
+ // Remove ORDER BY clause from $sql
+ $sql = preg_replace('/\s+ORDER BY(.*)/', '', $sql);
+
+ // Add ORDER BY clause as an argument for ROW_NUMBER()
+ $sql = "SELECT ROW_NUMBER() OVER ($over) AS \"ZEND_DB_ROWNUM\", * FROM ($sql) AS inner_tbl";
+
+ $start = $offset + 1;
+ $end = $offset + $count;
+
+ $sql = "WITH outer_tbl AS ($sql) SELECT * FROM outer_tbl WHERE \"ZEND_DB_ROWNUM\" BETWEEN $start AND $end";
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Check if the adapter supports real SQL parameters.
+ *
+ * @param string $type 'positional' or 'named'
+ * @return bool
+ */
+ public function supportsParameters($type)
+ {
+ if ($type == 'positional') {
+ return true;
+ }
+
+ // if its 'named' or anything else
+ return false;
+ }
+
+ /**
+ * Retrieve server version in PHP style
+ *
+ * @return string
+ */
+ public function getServerVersion()
+ {
+ $this->_connect();
+ $serverInfo = sqlsrv_server_info($this->_connection);
+
+ if ($serverInfo !== false) {
+ return $serverInfo['SQLServerVersion'];
+ }
+
+ return null;
+ }
+}
diff --git a/library/Zend/Db/Adapter/Sqlsrv/Exception.php b/library/Zend/Db/Adapter/Sqlsrv/Exception.php
new file mode 100644
index 0000000..ac447c0
--- /dev/null
+++ b/library/Zend/Db/Adapter/Sqlsrv/Exception.php
@@ -0,0 +1,63 @@
+_expression = (string) $expression;
+ }
+
+ /**
+ * @return string The string of the SQL expression stored in this object.
+ */
+ public function __toString()
+ {
+ return $this->_expression;
+ }
+
+}
diff --git a/library/Zend/Db/Profiler.php b/library/Zend/Db/Profiler.php
new file mode 100644
index 0000000..7d1fd3a
--- /dev/null
+++ b/library/Zend/Db/Profiler.php
@@ -0,0 +1,471 @@
+setEnabled($enabled);
+ }
+
+ /**
+ * Enable or disable the profiler. If $enable is false, the profiler
+ * is disabled and will not log any queries sent to it.
+ *
+ * @param boolean $enable
+ * @return Zend_Db_Profiler Provides a fluent interface
+ */
+ public function setEnabled($enable)
+ {
+ $this->_enabled = (boolean) $enable;
+
+ return $this;
+ }
+
+ /**
+ * Get the current state of enable. If True is returned,
+ * the profiler is enabled.
+ *
+ * @return boolean
+ */
+ public function getEnabled()
+ {
+ return $this->_enabled;
+ }
+
+ /**
+ * Sets a minimum number of seconds for saving query profiles. If this
+ * is set, only those queries whose elapsed time is equal or greater than
+ * $minimumSeconds will be saved. To save all queries regardless of
+ * elapsed time, set $minimumSeconds to null.
+ *
+ * @param integer $minimumSeconds OPTIONAL
+ * @return Zend_Db_Profiler Provides a fluent interface
+ */
+ public function setFilterElapsedSecs($minimumSeconds = null)
+ {
+ if (null === $minimumSeconds) {
+ $this->_filterElapsedSecs = null;
+ } else {
+ $this->_filterElapsedSecs = (integer) $minimumSeconds;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the minimum number of seconds for saving query profiles, or null if
+ * query profiles are saved regardless of elapsed time.
+ *
+ * @return integer|null
+ */
+ public function getFilterElapsedSecs()
+ {
+ return $this->_filterElapsedSecs;
+ }
+
+ /**
+ * Sets the types of query profiles to save. Set $queryType to one of
+ * the Zend_Db_Profiler::* constants to only save profiles for that type of
+ * query. To save more than one type, logical OR them together. To
+ * save all queries regardless of type, set $queryType to null.
+ *
+ * @param integer $queryTypes OPTIONAL
+ * @return Zend_Db_Profiler Provides a fluent interface
+ */
+ public function setFilterQueryType($queryTypes = null)
+ {
+ $this->_filterTypes = $queryTypes;
+
+ return $this;
+ }
+
+ /**
+ * Returns the types of query profiles saved, or null if queries are saved regardless
+ * of their types.
+ *
+ * @return integer|null
+ * @see Zend_Db_Profiler::setFilterQueryType()
+ */
+ public function getFilterQueryType()
+ {
+ return $this->_filterTypes;
+ }
+
+ /**
+ * Clears the history of any past query profiles. This is relentless
+ * and will even clear queries that were started and may not have
+ * been marked as ended.
+ *
+ * @return Zend_Db_Profiler Provides a fluent interface
+ */
+ public function clear()
+ {
+ $this->_queryProfiles = array();
+
+ return $this;
+ }
+
+ /**
+ * @param integer $queryId
+ * @return integer or null
+ */
+ public function queryClone(Zend_Db_Profiler_Query $query)
+ {
+ $this->_queryProfiles[] = clone $query;
+
+ end($this->_queryProfiles);
+
+ return key($this->_queryProfiles);
+ }
+
+ /**
+ * Starts a query. Creates a new query profile object (Zend_Db_Profiler_Query)
+ * and returns the "query profiler handle". Run the query, then call
+ * queryEnd() and pass it this handle to make the query as ended and
+ * record the time. If the profiler is not enabled, this takes no
+ * action and immediately returns null.
+ *
+ * @param string $queryText SQL statement
+ * @param integer $queryType OPTIONAL Type of query, one of the Zend_Db_Profiler::* constants
+ * @return integer|null
+ */
+ public function queryStart($queryText, $queryType = null)
+ {
+ if (!$this->_enabled) {
+ return null;
+ }
+
+ // make sure we have a query type
+ if (null === $queryType) {
+ switch (strtolower(substr(ltrim($queryText), 0, 6))) {
+ case 'insert':
+ $queryType = self::INSERT;
+ break;
+ case 'update':
+ $queryType = self::UPDATE;
+ break;
+ case 'delete':
+ $queryType = self::DELETE;
+ break;
+ case 'select':
+ $queryType = self::SELECT;
+ break;
+ default:
+ $queryType = self::QUERY;
+ break;
+ }
+ }
+
+ /**
+ * @see Zend_Db_Profiler_Query
+ */
+ require_once 'Zend/Db/Profiler/Query.php';
+ $this->_queryProfiles[] = new Zend_Db_Profiler_Query($queryText, $queryType);
+
+ end($this->_queryProfiles);
+
+ return key($this->_queryProfiles);
+ }
+
+ /**
+ * Ends a query. Pass it the handle that was returned by queryStart().
+ * This will mark the query as ended and save the time.
+ *
+ * @param integer $queryId
+ * @throws Zend_Db_Profiler_Exception
+ * @return void
+ */
+ public function queryEnd($queryId)
+ {
+ // Don't do anything if the Zend_Db_Profiler is not enabled.
+ if (!$this->_enabled) {
+ return self::IGNORED;
+ }
+
+ // Check for a valid query handle.
+ if (!isset($this->_queryProfiles[$queryId])) {
+ /**
+ * @see Zend_Db_Profiler_Exception
+ */
+ require_once 'Zend/Db/Profiler/Exception.php';
+ throw new Zend_Db_Profiler_Exception("Profiler has no query with handle '$queryId'.");
+ }
+
+ $qp = $this->_queryProfiles[$queryId];
+
+ // Ensure that the query profile has not already ended
+ if ($qp->hasEnded()) {
+ /**
+ * @see Zend_Db_Profiler_Exception
+ */
+ require_once 'Zend/Db/Profiler/Exception.php';
+ throw new Zend_Db_Profiler_Exception("Query with profiler handle '$queryId' has already ended.");
+ }
+
+ // End the query profile so that the elapsed time can be calculated.
+ $qp->end();
+
+ /**
+ * If filtering by elapsed time is enabled, only keep the profile if
+ * it ran for the minimum time.
+ */
+ if (null !== $this->_filterElapsedSecs && $qp->getElapsedSecs() < $this->_filterElapsedSecs) {
+ unset($this->_queryProfiles[$queryId]);
+ return self::IGNORED;
+ }
+
+ /**
+ * If filtering by query type is enabled, only keep the query if
+ * it was one of the allowed types.
+ */
+ if (null !== $this->_filterTypes && !($qp->getQueryType() & $this->_filterTypes)) {
+ unset($this->_queryProfiles[$queryId]);
+ return self::IGNORED;
+ }
+
+ return self::STORED;
+ }
+
+ /**
+ * Get a profile for a query. Pass it the same handle that was returned
+ * by queryStart() and it will return a Zend_Db_Profiler_Query object.
+ *
+ * @param integer $queryId
+ * @throws Zend_Db_Profiler_Exception
+ * @return Zend_Db_Profiler_Query
+ */
+ public function getQueryProfile($queryId)
+ {
+ if (!array_key_exists($queryId, $this->_queryProfiles)) {
+ /**
+ * @see Zend_Db_Profiler_Exception
+ */
+ require_once 'Zend/Db/Profiler/Exception.php';
+ throw new Zend_Db_Profiler_Exception("Query handle '$queryId' not found in profiler log.");
+ }
+
+ return $this->_queryProfiles[$queryId];
+ }
+
+ /**
+ * Get an array of query profiles (Zend_Db_Profiler_Query objects). If $queryType
+ * is set to one of the Zend_Db_Profiler::* constants then only queries of that
+ * type will be returned. Normally, queries that have not yet ended will
+ * not be returned unless $showUnfinished is set to True. If no
+ * queries were found, False is returned. The returned array is indexed by the query
+ * profile handles.
+ *
+ * @param integer $queryType
+ * @param boolean $showUnfinished
+ * @return array|false
+ */
+ public function getQueryProfiles($queryType = null, $showUnfinished = false)
+ {
+ $queryProfiles = array();
+ foreach ($this->_queryProfiles as $key => $qp) {
+ if ($queryType === null) {
+ $condition = true;
+ } else {
+ $condition = ($qp->getQueryType() & $queryType);
+ }
+
+ if (($qp->hasEnded() || $showUnfinished) && $condition) {
+ $queryProfiles[$key] = $qp;
+ }
+ }
+
+ if (empty($queryProfiles)) {
+ $queryProfiles = false;
+ }
+
+ return $queryProfiles;
+ }
+
+ /**
+ * Get the total elapsed time (in seconds) of all of the profiled queries.
+ * Only queries that have ended will be counted. If $queryType is set to
+ * one or more of the Zend_Db_Profiler::* constants, the elapsed time will be calculated
+ * only for queries of the given type(s).
+ *
+ * @param integer $queryType OPTIONAL
+ * @return float
+ */
+ public function getTotalElapsedSecs($queryType = null)
+ {
+ $elapsedSecs = 0;
+ foreach ($this->_queryProfiles as $key => $qp) {
+ if (null === $queryType) {
+ $condition = true;
+ } else {
+ $condition = ($qp->getQueryType() & $queryType);
+ }
+ if (($qp->hasEnded()) && $condition) {
+ $elapsedSecs += $qp->getElapsedSecs();
+ }
+ }
+ return $elapsedSecs;
+ }
+
+ /**
+ * Get the total number of queries that have been profiled. Only queries that have ended will
+ * be counted. If $queryType is set to one of the Zend_Db_Profiler::* constants, only queries of
+ * that type will be counted.
+ *
+ * @param integer $queryType OPTIONAL
+ * @return integer
+ */
+ public function getTotalNumQueries($queryType = null)
+ {
+ if (null === $queryType) {
+ return count($this->_queryProfiles);
+ }
+
+ $numQueries = 0;
+ foreach ($this->_queryProfiles as $qp) {
+ if ($qp->hasEnded() && ($qp->getQueryType() & $queryType)) {
+ $numQueries++;
+ }
+ }
+
+ return $numQueries;
+ }
+
+ /**
+ * Get the Zend_Db_Profiler_Query object for the last query that was run, regardless if it has
+ * ended or not. If the query has not ended, its end time will be null. If no queries have
+ * been profiled, false is returned.
+ *
+ * @return Zend_Db_Profiler_Query|false
+ */
+ public function getLastQueryProfile()
+ {
+ if (empty($this->_queryProfiles)) {
+ return false;
+ }
+
+ end($this->_queryProfiles);
+
+ return current($this->_queryProfiles);
+ }
+
+}
+
diff --git a/library/Zend/Db/Profiler/Exception.php b/library/Zend/Db/Profiler/Exception.php
new file mode 100644
index 0000000..5afe932
--- /dev/null
+++ b/library/Zend/Db/Profiler/Exception.php
@@ -0,0 +1,40 @@
+_label = $label;
+ if(!$this->_label) {
+ $this->_label = 'Zend_Db_Profiler_Firebug';
+ }
+ }
+
+ /**
+ * Enable or disable the profiler. If $enable is false, the profiler
+ * is disabled and will not log any queries sent to it.
+ *
+ * @param boolean $enable
+ * @return Zend_Db_Profiler Provides a fluent interface
+ */
+ public function setEnabled($enable)
+ {
+ parent::setEnabled($enable);
+
+ if ($this->getEnabled()) {
+
+ if (!$this->_message) {
+ $this->_message = new Zend_Wildfire_Plugin_FirePhp_TableMessage($this->_label);
+ $this->_message->setBuffered(true);
+ $this->_message->setHeader(array('Time','Event','Parameters'));
+ $this->_message->setDestroy(true);
+ $this->_message->setOption('includeLineNumbers', false);
+ Zend_Wildfire_Plugin_FirePhp::getInstance()->send($this->_message);
+ }
+
+ } else {
+
+ if ($this->_message) {
+ $this->_message->setDestroy(true);
+ $this->_message = null;
+ }
+
+ }
+
+ return $this;
+ }
+
+ /**
+ * Intercept the query end and log the profiling data.
+ *
+ * @param integer $queryId
+ * @throws Zend_Db_Profiler_Exception
+ * @return void
+ */
+ public function queryEnd($queryId)
+ {
+ $state = parent::queryEnd($queryId);
+
+ if (!$this->getEnabled() || $state == self::IGNORED) {
+ return;
+ }
+
+ $this->_message->setDestroy(false);
+
+ $profile = $this->getQueryProfile($queryId);
+
+ $this->_totalElapsedTime += $profile->getElapsedSecs();
+
+ $this->_message->addRow(array((string)round($profile->getElapsedSecs(),5),
+ $profile->getQuery(),
+ ($params=$profile->getQueryParams())?$params:null));
+
+ $this->updateMessageLabel();
+ }
+
+ /**
+ * Update the label of the message holding the profile info.
+ *
+ * @return void
+ */
+ protected function updateMessageLabel()
+ {
+ if (!$this->_message) {
+ return;
+ }
+ $this->_message->setLabel(str_replace(array('%label%',
+ '%totalCount%',
+ '%totalDuration%'),
+ array($this->_label,
+ $this->getTotalNumQueries(),
+ (string)round($this->_totalElapsedTime,5)),
+ $this->_label_template));
+ }
+}
diff --git a/library/Zend/Db/Profiler/Query.php b/library/Zend/Db/Profiler/Query.php
new file mode 100644
index 0000000..6f3a9b4
--- /dev/null
+++ b/library/Zend/Db/Profiler/Query.php
@@ -0,0 +1,213 @@
+_query = $query;
+ $this->_queryType = $queryType;
+ // by default, and for backward-compatibility, start the click ticking
+ $this->start();
+ }
+
+ /**
+ * Clone handler for the query object.
+ * @return void
+ */
+ public function __clone()
+ {
+ $this->_boundParams = array();
+ $this->_endedMicrotime = null;
+ $this->start();
+ }
+
+ /**
+ * Starts the elapsed time click ticking.
+ * This can be called subsequent to object creation,
+ * to restart the clock. For instance, this is useful
+ * right before executing a prepared query.
+ *
+ * @return void
+ */
+ public function start()
+ {
+ $this->_startedMicrotime = microtime(true);
+ }
+
+ /**
+ * Ends the query and records the time so that the elapsed time can be determined later.
+ *
+ * @return void
+ */
+ public function end()
+ {
+ $this->_endedMicrotime = microtime(true);
+ }
+
+ /**
+ * Returns true if and only if the query has ended.
+ *
+ * @return boolean
+ */
+ public function hasEnded()
+ {
+ return $this->_endedMicrotime !== null;
+ }
+
+ /**
+ * Get the original SQL text of the query.
+ *
+ * @return string
+ */
+ public function getQuery()
+ {
+ return $this->_query;
+ }
+
+ /**
+ * Get the type of this query (one of the Zend_Db_Profiler::* constants)
+ *
+ * @return integer
+ */
+ public function getQueryType()
+ {
+ return $this->_queryType;
+ }
+
+ /**
+ * @param string $param
+ * @param mixed $variable
+ * @return void
+ */
+ public function bindParam($param, $variable)
+ {
+ $this->_boundParams[$param] = $variable;
+ }
+
+ /**
+ * @param array $param
+ * @return void
+ */
+ public function bindParams(array $params)
+ {
+ if (array_key_exists(0, $params)) {
+ array_unshift($params, null);
+ unset($params[0]);
+ }
+ foreach ($params as $param => $value) {
+ $this->bindParam($param, $value);
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getQueryParams()
+ {
+ return $this->_boundParams;
+ }
+
+ /**
+ * Get the elapsed time (in seconds) that the query ran.
+ * If the query has not yet ended, false is returned.
+ *
+ * @return float|false
+ */
+ public function getElapsedSecs()
+ {
+ if (null === $this->_endedMicrotime) {
+ return false;
+ }
+
+ return $this->_endedMicrotime - $this->_startedMicrotime;
+ }
+
+ /**
+ * Get the time (in seconds) when the profiler started running.
+ *
+ * @return bool|float
+ */
+ public function getStartedMicrotime()
+ {
+ if(null === $this->_startedMicrotime) {
+ return false;
+ }
+
+ return $this->_startedMicrotime;
+ }
+}
+
diff --git a/library/Zend/Db/Select.php b/library/Zend/Db/Select.php
new file mode 100644
index 0000000..29e8757
--- /dev/null
+++ b/library/Zend/Db/Select.php
@@ -0,0 +1,1351 @@
+ false,
+ self::COLUMNS => array(),
+ self::UNION => array(),
+ self::FROM => array(),
+ self::WHERE => array(),
+ self::GROUP => array(),
+ self::HAVING => array(),
+ self::ORDER => array(),
+ self::LIMIT_COUNT => null,
+ self::LIMIT_OFFSET => null,
+ self::FOR_UPDATE => false
+ );
+
+ /**
+ * Specify legal join types.
+ *
+ * @var array
+ */
+ protected static $_joinTypes = array(
+ self::INNER_JOIN,
+ self::LEFT_JOIN,
+ self::RIGHT_JOIN,
+ self::FULL_JOIN,
+ self::CROSS_JOIN,
+ self::NATURAL_JOIN,
+ );
+
+ /**
+ * Specify legal union types.
+ *
+ * @var array
+ */
+ protected static $_unionTypes = array(
+ self::SQL_UNION,
+ self::SQL_UNION_ALL
+ );
+
+ /**
+ * The component parts of a SELECT statement.
+ * Initialized to the $_partsInit array in the constructor.
+ *
+ * @var array
+ */
+ protected $_parts = array();
+
+ /**
+ * Tracks which columns are being select from each table and join.
+ *
+ * @var array
+ */
+ protected $_tableCols = array();
+
+ /**
+ * Class constructor
+ *
+ * @param Zend_Db_Adapter_Abstract $adapter
+ */
+ public function __construct(Zend_Db_Adapter_Abstract $adapter)
+ {
+ $this->_adapter = $adapter;
+ $this->_parts = self::$_partsInit;
+ }
+
+ /**
+ * Get bind variables
+ *
+ * @return array
+ */
+ public function getBind()
+ {
+ return $this->_bind;
+ }
+
+ /**
+ * Set bind variables
+ *
+ * @param mixed $bind
+ * @return Zend_Db_Select
+ */
+ public function bind($bind)
+ {
+ $this->_bind = $bind;
+
+ return $this;
+ }
+
+ /**
+ * Makes the query SELECT DISTINCT.
+ *
+ * @param bool $flag Whether or not the SELECT is DISTINCT (default true).
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function distinct($flag = true)
+ {
+ $this->_parts[self::DISTINCT] = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Adds a FROM table and optional columns to the query.
+ *
+ * The first parameter $name can be a simple string, in which case the
+ * correlation name is generated automatically. If you want to specify
+ * the correlation name, the first parameter must be an associative
+ * array in which the key is the correlation name, and the value is
+ * the physical table name. For example, array('alias' => 'table').
+ * The correlation name is prepended to all columns fetched for this
+ * table.
+ *
+ * The second parameter can be a single string or Zend_Db_Expr object,
+ * or else an array of strings or Zend_Db_Expr objects.
+ *
+ * The first parameter can be null or an empty string, in which case
+ * no correlation name is generated or prepended to the columns named
+ * in the second parameter.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name or an associative array
+ * relating correlation name to table name.
+ * @param array|string|Zend_Db_Expr $cols The columns to select from this table.
+ * @param string $schema The schema name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function from($name, $cols = '*', $schema = null)
+ {
+ return $this->_join(self::FROM, $name, null, $cols, $schema);
+ }
+
+ /**
+ * Specifies the columns used in the FROM clause.
+ *
+ * The parameter can be a single string or Zend_Db_Expr object,
+ * or else an array of strings or Zend_Db_Expr objects.
+ *
+ * @param array|string|Zend_Db_Expr $cols The columns to select from this table.
+ * @param string $correlationName Correlation name of target table. OPTIONAL
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function columns($cols = '*', $correlationName = null)
+ {
+ if ($correlationName === null && count($this->_parts[self::FROM])) {
+ $correlationNameKeys = array_keys($this->_parts[self::FROM]);
+ $correlationName = current($correlationNameKeys);
+ }
+
+ if (!array_key_exists($correlationName, $this->_parts[self::FROM])) {
+ /**
+ * @see Zend_Db_Select_Exception
+ */
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("No table has been specified for the FROM clause");
+ }
+
+ $this->_tableCols($correlationName, $cols);
+
+ return $this;
+ }
+
+ /**
+ * Adds a UNION clause to the query.
+ *
+ * The first parameter has to be an array of Zend_Db_Select or
+ * sql query strings.
+ *
+ *
+ * $sql1 = $db->select();
+ * $sql2 = "SELECT ...";
+ * $select = $db->select()
+ * ->union(array($sql1, $sql2))
+ * ->order("id");
+ *
+ *
+ * @param array $select Array of select clauses for the union.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function union($select = array(), $type = self::SQL_UNION)
+ {
+ if (!is_array($select)) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception(
+ "union() only accepts an array of Zend_Db_Select instances of sql query strings."
+ );
+ }
+
+ if (!in_array($type, self::$_unionTypes)) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Invalid union type '{$type}'");
+ }
+
+ foreach ($select as $target) {
+ $this->_parts[self::UNION][] = array($target, $type);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Adds a JOIN table and columns to the query.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name.
+ * @param string $cond Join on this condition.
+ * @param array|string $cols The columns to select from the joined table.
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function join($name, $cond, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ return $this->joinInner($name, $cond, $cols, $schema);
+ }
+
+ /**
+ * Add an INNER JOIN table and colums to the query
+ * Rows in both tables are matched according to the expression
+ * in the $cond argument. The result set is comprised
+ * of all cases where rows from the left table match
+ * rows from the right table.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name.
+ * @param string $cond Join on this condition.
+ * @param array|string $cols The columns to select from the joined table.
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function joinInner($name, $cond, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ return $this->_join(self::INNER_JOIN, $name, $cond, $cols, $schema);
+ }
+
+ /**
+ * Add a LEFT OUTER JOIN table and colums to the query
+ * All rows from the left operand table are included,
+ * matching rows from the right operand table included,
+ * and the columns from the right operand table are filled
+ * with NULLs if no row exists matching the left table.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name.
+ * @param string $cond Join on this condition.
+ * @param array|string $cols The columns to select from the joined table.
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function joinLeft($name, $cond, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ return $this->_join(self::LEFT_JOIN, $name, $cond, $cols, $schema);
+ }
+
+ /**
+ * Add a RIGHT OUTER JOIN table and colums to the query.
+ * Right outer join is the complement of left outer join.
+ * All rows from the right operand table are included,
+ * matching rows from the left operand table included,
+ * and the columns from the left operand table are filled
+ * with NULLs if no row exists matching the right table.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name.
+ * @param string $cond Join on this condition.
+ * @param array|string $cols The columns to select from the joined table.
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function joinRight($name, $cond, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ return $this->_join(self::RIGHT_JOIN, $name, $cond, $cols, $schema);
+ }
+
+ /**
+ * Add a FULL OUTER JOIN table and colums to the query.
+ * A full outer join is like combining a left outer join
+ * and a right outer join. All rows from both tables are
+ * included, paired with each other on the same row of the
+ * result set if they satisfy the join condition, and otherwise
+ * paired with NULLs in place of columns from the other table.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name.
+ * @param string $cond Join on this condition.
+ * @param array|string $cols The columns to select from the joined table.
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function joinFull($name, $cond, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ return $this->_join(self::FULL_JOIN, $name, $cond, $cols, $schema);
+ }
+
+ /**
+ * Add a CROSS JOIN table and colums to the query.
+ * A cross join is a cartesian product; there is no join condition.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name.
+ * @param array|string $cols The columns to select from the joined table.
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function joinCross($name, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ return $this->_join(self::CROSS_JOIN, $name, null, $cols, $schema);
+ }
+
+ /**
+ * Add a NATURAL JOIN table and colums to the query.
+ * A natural join assumes an equi-join across any column(s)
+ * that appear with the same name in both tables.
+ * Only natural inner joins are supported by this API,
+ * even though SQL permits natural outer joins as well.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param array|string|Zend_Db_Expr $name The table name.
+ * @param array|string $cols The columns to select from the joined table.
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function joinNatural($name, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ return $this->_join(self::NATURAL_JOIN, $name, null, $cols, $schema);
+ }
+
+ /**
+ * Adds a WHERE condition to the query by AND.
+ *
+ * If a value is passed as the second param, it will be quoted
+ * and replaced into the condition wherever a question-mark
+ * appears. Array values are quoted and comma-separated.
+ *
+ *
+ * // simplest but non-secure
+ * $select->where("id = $id");
+ *
+ * // secure (ID is quoted but matched anyway)
+ * $select->where('id = ?', $id);
+ *
+ * // alternatively, with named binding
+ * $select->where('id = :id');
+ *
+ *
+ * Note that it is more correct to use named bindings in your
+ * queries for values other than strings. When you use named
+ * bindings, don't forget to pass the values when actually
+ * making a query:
+ *
+ *
+ * $db->fetchAll($select, array('id' => 5));
+ *
+ *
+ * @param string $cond The WHERE condition.
+ * @param mixed $value OPTIONAL The value to quote into the condition.
+ * @param int $type OPTIONAL The type of the given value
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function where($cond, $value = null, $type = null)
+ {
+ $this->_parts[self::WHERE][] = $this->_where($cond, $value, $type, true);
+
+ return $this;
+ }
+
+ /**
+ * Adds a WHERE condition to the query by OR.
+ *
+ * Otherwise identical to where().
+ *
+ * @param string $cond The WHERE condition.
+ * @param mixed $value OPTIONAL The value to quote into the condition.
+ * @param int $type OPTIONAL The type of the given value
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ *
+ * @see where()
+ */
+ public function orWhere($cond, $value = null, $type = null)
+ {
+ $this->_parts[self::WHERE][] = $this->_where($cond, $value, $type, false);
+
+ return $this;
+ }
+
+ /**
+ * Adds grouping to the query.
+ *
+ * @param array|string $spec The column(s) to group by.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function group($spec)
+ {
+ if (!is_array($spec)) {
+ $spec = array($spec);
+ }
+
+ foreach ($spec as $val) {
+ if (preg_match('/\(.*\)/', (string) $val)) {
+ $val = new Zend_Db_Expr($val);
+ }
+ $this->_parts[self::GROUP][] = $val;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Adds a HAVING condition to the query by AND.
+ *
+ * If a value is passed as the second param, it will be quoted
+ * and replaced into the condition wherever a question-mark
+ * appears. See {@link where()} for an example
+ *
+ * @param string $cond The HAVING condition.
+ * @param mixed $value OPTIONAL The value to quote into the condition.
+ * @param int $type OPTIONAL The type of the given value
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function having($cond, $value = null, $type = null)
+ {
+ if ($value !== null) {
+ $cond = $this->_adapter->quoteInto($cond, $value, $type);
+ }
+
+ if ($this->_parts[self::HAVING]) {
+ $this->_parts[self::HAVING][] = self::SQL_AND . " ($cond)";
+ } else {
+ $this->_parts[self::HAVING][] = "($cond)";
+ }
+
+ return $this;
+ }
+
+ /**
+ * Adds a HAVING condition to the query by OR.
+ *
+ * Otherwise identical to orHaving().
+ *
+ * @param string $cond The HAVING condition.
+ * @param mixed $value OPTIONAL The value to quote into the condition.
+ * @param int $type OPTIONAL The type of the given value
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ *
+ * @see having()
+ */
+ public function orHaving($cond, $value = null, $type = null)
+ {
+ if ($value !== null) {
+ $cond = $this->_adapter->quoteInto($cond, $value, $type);
+ }
+
+ if ($this->_parts[self::HAVING]) {
+ $this->_parts[self::HAVING][] = self::SQL_OR . " ($cond)";
+ } else {
+ $this->_parts[self::HAVING][] = "($cond)";
+ }
+
+ return $this;
+ }
+
+ /**
+ * Adds a row order to the query.
+ *
+ * @param mixed $spec The column(s) and direction to order by.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function order($spec)
+ {
+ if (!is_array($spec)) {
+ $spec = array($spec);
+ }
+
+ // force 'ASC' or 'DESC' on each order spec, default is ASC.
+ foreach ($spec as $val) {
+ if ($val instanceof Zend_Db_Expr) {
+ $expr = $val->__toString();
+ if (empty($expr)) {
+ continue;
+ }
+ $this->_parts[self::ORDER][] = $val;
+ } else {
+ if (empty($val)) {
+ continue;
+ }
+ $direction = self::SQL_ASC;
+ if (preg_match('/(.*\W)(' . self::SQL_ASC . '|' . self::SQL_DESC . ')\b/si', $val, $matches)) {
+ $val = trim($matches[1]);
+ $direction = $matches[2];
+ }
+ if (preg_match('/\(.*\)/', $val)) {
+ $val = new Zend_Db_Expr($val);
+ }
+ $this->_parts[self::ORDER][] = array($val, $direction);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets a limit count and offset to the query.
+ *
+ * @param int $count OPTIONAL The number of rows to return.
+ * @param int $offset OPTIONAL Start returning after this many rows.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function limit($count = null, $offset = null)
+ {
+ $this->_parts[self::LIMIT_COUNT] = (int) $count;
+ $this->_parts[self::LIMIT_OFFSET] = (int) $offset;
+ return $this;
+ }
+
+ /**
+ * Sets the limit and count by page number.
+ *
+ * @param int $page Limit results to this page number.
+ * @param int $rowCount Use this many rows per page.
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function limitPage($page, $rowCount)
+ {
+ $page = ($page > 0) ? $page : 1;
+ $rowCount = ($rowCount > 0) ? $rowCount : 1;
+ $this->_parts[self::LIMIT_COUNT] = (int) $rowCount;
+ $this->_parts[self::LIMIT_OFFSET] = (int) $rowCount * ($page - 1);
+ return $this;
+ }
+
+ /**
+ * Makes the query SELECT FOR UPDATE.
+ *
+ * @param bool $flag Whether or not the SELECT is FOR UPDATE (default true).
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function forUpdate($flag = true)
+ {
+ $this->_parts[self::FOR_UPDATE] = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Get part of the structured information for the currect query.
+ *
+ * @param string $part
+ * @return mixed
+ * @throws Zend_Db_Select_Exception
+ */
+ public function getPart($part)
+ {
+ $part = strtolower($part);
+ if (!array_key_exists($part, $this->_parts)) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Invalid Select part '$part'");
+ }
+ return $this->_parts[$part];
+ }
+
+ /**
+ * Executes the current select object and returns the result
+ *
+ * @param integer $fetchMode OPTIONAL
+ * @param mixed $bind An array of data to bind to the placeholders.
+ * @return PDO_Statement|Zend_Db_Statement
+ */
+ public function query($fetchMode = null, $bind = array())
+ {
+ if (!empty($bind)) {
+ $this->bind($bind);
+ }
+
+ $stmt = $this->_adapter->query($this);
+ if ($fetchMode == null) {
+ $fetchMode = $this->_adapter->getFetchMode();
+ }
+ $stmt->setFetchMode($fetchMode);
+ return $stmt;
+ }
+
+ /**
+ * Converts this object to an SQL SELECT string.
+ *
+ * @return string|null This object as a SELECT string. (or null if a string cannot be produced.)
+ */
+ public function assemble()
+ {
+ $sql = self::SQL_SELECT;
+ foreach (array_keys(self::$_partsInit) as $part) {
+ $method = '_render' . ucfirst($part);
+ if (method_exists($this, $method)) {
+ $sql = $this->$method($sql);
+ }
+ }
+ return $sql;
+ }
+
+ /**
+ * Clear parts of the Select object, or an individual part.
+ *
+ * @param string $part OPTIONAL
+ * @return Zend_Db_Select
+ */
+ public function reset($part = null)
+ {
+ if ($part == null) {
+ $this->_parts = self::$_partsInit;
+ } else if (array_key_exists($part, self::$_partsInit)) {
+ $this->_parts[$part] = self::$_partsInit[$part];
+ }
+ return $this;
+ }
+
+ /**
+ * Gets the Zend_Db_Adapter_Abstract for this
+ * particular Zend_Db_Select object.
+ *
+ * @return Zend_Db_Adapter_Abstract
+ */
+ public function getAdapter()
+ {
+ return $this->_adapter;
+ }
+
+ /**
+ * Populate the {@link $_parts} 'join' key
+ *
+ * Does the dirty work of populating the join key.
+ *
+ * The $name and $cols parameters follow the same logic
+ * as described in the from() method.
+ *
+ * @param null|string $type Type of join; inner, left, and null are currently supported
+ * @param array|string|Zend_Db_Expr $name Table name
+ * @param string $cond Join on this condition
+ * @param array|string $cols The columns to select from the joined table
+ * @param string $schema The database name to specify, if any.
+ * @return Zend_Db_Select This Zend_Db_Select object
+ * @throws Zend_Db_Select_Exception
+ */
+ protected function _join($type, $name, $cond, $cols, $schema = null)
+ {
+ if (!in_array($type, self::$_joinTypes) && $type != self::FROM) {
+ /**
+ * @see Zend_Db_Select_Exception
+ */
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Invalid join type '$type'");
+ }
+
+ if (count($this->_parts[self::UNION])) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Invalid use of table with " . self::SQL_UNION);
+ }
+
+ if (empty($name)) {
+ $correlationName = $tableName = '';
+ } else if (is_array($name)) {
+ // Must be array($correlationName => $tableName) or array($ident, ...)
+ foreach ($name as $_correlationName => $_tableName) {
+ if (is_string($_correlationName)) {
+ // We assume the key is the correlation name and value is the table name
+ $tableName = $_tableName;
+ $correlationName = $_correlationName;
+ } else {
+ // We assume just an array of identifiers, with no correlation name
+ $tableName = $_tableName;
+ $correlationName = $this->_uniqueCorrelation($tableName);
+ }
+ break;
+ }
+ } else if ($name instanceof Zend_Db_Expr|| $name instanceof Zend_Db_Select) {
+ $tableName = $name;
+ $correlationName = $this->_uniqueCorrelation('t');
+ } else if (preg_match('/^(.+)\s+AS\s+(.+)$/i', $name, $m)) {
+ $tableName = $m[1];
+ $correlationName = $m[2];
+ } else {
+ $tableName = $name;
+ $correlationName = $this->_uniqueCorrelation($tableName);
+ }
+
+ // Schema from table name overrides schema argument
+ if (!is_object($tableName) && false !== strpos($tableName, '.')) {
+ list($schema, $tableName) = explode('.', $tableName);
+ }
+
+ $lastFromCorrelationName = null;
+ if (!empty($correlationName)) {
+ if (array_key_exists($correlationName, $this->_parts[self::FROM])) {
+ /**
+ * @see Zend_Db_Select_Exception
+ */
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("You cannot define a correlation name '$correlationName' more than once");
+ }
+
+ if ($type == self::FROM) {
+ // append this from after the last from joinType
+ $tmpFromParts = $this->_parts[self::FROM];
+ $this->_parts[self::FROM] = array();
+ // move all the froms onto the stack
+ while ($tmpFromParts) {
+ $currentCorrelationName = key($tmpFromParts);
+ if ($tmpFromParts[$currentCorrelationName]['joinType'] != self::FROM) {
+ break;
+ }
+ $lastFromCorrelationName = $currentCorrelationName;
+ $this->_parts[self::FROM][$currentCorrelationName] = array_shift($tmpFromParts);
+ }
+ } else {
+ $tmpFromParts = array();
+ }
+ $this->_parts[self::FROM][$correlationName] = array(
+ 'joinType' => $type,
+ 'schema' => $schema,
+ 'tableName' => $tableName,
+ 'joinCondition' => $cond
+ );
+ while ($tmpFromParts) {
+ $currentCorrelationName = key($tmpFromParts);
+ $this->_parts[self::FROM][$currentCorrelationName] = array_shift($tmpFromParts);
+ }
+ }
+
+ // add to the columns from this joined table
+ if ($type == self::FROM && $lastFromCorrelationName == null) {
+ $lastFromCorrelationName = true;
+ }
+ $this->_tableCols($correlationName, $cols, $lastFromCorrelationName);
+
+ return $this;
+ }
+
+ /**
+ * Handle JOIN... USING... syntax
+ *
+ * This is functionality identical to the existing JOIN methods, however
+ * the join condition can be passed as a single column name. This method
+ * then completes the ON condition by using the same field for the FROM
+ * table and the JOIN table.
+ *
+ *
+ * $select = $db->select()->from('table1')
+ * ->joinUsing('table2', 'column1');
+ *
+ * // SELECT * FROM table1 JOIN table2 ON table1.column1 = table2.column2
+ *
+ *
+ * These joins are called by the developer simply by adding 'Using' to the
+ * method name. E.g.
+ * * joinUsing
+ * * joinInnerUsing
+ * * joinFullUsing
+ * * joinRightUsing
+ * * joinLeftUsing
+ *
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function _joinUsing($type, $name, $cond, $cols = '*', $schema = null)
+ {
+ if (empty($this->_parts[self::FROM])) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("You can only perform a joinUsing after specifying a FROM table");
+ }
+
+ $join = $this->_adapter->quoteIdentifier(key($this->_parts[self::FROM]), true);
+ $from = $this->_adapter->quoteIdentifier($this->_uniqueCorrelation($name), true);
+
+ $cond1 = $from . '.' . $cond;
+ $cond2 = $join . '.' . $cond;
+ $cond = $cond1 . ' = ' . $cond2;
+
+ return $this->_join($type, $name, $cond, $cols, $schema);
+ }
+
+ /**
+ * Generate a unique correlation name
+ *
+ * @param string|array $name A qualified identifier.
+ * @return string A unique correlation name.
+ */
+ private function _uniqueCorrelation($name)
+ {
+ if (is_array($name)) {
+ $c = end($name);
+ } else {
+ // Extract just the last name of a qualified table name
+ $dot = strrpos($name,'.');
+ $c = ($dot === false) ? $name : substr($name, $dot+1);
+ }
+ for ($i = 2; array_key_exists($c, $this->_parts[self::FROM]); ++$i) {
+ $c = $name . '_' . (string) $i;
+ }
+ return $c;
+ }
+
+ /**
+ * Adds to the internal table-to-column mapping array.
+ *
+ * @param string $tbl The table/join the columns come from.
+ * @param array|string $cols The list of columns; preferably as
+ * an array, but possibly as a string containing one column.
+ * @param bool|string True if it should be prepended, a correlation name if it should be inserted
+ * @return void
+ */
+ protected function _tableCols($correlationName, $cols, $afterCorrelationName = null)
+ {
+ if (!is_array($cols)) {
+ $cols = array($cols);
+ }
+
+ if ($correlationName == null) {
+ $correlationName = '';
+ }
+
+ $columnValues = array();
+
+ foreach (array_filter($cols) as $alias => $col) {
+ $currentCorrelationName = $correlationName;
+ if (is_string($col)) {
+ // Check for a column matching " AS " and extract the alias name
+ if (preg_match('/^(.+)\s+' . self::SQL_AS . '\s+(.+)$/i', $col, $m)) {
+ $col = $m[1];
+ $alias = $m[2];
+ }
+ // Check for columns that look like functions and convert to Zend_Db_Expr
+ if (preg_match('/\(.*\)/', $col)) {
+ $col = new Zend_Db_Expr($col);
+ } elseif (preg_match('/(.+)\.(.+)/', $col, $m)) {
+ $currentCorrelationName = $m[1];
+ $col = $m[2];
+ }
+ }
+ $columnValues[] = array($currentCorrelationName, $col, is_string($alias) ? $alias : null);
+ }
+
+ if ($columnValues) {
+
+ // should we attempt to prepend or insert these values?
+ if ($afterCorrelationName === true || is_string($afterCorrelationName)) {
+ $tmpColumns = $this->_parts[self::COLUMNS];
+ $this->_parts[self::COLUMNS] = array();
+ } else {
+ $tmpColumns = array();
+ }
+
+ // find the correlation name to insert after
+ if (is_string($afterCorrelationName)) {
+ while ($tmpColumns) {
+ $this->_parts[self::COLUMNS][] = $currentColumn = array_shift($tmpColumns);
+ if ($currentColumn[0] == $afterCorrelationName) {
+ break;
+ }
+ }
+ }
+
+ // apply current values to current stack
+ foreach ($columnValues as $columnValue) {
+ array_push($this->_parts[self::COLUMNS], $columnValue);
+ }
+
+ // finish ensuring that all previous values are applied (if they exist)
+ while ($tmpColumns) {
+ array_push($this->_parts[self::COLUMNS], array_shift($tmpColumns));
+ }
+ }
+ }
+
+ /**
+ * Internal function for creating the where clause
+ *
+ * @param string $condition
+ * @param mixed $value optional
+ * @param string $type optional
+ * @param boolean $bool true = AND, false = OR
+ * @return string clause
+ */
+ protected function _where($condition, $value = null, $type = null, $bool = true)
+ {
+ if (count($this->_parts[self::UNION])) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Invalid use of where clause with " . self::SQL_UNION);
+ }
+
+ if ($value !== null) {
+ $condition = $this->_adapter->quoteInto($condition, $value, $type);
+ }
+
+ $cond = "";
+ if ($this->_parts[self::WHERE]) {
+ if ($bool === true) {
+ $cond = self::SQL_AND . ' ';
+ } else {
+ $cond = self::SQL_OR . ' ';
+ }
+ }
+
+ return $cond . "($condition)";
+ }
+
+ /**
+ * @return array
+ */
+ protected function _getDummyTable()
+ {
+ return array();
+ }
+
+ /**
+ * Return a quoted schema name
+ *
+ * @param string $schema The schema name OPTIONAL
+ * @return string|null
+ */
+ protected function _getQuotedSchema($schema = null)
+ {
+ if ($schema === null) {
+ return null;
+ }
+ return $this->_adapter->quoteIdentifier($schema, true) . '.';
+ }
+
+ /**
+ * Return a quoted table name
+ *
+ * @param string $tableName The table name
+ * @param string $correlationName The correlation name OPTIONAL
+ * @return string
+ */
+ protected function _getQuotedTable($tableName, $correlationName = null)
+ {
+ return $this->_adapter->quoteTableAs($tableName, $correlationName, true);
+ }
+
+ /**
+ * Render DISTINCT clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderDistinct($sql)
+ {
+ if ($this->_parts[self::DISTINCT]) {
+ $sql .= ' ' . self::SQL_DISTINCT;
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render DISTINCT clause
+ *
+ * @param string $sql SQL query
+ * @return string|null
+ */
+ protected function _renderColumns($sql)
+ {
+ if (!count($this->_parts[self::COLUMNS])) {
+ return null;
+ }
+
+ $columns = array();
+ foreach ($this->_parts[self::COLUMNS] as $columnEntry) {
+ list($correlationName, $column, $alias) = $columnEntry;
+ if ($column instanceof Zend_Db_Expr) {
+ $columns[] = $this->_adapter->quoteColumnAs($column, $alias, true);
+ } else {
+ if ($column == self::SQL_WILDCARD) {
+ $column = new Zend_Db_Expr(self::SQL_WILDCARD);
+ $alias = null;
+ }
+ if (empty($correlationName)) {
+ $columns[] = $this->_adapter->quoteColumnAs($column, $alias, true);
+ } else {
+ $columns[] = $this->_adapter->quoteColumnAs(array($correlationName, $column), $alias, true);
+ }
+ }
+ }
+
+ return $sql .= ' ' . implode(', ', $columns);
+ }
+
+ /**
+ * Render FROM clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderFrom($sql)
+ {
+ /*
+ * If no table specified, use RDBMS-dependent solution
+ * for table-less query. e.g. DUAL in Oracle.
+ */
+ if (empty($this->_parts[self::FROM])) {
+ $this->_parts[self::FROM] = $this->_getDummyTable();
+ }
+
+ $from = array();
+
+ foreach ($this->_parts[self::FROM] as $correlationName => $table) {
+ $tmp = '';
+
+ $joinType = ($table['joinType'] == self::FROM) ? self::INNER_JOIN : $table['joinType'];
+
+ // Add join clause (if applicable)
+ if (! empty($from)) {
+ $tmp .= ' ' . strtoupper($joinType) . ' ';
+ }
+
+ $tmp .= $this->_getQuotedSchema($table['schema']);
+ $tmp .= $this->_getQuotedTable($table['tableName'], $correlationName);
+
+ // Add join conditions (if applicable)
+ if (!empty($from) && ! empty($table['joinCondition'])) {
+ $tmp .= ' ' . self::SQL_ON . ' ' . $table['joinCondition'];
+ }
+
+ // Add the table name and condition add to the list
+ $from[] = $tmp;
+ }
+
+ // Add the list of all joins
+ if (!empty($from)) {
+ $sql .= ' ' . self::SQL_FROM . ' ' . implode("\n", $from);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render UNION query
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderUnion($sql)
+ {
+ if ($this->_parts[self::UNION]) {
+ $parts = count($this->_parts[self::UNION]);
+ foreach ($this->_parts[self::UNION] as $cnt => $union) {
+ list($target, $type) = $union;
+ if ($target instanceof Zend_Db_Select) {
+ $target = $target->assemble();
+ }
+ $sql .= $target;
+ if ($cnt < $parts - 1) {
+ $sql .= ' ' . $type . ' ';
+ }
+ }
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render WHERE clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderWhere($sql)
+ {
+ if ($this->_parts[self::FROM] && $this->_parts[self::WHERE]) {
+ $sql .= ' ' . self::SQL_WHERE . ' ' . implode(' ', $this->_parts[self::WHERE]);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render GROUP clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderGroup($sql)
+ {
+ if ($this->_parts[self::FROM] && $this->_parts[self::GROUP]) {
+ $group = array();
+ foreach ($this->_parts[self::GROUP] as $term) {
+ $group[] = $this->_adapter->quoteIdentifier($term, true);
+ }
+ $sql .= ' ' . self::SQL_GROUP_BY . ' ' . implode(",\n\t", $group);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render HAVING clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderHaving($sql)
+ {
+ if ($this->_parts[self::FROM] && $this->_parts[self::HAVING]) {
+ $sql .= ' ' . self::SQL_HAVING . ' ' . implode(' ', $this->_parts[self::HAVING]);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render ORDER clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderOrder($sql)
+ {
+ if ($this->_parts[self::ORDER]) {
+ $order = array();
+ foreach ($this->_parts[self::ORDER] as $term) {
+ if (is_array($term)) {
+ if(is_numeric($term[0]) && strval(intval($term[0])) == $term[0]) {
+ $order[] = (int)trim($term[0]) . ' ' . $term[1];
+ } else {
+ $order[] = $this->_adapter->quoteIdentifier($term[0], true) . ' ' . $term[1];
+ }
+ } else if (is_numeric($term) && strval(intval($term)) == $term) {
+ $order[] = (int)trim($term);
+ } else {
+ $order[] = $this->_adapter->quoteIdentifier($term, true);
+ }
+ }
+ $sql .= ' ' . self::SQL_ORDER_BY . ' ' . implode(', ', $order);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render LIMIT OFFSET clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderLimitoffset($sql)
+ {
+ $count = 0;
+ $offset = 0;
+
+ if (!empty($this->_parts[self::LIMIT_OFFSET])) {
+ $offset = (int) $this->_parts[self::LIMIT_OFFSET];
+ $count = PHP_INT_MAX;
+ }
+
+ if (!empty($this->_parts[self::LIMIT_COUNT])) {
+ $count = (int) $this->_parts[self::LIMIT_COUNT];
+ }
+
+ /*
+ * Add limits clause
+ */
+ if ($count > 0) {
+ $sql = trim($this->_adapter->limit($sql, $count, $offset));
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Render FOR UPDATE clause
+ *
+ * @param string $sql SQL query
+ * @return string
+ */
+ protected function _renderForupdate($sql)
+ {
+ if ($this->_parts[self::FOR_UPDATE]) {
+ $sql .= ' ' . self::SQL_FOR_UPDATE;
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Turn magic function calls into non-magic function calls
+ * for joinUsing syntax
+ *
+ * @param string $method
+ * @param array $args OPTIONAL Zend_Db_Table_Select query modifier
+ * @return Zend_Db_Select
+ * @throws Zend_Db_Select_Exception If an invalid method is called.
+ */
+ public function __call($method, array $args)
+ {
+ $matches = array();
+
+ /**
+ * Recognize methods for Has-Many cases:
+ * findParent()
+ * findParentBy()
+ * Use the non-greedy pattern repeat modifier e.g. \w+?
+ */
+ if (preg_match('/^join([a-zA-Z]*?)Using$/', $method, $matches)) {
+ $type = strtolower($matches[1]);
+ if ($type) {
+ $type .= ' join';
+ if (!in_array($type, self::$_joinTypes)) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Unrecognized method '$method()'");
+ }
+ if (in_array($type, array(self::CROSS_JOIN, self::NATURAL_JOIN))) {
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Cannot perform a joinUsing with method '$method()'");
+ }
+ } else {
+ $type = self::INNER_JOIN;
+ }
+ array_unshift($args, $type);
+ return call_user_func_array(array($this, '_joinUsing'), $args);
+ }
+
+ require_once 'Zend/Db/Select/Exception.php';
+ throw new Zend_Db_Select_Exception("Unrecognized method '$method()'");
+ }
+
+ /**
+ * Implements magic method.
+ *
+ * @return string This object as a SELECT string.
+ */
+ public function __toString()
+ {
+ try {
+ $sql = $this->assemble();
+ } catch (Exception $e) {
+ trigger_error($e->getMessage(), E_USER_WARNING);
+ $sql = '';
+ }
+ return (string)$sql;
+ }
+
+}
diff --git a/library/Zend/Db/Select/Exception.php b/library/Zend/Db/Select/Exception.php
new file mode 100644
index 0000000..89823fb
--- /dev/null
+++ b/library/Zend/Db/Select/Exception.php
@@ -0,0 +1,39 @@
+_adapter = $adapter;
+ if ($sql instanceof Zend_Db_Select) {
+ $sql = $sql->assemble();
+ }
+ $this->_parseParameters($sql);
+ $this->_prepare($sql);
+
+ $this->_queryId = $this->_adapter->getProfiler()->queryStart($sql);
+ }
+
+ /**
+ * Internal method called by abstract statment constructor to setup
+ * the driver level statement
+ *
+ * @return void
+ */
+ protected function _prepare($sql)
+ {
+ return;
+ }
+
+ /**
+ * @param string $sql
+ * @return void
+ */
+ protected function _parseParameters($sql)
+ {
+ $sql = $this->_stripQuoted($sql);
+
+ // split into text and params
+ $this->_sqlSplit = preg_split('/(\?|\:[a-zA-Z0-9_]+)/',
+ $sql, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+
+ // map params
+ $this->_sqlParam = array();
+ foreach ($this->_sqlSplit as $key => $val) {
+ if ($val == '?') {
+ if ($this->_adapter->supportsParameters('positional') === false) {
+ /**
+ * @see Zend_Db_Statement_Exception
+ */
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception("Invalid bind-variable position '$val'");
+ }
+ } else if ($val[0] == ':') {
+ if ($this->_adapter->supportsParameters('named') === false) {
+ /**
+ * @see Zend_Db_Statement_Exception
+ */
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception("Invalid bind-variable name '$val'");
+ }
+ }
+ $this->_sqlParam[] = $val;
+ }
+
+ // set up for binding
+ $this->_bindParam = array();
+ }
+
+ /**
+ * Remove parts of a SQL string that contain quoted strings
+ * of values or identifiers.
+ *
+ * @param string $sql
+ * @return string
+ */
+ protected function _stripQuoted($sql)
+ {
+ // get the character for delimited id quotes,
+ // this is usually " but in MySQL is `
+ $d = $this->_adapter->quoteIdentifier('a');
+ $d = $d[0];
+
+ // get the value used as an escaped delimited id quote,
+ // e.g. \" or "" or \`
+ $de = $this->_adapter->quoteIdentifier($d);
+ $de = substr($de, 1, 2);
+ $de = str_replace('\\', '\\\\', $de);
+
+ // get the character for value quoting
+ // this should be '
+ $q = $this->_adapter->quote('a');
+ $q = $q[0];
+
+ // get the value used as an escaped quote,
+ // e.g. \' or ''
+ $qe = $this->_adapter->quote($q);
+ $qe = substr($qe, 1, 2);
+ $qe = str_replace('\\', '\\\\', $qe);
+
+ // get a version of the SQL statement with all quoted
+ // values and delimited identifiers stripped out
+ // remove "foo\"bar"
+ $sql = preg_replace("/$q($qe|\\\\{2}|[^$q])*$q/", '', $sql);
+ // remove 'foo\'bar'
+ if (!empty($q)) {
+ $sql = preg_replace("/$q($qe|[^$q])*$q/", '', $sql);
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Bind a column of the statement result set to a PHP variable.
+ *
+ * @param string $column Name the column in the result set, either by
+ * position or by name.
+ * @param mixed $param Reference to the PHP variable containing the value.
+ * @param mixed $type OPTIONAL
+ * @return bool
+ */
+ public function bindColumn($column, &$param, $type = null)
+ {
+ $this->_bindColumn[$column] =& $param;
+ return true;
+ }
+
+ /**
+ * Binds a parameter to the specified variable name.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $variable Reference to PHP variable containing the value.
+ * @param mixed $type OPTIONAL Datatype of SQL parameter.
+ * @param mixed $length OPTIONAL Length of SQL parameter.
+ * @param mixed $options OPTIONAL Other options.
+ * @return bool
+ */
+ public function bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
+ {
+ if (!is_int($parameter) && !is_string($parameter)) {
+ /**
+ * @see Zend_Db_Statement_Exception
+ */
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception('Invalid bind-variable position');
+ }
+
+ $position = null;
+ if (($intval = (int) $parameter) > 0 && $this->_adapter->supportsParameters('positional')) {
+ if ($intval >= 1 || $intval <= count($this->_sqlParam)) {
+ $position = $intval;
+ }
+ } else if ($this->_adapter->supportsParameters('named')) {
+ if ($parameter[0] != ':') {
+ $parameter = ':' . $parameter;
+ }
+ if (in_array($parameter, $this->_sqlParam) !== false) {
+ $position = $parameter;
+ }
+ }
+
+ if ($position === null) {
+ /**
+ * @see Zend_Db_Statement_Exception
+ */
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception("Invalid bind-variable position '$parameter'");
+ }
+
+ // Finally we are assured that $position is valid
+ $this->_bindParam[$position] =& $variable;
+ return $this->_bindParam($position, $variable, $type, $length, $options);
+ }
+
+ /**
+ * Binds a value to a parameter.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $value Scalar value to bind to the parameter.
+ * @param mixed $type OPTIONAL Datatype of the parameter.
+ * @return bool
+ */
+ public function bindValue($parameter, $value, $type = null)
+ {
+ return $this->bindParam($parameter, $value, $type);
+ }
+
+ /**
+ * Executes a prepared statement.
+ *
+ * @param array $params OPTIONAL Values to bind to parameter placeholders.
+ * @return bool
+ */
+ public function execute(array $params = null)
+ {
+ /*
+ * Simple case - no query profiler to manage.
+ */
+ if ($this->_queryId === null) {
+ return $this->_execute($params);
+ }
+
+ /*
+ * Do the same thing, but with query profiler
+ * management before and after the execute.
+ */
+ $prof = $this->_adapter->getProfiler();
+ $qp = $prof->getQueryProfile($this->_queryId);
+ if ($qp->hasEnded()) {
+ $this->_queryId = $prof->queryClone($qp);
+ $qp = $prof->getQueryProfile($this->_queryId);
+ }
+ if ($params !== null) {
+ $qp->bindParams($params);
+ } else {
+ $qp->bindParams($this->_bindParam);
+ }
+ $qp->start($this->_queryId);
+
+ $retval = $this->_execute($params);
+
+ $prof->queryEnd($this->_queryId);
+
+ return $retval;
+ }
+
+ /**
+ * Returns an array containing all of the result set rows.
+ *
+ * @param int $style OPTIONAL Fetch mode.
+ * @param int $col OPTIONAL Column number, if fetch mode is by column.
+ * @return array Collection of rows, each in a format by the fetch mode.
+ */
+ public function fetchAll($style = null, $col = null)
+ {
+ $data = array();
+ if ($style === Zend_Db::FETCH_COLUMN && $col === null) {
+ $col = 0;
+ }
+ if ($col === null) {
+ while ($row = $this->fetch($style)) {
+ $data[] = $row;
+ }
+ } else {
+ while (false !== ($val = $this->fetchColumn($col))) {
+ $data[] = $val;
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Returns a single column from the next row of a result set.
+ *
+ * @param int $col OPTIONAL Position of the column to fetch.
+ * @return string One value from the next row of result set, or false.
+ */
+ public function fetchColumn($col = 0)
+ {
+ $data = array();
+ $col = (int) $col;
+ $row = $this->fetch(Zend_Db::FETCH_NUM);
+ if (!is_array($row)) {
+ return false;
+ }
+ return $row[$col];
+ }
+
+ /**
+ * Fetches the next row and returns it as an object.
+ *
+ * @param string $class OPTIONAL Name of the class to create.
+ * @param array $config OPTIONAL Constructor arguments for the class.
+ * @return mixed One object instance of the specified class, or false.
+ */
+ public function fetchObject($class = 'stdClass', array $config = array())
+ {
+ $obj = new $class($config);
+ $row = $this->fetch(Zend_Db::FETCH_ASSOC);
+ if (!is_array($row)) {
+ return false;
+ }
+ foreach ($row as $key => $val) {
+ $obj->$key = $val;
+ }
+ return $obj;
+ }
+
+ /**
+ * Retrieve a statement attribute.
+ *
+ * @param string $key Attribute name.
+ * @return mixed Attribute value.
+ */
+ public function getAttribute($key)
+ {
+ if (array_key_exists($key, $this->_attribute)) {
+ return $this->_attribute[$key];
+ }
+ }
+
+ /**
+ * Set a statement attribute.
+ *
+ * @param string $key Attribute name.
+ * @param mixed $val Attribute value.
+ * @return bool
+ */
+ public function setAttribute($key, $val)
+ {
+ $this->_attribute[$key] = $val;
+ }
+
+ /**
+ * Set the default fetch mode for this statement.
+ *
+ * @param int $mode The fetch mode.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function setFetchMode($mode)
+ {
+ switch ($mode) {
+ case Zend_Db::FETCH_NUM:
+ case Zend_Db::FETCH_ASSOC:
+ case Zend_Db::FETCH_BOTH:
+ case Zend_Db::FETCH_OBJ:
+ $this->_fetchMode = $mode;
+ break;
+ case Zend_Db::FETCH_BOUND:
+ default:
+ $this->closeCursor();
+ /**
+ * @see Zend_Db_Statement_Exception
+ */
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception('invalid fetch mode');
+ break;
+ }
+ }
+
+ /**
+ * Helper function to map retrieved row
+ * to bound column variables
+ *
+ * @param array $row
+ * @return bool True
+ */
+ public function _fetchBound($row)
+ {
+ foreach ($row as $key => $value) {
+ // bindColumn() takes 1-based integer positions
+ // but fetch() returns 0-based integer indexes
+ if (is_int($key)) {
+ $key++;
+ }
+ // set results only to variables that were bound previously
+ if (isset($this->_bindColumn[$key])) {
+ $this->_bindColumn[$key] = $value;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Gets the Zend_Db_Adapter_Abstract for this
+ * particular Zend_Db_Statement object.
+ *
+ * @return Zend_Db_Adapter_Abstract
+ */
+ public function getAdapter()
+ {
+ return $this->_adapter;
+ }
+
+ /**
+ * Gets the resource or object setup by the
+ * _parse
+ * @return unknown_type
+ */
+ public function getDriverStatement()
+ {
+ return $this->_stmt;
+ }
+}
diff --git a/library/Zend/Db/Statement/Db2.php b/library/Zend/Db/Statement/Db2.php
new file mode 100644
index 0000000..54bd6bb
--- /dev/null
+++ b/library/Zend/Db/Statement/Db2.php
@@ -0,0 +1,360 @@
+_adapter->getConnection();
+
+ // db2_prepare on i5 emits errors, these need to be
+ // suppressed so that proper exceptions can be thrown
+ $this->_stmt = @db2_prepare($connection, $sql);
+
+ if (!$this->_stmt) {
+ /**
+ * @see Zend_Db_Statement_Db2_Exception
+ */
+ require_once 'Zend/Db/Statement/Db2/Exception.php';
+ throw new Zend_Db_Statement_Db2_Exception(
+ db2_stmt_errormsg(),
+ db2_stmt_error()
+ );
+ }
+ }
+
+ /**
+ * Binds a parameter to the specified variable name.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $variable Reference to PHP variable containing the value.
+ * @param mixed $type OPTIONAL Datatype of SQL parameter.
+ * @param mixed $length OPTIONAL Length of SQL parameter.
+ * @param mixed $options OPTIONAL Other options.
+ * @return bool
+ * @throws Zend_Db_Statement_Db2_Exception
+ */
+ public function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
+ {
+ if ($type === null) {
+ $type = DB2_PARAM_IN;
+ }
+
+ if (isset($options['data-type'])) {
+ $datatype = $options['data-type'];
+ } else {
+ $datatype = DB2_CHAR;
+ }
+
+ if (!db2_bind_param($this->_stmt, $position, "variable", $type, $datatype)) {
+ /**
+ * @see Zend_Db_Statement_Db2_Exception
+ */
+ require_once 'Zend/Db/Statement/Db2/Exception.php';
+ throw new Zend_Db_Statement_Db2_Exception(
+ db2_stmt_errormsg(),
+ db2_stmt_error()
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Closes the cursor, allowing the statement to be executed again.
+ *
+ * @return bool
+ */
+ public function closeCursor()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+ db2_free_stmt($this->_stmt);
+ $this->_stmt = false;
+ return true;
+ }
+
+
+ /**
+ * Returns the number of columns in the result set.
+ * Returns null if the statement has no result set metadata.
+ *
+ * @return int The number of columns.
+ */
+ public function columnCount()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+ return db2_num_fields($this->_stmt);
+ }
+
+ /**
+ * Retrieves the error code, if any, associated with the last operation on
+ * the statement handle.
+ *
+ * @return string error code.
+ */
+ public function errorCode()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $error = db2_stmt_error();
+ if ($error === '') {
+ return false;
+ }
+
+ return $error;
+ }
+
+ /**
+ * Retrieves an array of error information, if any, associated with the
+ * last operation on the statement handle.
+ *
+ * @return array
+ */
+ public function errorInfo()
+ {
+ $error = $this->errorCode();
+ if ($error === false){
+ return false;
+ }
+
+ /*
+ * Return three-valued array like PDO. But DB2 does not distinguish
+ * between SQLCODE and native RDBMS error code, so repeat the SQLCODE.
+ */
+ return array(
+ $error,
+ $error,
+ db2_stmt_errormsg()
+ );
+ }
+
+ /**
+ * Executes a prepared statement.
+ *
+ * @param array $params OPTIONAL Values to bind to parameter placeholders.
+ * @return bool
+ * @throws Zend_Db_Statement_Db2_Exception
+ */
+ public function _execute(array $params = null)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $retval = true;
+ if ($params !== null) {
+ $retval = @db2_execute($this->_stmt, $params);
+ } else {
+ $retval = @db2_execute($this->_stmt);
+ }
+
+ if ($retval === false) {
+ /**
+ * @see Zend_Db_Statement_Db2_Exception
+ */
+ require_once 'Zend/Db/Statement/Db2/Exception.php';
+ throw new Zend_Db_Statement_Db2_Exception(
+ db2_stmt_errormsg(),
+ db2_stmt_error());
+ }
+
+ $this->_keys = array();
+ if ($field_num = $this->columnCount()) {
+ for ($i = 0; $i < $field_num; $i++) {
+ $name = db2_field_name($this->_stmt, $i);
+ $this->_keys[] = $name;
+ }
+ }
+
+ $this->_values = array();
+ if ($this->_keys) {
+ $this->_values = array_fill(0, count($this->_keys), null);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * Fetches a row from the result set.
+ *
+ * @param int $style OPTIONAL Fetch mode for this fetch operation.
+ * @param int $cursor OPTIONAL Absolute, relative, or other.
+ * @param int $offset OPTIONAL Number for absolute or relative cursors.
+ * @return mixed Array, object, or scalar depending on fetch mode.
+ * @throws Zend_Db_Statement_Db2_Exception
+ */
+ public function fetch($style = null, $cursor = null, $offset = null)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if ($style === null) {
+ $style = $this->_fetchMode;
+ }
+
+ switch ($style) {
+ case Zend_Db::FETCH_NUM :
+ $row = db2_fetch_array($this->_stmt);
+ break;
+ case Zend_Db::FETCH_ASSOC :
+ $row = db2_fetch_assoc($this->_stmt);
+ break;
+ case Zend_Db::FETCH_BOTH :
+ $row = db2_fetch_both($this->_stmt);
+ break;
+ case Zend_Db::FETCH_OBJ :
+ $row = db2_fetch_object($this->_stmt);
+ break;
+ case Zend_Db::FETCH_BOUND:
+ $row = db2_fetch_both($this->_stmt);
+ if ($row !== false) {
+ return $this->_fetchBound($row);
+ }
+ break;
+ default:
+ /**
+ * @see Zend_Db_Statement_Db2_Exception
+ */
+ require_once 'Zend/Db/Statement/Db2/Exception.php';
+ throw new Zend_Db_Statement_Db2_Exception("Invalid fetch mode '$style' specified");
+ break;
+ }
+
+ return $row;
+ }
+
+ /**
+ * Fetches the next row and returns it as an object.
+ *
+ * @param string $class OPTIONAL Name of the class to create.
+ * @param array $config OPTIONAL Constructor arguments for the class.
+ * @return mixed One object instance of the specified class.
+ */
+ public function fetchObject($class = 'stdClass', array $config = array())
+ {
+ $obj = $this->fetch(Zend_Db::FETCH_OBJ);
+ return $obj;
+ }
+
+ /**
+ * Retrieves the next rowset (result set) for a SQL statement that has
+ * multiple result sets. An example is a stored procedure that returns
+ * the results of multiple queries.
+ *
+ * @return bool
+ * @throws Zend_Db_Statement_Db2_Exception
+ */
+ public function nextRowset()
+ {
+ /**
+ * @see Zend_Db_Statement_Db2_Exception
+ */
+ require_once 'Zend/Db/Statement/Db2/Exception.php';
+ throw new Zend_Db_Statement_Db2_Exception(__FUNCTION__ . '() is not implemented');
+ }
+
+ /**
+ * Returns the number of rows affected by the execution of the
+ * last INSERT, DELETE, or UPDATE statement executed by this
+ * statement object.
+ *
+ * @return int The number of rows affected.
+ */
+ public function rowCount()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $num = @db2_num_rows($this->_stmt);
+
+ if ($num === false) {
+ return 0;
+ }
+
+ return $num;
+ }
+
+ /**
+ * Returns an array containing all of the result set rows.
+ *
+ * @param int $style OPTIONAL Fetch mode.
+ * @param int $col OPTIONAL Column number, if fetch mode is by column.
+ * @return array Collection of rows, each in a format by the fetch mode.
+ *
+ * Behaves like parent, but if limit()
+ * is used, the final result removes the extra column
+ * 'zend_db_rownum'
+ */
+ public function fetchAll($style = null, $col = null)
+ {
+ $data = parent::fetchAll($style, $col);
+ $results = array();
+ $remove = $this->_adapter->foldCase('ZEND_DB_ROWNUM');
+
+ foreach ($data as $row) {
+ if (is_array($row) && array_key_exists($remove, $row)) {
+ unset($row[$remove]);
+ }
+ $results[] = $row;
+ }
+ return $results;
+ }
+}
diff --git a/library/Zend/Db/Statement/Db2/Exception.php b/library/Zend/Db/Statement/Db2/Exception.php
new file mode 100644
index 0000000..1b0c372
--- /dev/null
+++ b/library/Zend/Db/Statement/Db2/Exception.php
@@ -0,0 +1,58 @@
+message = $msg;
+ $this->code = $state;
+ }
+
+}
+
diff --git a/library/Zend/Db/Statement/Exception.php b/library/Zend/Db/Statement/Exception.php
new file mode 100644
index 0000000..ff49a08
--- /dev/null
+++ b/library/Zend/Db/Statement/Exception.php
@@ -0,0 +1,56 @@
+getPrevious() !== null);
+ }
+
+ /**
+ * @return Exception|null
+ */
+ public function getChainedException()
+ {
+ return $this->getPrevious();
+ }
+}
diff --git a/library/Zend/Db/Statement/Interface.php b/library/Zend/Db/Statement/Interface.php
new file mode 100644
index 0000000..8ae38c3
--- /dev/null
+++ b/library/Zend/Db/Statement/Interface.php
@@ -0,0 +1,203 @@
+_adapter->getConnection();
+
+ $this->_stmt = $mysqli->prepare($sql);
+
+ if ($this->_stmt === false || $mysqli->errno) {
+ /**
+ * @see Zend_Db_Statement_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Statement/Mysqli/Exception.php';
+ throw new Zend_Db_Statement_Mysqli_Exception("Mysqli prepare error: " . $mysqli->error, $mysqli->errno);
+ }
+ }
+
+ /**
+ * Binds a parameter to the specified variable name.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $variable Reference to PHP variable containing the value.
+ * @param mixed $type OPTIONAL Datatype of SQL parameter.
+ * @param mixed $length OPTIONAL Length of SQL parameter.
+ * @param mixed $options OPTIONAL Other options.
+ * @return bool
+ * @throws Zend_Db_Statement_Mysqli_Exception
+ */
+ protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
+ {
+ return true;
+ }
+
+ /**
+ * Closes the cursor and the statement.
+ *
+ * @return bool
+ */
+ public function close()
+ {
+ if ($this->_stmt) {
+ $r = $this->_stmt->close();
+ $this->_stmt = null;
+ return $r;
+ }
+ return false;
+ }
+
+ /**
+ * Closes the cursor, allowing the statement to be executed again.
+ *
+ * @return bool
+ */
+ public function closeCursor()
+ {
+ if ($stmt = $this->_stmt) {
+ $mysqli = $this->_adapter->getConnection();
+ while ($mysqli->more_results()) {
+ $mysqli->next_result();
+ }
+ $this->_stmt->free_result();
+ return $this->_stmt->reset();
+ }
+ return false;
+ }
+
+ /**
+ * Returns the number of columns in the result set.
+ * Returns null if the statement has no result set metadata.
+ *
+ * @return int The number of columns.
+ */
+ public function columnCount()
+ {
+ if (isset($this->_meta) && $this->_meta) {
+ return $this->_meta->field_count;
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieves the error code, if any, associated with the last operation on
+ * the statement handle.
+ *
+ * @return string error code.
+ */
+ public function errorCode()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+ return substr($this->_stmt->sqlstate, 0, 5);
+ }
+
+ /**
+ * Retrieves an array of error information, if any, associated with the
+ * last operation on the statement handle.
+ *
+ * @return array
+ */
+ public function errorInfo()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+ return array(
+ substr($this->_stmt->sqlstate, 0, 5),
+ $this->_stmt->errno,
+ $this->_stmt->error,
+ );
+ }
+
+ /**
+ * Executes a prepared statement.
+ *
+ * @param array $params OPTIONAL Values to bind to parameter placeholders.
+ * @return bool
+ * @throws Zend_Db_Statement_Mysqli_Exception
+ */
+ public function _execute(array $params = null)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ // if no params were given as an argument to execute(),
+ // then default to the _bindParam array
+ if ($params === null) {
+ $params = $this->_bindParam;
+ }
+ // send $params as input parameters to the statement
+ if ($params) {
+ array_unshift($params, str_repeat('s', count($params)));
+ $stmtParams = array();
+ foreach ($params as $k => &$value) {
+ $stmtParams[$k] = &$value;
+ }
+ call_user_func_array(
+ array($this->_stmt, 'bind_param'),
+ $stmtParams
+ );
+ }
+
+ // execute the statement
+ $retval = $this->_stmt->execute();
+ if ($retval === false) {
+ /**
+ * @see Zend_Db_Statement_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Statement/Mysqli/Exception.php';
+ throw new Zend_Db_Statement_Mysqli_Exception("Mysqli statement execute error : " . $this->_stmt->error, $this->_stmt->errno);
+ }
+
+
+ // retain metadata
+ if ($this->_meta === null) {
+ $this->_meta = $this->_stmt->result_metadata();
+ if ($this->_stmt->errno) {
+ /**
+ * @see Zend_Db_Statement_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Statement/Mysqli/Exception.php';
+ throw new Zend_Db_Statement_Mysqli_Exception("Mysqli statement metadata error: " . $this->_stmt->error, $this->_stmt->errno);
+ }
+ }
+
+ // statements that have no result set do not return metadata
+ if ($this->_meta !== false) {
+
+ // get the column names that will result
+ $this->_keys = array();
+ foreach ($this->_meta->fetch_fields() as $col) {
+ $this->_keys[] = $this->_adapter->foldCase($col->name);
+ }
+
+ // set up a binding space for result variables
+ $this->_values = array_fill(0, count($this->_keys), null);
+
+ // set up references to the result binding space.
+ // just passing $this->_values in the call_user_func_array()
+ // below won't work, you need references.
+ $refs = array();
+ foreach ($this->_values as $i => &$f) {
+ $refs[$i] = &$f;
+ }
+
+ $this->_stmt->store_result();
+ // bind to the result variables
+ call_user_func_array(
+ array($this->_stmt, 'bind_result'),
+ $this->_values
+ );
+ }
+ return $retval;
+ }
+
+
+ /**
+ * Fetches a row from the result set.
+ *
+ * @param int $style OPTIONAL Fetch mode for this fetch operation.
+ * @param int $cursor OPTIONAL Absolute, relative, or other.
+ * @param int $offset OPTIONAL Number for absolute or relative cursors.
+ * @return mixed Array, object, or scalar depending on fetch mode.
+ * @throws Zend_Db_Statement_Mysqli_Exception
+ */
+ public function fetch($style = null, $cursor = null, $offset = null)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+ // fetch the next result
+ $retval = $this->_stmt->fetch();
+ switch ($retval) {
+ case null: // end of data
+ case false: // error occurred
+ $this->_stmt->reset();
+ return false;
+ default:
+ // fallthrough
+ }
+
+ // make sure we have a fetch mode
+ if ($style === null) {
+ $style = $this->_fetchMode;
+ }
+
+ // dereference the result values, otherwise things like fetchAll()
+ // return the same values for every entry (because of the reference).
+ $values = array();
+ foreach ($this->_values as $key => $val) {
+ $values[] = $val;
+ }
+
+ $row = false;
+ switch ($style) {
+ case Zend_Db::FETCH_NUM:
+ $row = $values;
+ break;
+ case Zend_Db::FETCH_ASSOC:
+ $row = array_combine($this->_keys, $values);
+ break;
+ case Zend_Db::FETCH_BOTH:
+ $assoc = array_combine($this->_keys, $values);
+ $row = array_merge($values, $assoc);
+ break;
+ case Zend_Db::FETCH_OBJ:
+ $row = (object) array_combine($this->_keys, $values);
+ break;
+ case Zend_Db::FETCH_BOUND:
+ $assoc = array_combine($this->_keys, $values);
+ $row = array_merge($values, $assoc);
+ return $this->_fetchBound($row);
+ break;
+ default:
+ /**
+ * @see Zend_Db_Statement_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Statement/Mysqli/Exception.php';
+ throw new Zend_Db_Statement_Mysqli_Exception("Invalid fetch mode '$style' specified");
+ break;
+ }
+ return $row;
+ }
+
+ /**
+ * Retrieves the next rowset (result set) for a SQL statement that has
+ * multiple result sets. An example is a stored procedure that returns
+ * the results of multiple queries.
+ *
+ * @return bool
+ * @throws Zend_Db_Statement_Mysqli_Exception
+ */
+ public function nextRowset()
+ {
+ /**
+ * @see Zend_Db_Statement_Mysqli_Exception
+ */
+ require_once 'Zend/Db/Statement/Mysqli/Exception.php';
+ throw new Zend_Db_Statement_Mysqli_Exception(__FUNCTION__.'() is not implemented');
+ }
+
+ /**
+ * Returns the number of rows affected by the execution of the
+ * last INSERT, DELETE, or UPDATE statement executed by this
+ * statement object.
+ *
+ * @return int The number of rows affected.
+ */
+ public function rowCount()
+ {
+ if (!$this->_adapter) {
+ return false;
+ }
+ $mysqli = $this->_adapter->getConnection();
+ return $mysqli->affected_rows;
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Db/Statement/Mysqli/Exception.php b/library/Zend/Db/Statement/Mysqli/Exception.php
new file mode 100644
index 0000000..2fb13b7
--- /dev/null
+++ b/library/Zend/Db/Statement/Mysqli/Exception.php
@@ -0,0 +1,38 @@
+_lobAsString = (bool) $lob_as_string;
+ return $this;
+ }
+
+ /**
+ * Return whether or not LOB are returned as string
+ *
+ * @return boolean
+ */
+ public function getLobAsString()
+ {
+ return $this->_lobAsString;
+ }
+
+ /**
+ * Prepares statement handle
+ *
+ * @param string $sql
+ * @return void
+ * @throws Zend_Db_Statement_Oracle_Exception
+ */
+ protected function _prepare($sql)
+ {
+ $connection = $this->_adapter->getConnection();
+ $this->_stmt = @oci_parse($connection, $sql);
+ if (!$this->_stmt) {
+ /**
+ * @see Zend_Db_Statement_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(oci_error($connection));
+ }
+ }
+
+ /**
+ * Binds a parameter to the specified variable name.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $variable Reference to PHP variable containing the value.
+ * @param mixed $type OPTIONAL Datatype of SQL parameter.
+ * @param mixed $length OPTIONAL Length of SQL parameter.
+ * @param mixed $options OPTIONAL Other options.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
+ {
+ // default value
+ if ($type === NULL) {
+ $type = SQLT_CHR;
+ }
+
+ // default value
+ if ($length === NULL) {
+ $length = -1;
+ }
+
+ $retval = @oci_bind_by_name($this->_stmt, $parameter, $variable, $length, $type);
+ if ($retval === false) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(oci_error($this->_stmt));
+ }
+
+ return true;
+ }
+
+ /**
+ * Closes the cursor, allowing the statement to be executed again.
+ *
+ * @return bool
+ */
+ public function closeCursor()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ oci_free_statement($this->_stmt);
+ $this->_stmt = false;
+ return true;
+ }
+
+ /**
+ * Returns the number of columns in the result set.
+ * Returns null if the statement has no result set metadata.
+ *
+ * @return int The number of columns.
+ */
+ public function columnCount()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ return oci_num_fields($this->_stmt);
+ }
+
+
+ /**
+ * Retrieves the error code, if any, associated with the last operation on
+ * the statement handle.
+ *
+ * @return string error code.
+ */
+ public function errorCode()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $error = oci_error($this->_stmt);
+
+ if (!$error) {
+ return false;
+ }
+
+ return $error['code'];
+ }
+
+
+ /**
+ * Retrieves an array of error information, if any, associated with the
+ * last operation on the statement handle.
+ *
+ * @return array
+ */
+ public function errorInfo()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $error = oci_error($this->_stmt);
+ if (!$error) {
+ return false;
+ }
+
+ if (isset($error['sqltext'])) {
+ return array(
+ $error['code'],
+ $error['message'],
+ $error['offset'],
+ $error['sqltext'],
+ );
+ } else {
+ return array(
+ $error['code'],
+ $error['message'],
+ );
+ }
+ }
+
+
+ /**
+ * Executes a prepared statement.
+ *
+ * @param array $params OPTIONAL Values to bind to parameter placeholders.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function _execute(array $params = null)
+ {
+ $connection = $this->_adapter->getConnection();
+
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if ($params !== null) {
+ if (!is_array($params)) {
+ $params = array($params);
+ }
+ $error = false;
+ foreach (array_keys($params) as $name) {
+ if (!@oci_bind_by_name($this->_stmt, $name, $params[$name], -1)) {
+ $error = true;
+ break;
+ }
+ }
+ if ($error) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(oci_error($this->_stmt));
+ }
+ }
+
+ $retval = @oci_execute($this->_stmt, $this->_adapter->_getExecuteMode());
+ if ($retval === false) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(oci_error($this->_stmt));
+ }
+
+ $this->_keys = Array();
+ if ($field_num = oci_num_fields($this->_stmt)) {
+ for ($i = 1; $i <= $field_num; $i++) {
+ $name = oci_field_name($this->_stmt, $i);
+ $this->_keys[] = $name;
+ }
+ }
+
+ $this->_values = Array();
+ if ($this->_keys) {
+ $this->_values = array_fill(0, count($this->_keys), null);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * Fetches a row from the result set.
+ *
+ * @param int $style OPTIONAL Fetch mode for this fetch operation.
+ * @param int $cursor OPTIONAL Absolute, relative, or other.
+ * @param int $offset OPTIONAL Number for absolute or relative cursors.
+ * @return mixed Array, object, or scalar depending on fetch mode.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetch($style = null, $cursor = null, $offset = null)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if ($style === null) {
+ $style = $this->_fetchMode;
+ }
+
+ $lob_as_string = $this->getLobAsString() ? OCI_RETURN_LOBS : 0;
+
+ switch ($style) {
+ case Zend_Db::FETCH_NUM:
+ $row = oci_fetch_array($this->_stmt, OCI_NUM | OCI_RETURN_NULLS | $lob_as_string);
+ break;
+ case Zend_Db::FETCH_ASSOC:
+ $row = oci_fetch_array($this->_stmt, OCI_ASSOC | OCI_RETURN_NULLS | $lob_as_string);
+ break;
+ case Zend_Db::FETCH_BOTH:
+ $row = oci_fetch_array($this->_stmt, OCI_BOTH | OCI_RETURN_NULLS | $lob_as_string);
+ break;
+ case Zend_Db::FETCH_OBJ:
+ $row = oci_fetch_object($this->_stmt);
+ break;
+ case Zend_Db::FETCH_BOUND:
+ $row = oci_fetch_array($this->_stmt, OCI_BOTH | OCI_RETURN_NULLS | $lob_as_string);
+ if ($row !== false) {
+ return $this->_fetchBound($row);
+ }
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(
+ array(
+ 'code' => 'HYC00',
+ 'message' => "Invalid fetch mode '$style' specified"
+ )
+ );
+ break;
+ }
+
+ if (! $row && $error = oci_error($this->_stmt)) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception($error);
+ }
+
+ if (is_array($row) && array_key_exists('zend_db_rownum', $row)) {
+ unset($row['zend_db_rownum']);
+ }
+
+ return $row;
+ }
+
+ /**
+ * Returns an array containing all of the result set rows.
+ *
+ * @param int $style OPTIONAL Fetch mode.
+ * @param int $col OPTIONAL Column number, if fetch mode is by column.
+ * @return array Collection of rows, each in a format by the fetch mode.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchAll($style = null, $col = 0)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ // make sure we have a fetch mode
+ if ($style === null) {
+ $style = $this->_fetchMode;
+ }
+
+ $flags = OCI_FETCHSTATEMENT_BY_ROW;
+
+ switch ($style) {
+ case Zend_Db::FETCH_BOTH:
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(
+ array(
+ 'code' => 'HYC00',
+ 'message' => "OCI8 driver does not support fetchAll(FETCH_BOTH), use fetch() in a loop instead"
+ )
+ );
+ // notreached
+ $flags |= OCI_NUM;
+ $flags |= OCI_ASSOC;
+ break;
+ case Zend_Db::FETCH_NUM:
+ $flags |= OCI_NUM;
+ break;
+ case Zend_Db::FETCH_ASSOC:
+ $flags |= OCI_ASSOC;
+ break;
+ case Zend_Db::FETCH_OBJ:
+ break;
+ case Zend_Db::FETCH_COLUMN:
+ $flags = $flags &~ OCI_FETCHSTATEMENT_BY_ROW;
+ $flags |= OCI_FETCHSTATEMENT_BY_COLUMN;
+ $flags |= OCI_NUM;
+ break;
+ default:
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(
+ array(
+ 'code' => 'HYC00',
+ 'message' => "Invalid fetch mode '$style' specified"
+ )
+ );
+ break;
+ }
+
+ $result = Array();
+ if ($flags != OCI_FETCHSTATEMENT_BY_ROW) { /* not Zend_Db::FETCH_OBJ */
+ if (! ($rows = oci_fetch_all($this->_stmt, $result, 0, -1, $flags) )) {
+ if ($error = oci_error($this->_stmt)) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception($error);
+ }
+ if (!$rows) {
+ return array();
+ }
+ }
+ if ($style == Zend_Db::FETCH_COLUMN) {
+ $result = $result[$col];
+ }
+ foreach ($result as &$row) {
+ if (is_array($row) && array_key_exists('zend_db_rownum', $row)) {
+ unset($row['zend_db_rownum']);
+ }
+ }
+ } else {
+ while (($row = oci_fetch_object($this->_stmt)) !== false) {
+ $result [] = $row;
+ }
+ if ($error = oci_error($this->_stmt)) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception($error);
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * Returns a single column from the next row of a result set.
+ *
+ * @param int $col OPTIONAL Position of the column to fetch.
+ * @return string
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchColumn($col = 0)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if (!oci_fetch($this->_stmt)) {
+ // if no error, there is simply no record
+ if (!$error = oci_error($this->_stmt)) {
+ return false;
+ }
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception($error);
+ }
+
+ $data = oci_result($this->_stmt, $col+1); //1-based
+ if ($data === false) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(oci_error($this->_stmt));
+ }
+
+ if ($this->getLobAsString()) {
+ // instanceof doesn't allow '-', we must use a temporary string
+ $type = 'OCI-Lob';
+ if ($data instanceof $type) {
+ $data = $data->read($data->size());
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Fetches the next row and returns it as an object.
+ *
+ * @param string $class OPTIONAL Name of the class to create.
+ * @param array $config OPTIONAL Constructor arguments for the class.
+ * @return mixed One object instance of the specified class.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchObject($class = 'stdClass', array $config = array())
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $obj = oci_fetch_object($this->_stmt);
+
+ if ($error = oci_error($this->_stmt)) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception($error);
+ }
+
+ /* @todo XXX handle parameters */
+
+ return $obj;
+ }
+
+ /**
+ * Retrieves the next rowset (result set) for a SQL statement that has
+ * multiple result sets. An example is a stored procedure that returns
+ * the results of multiple queries.
+ *
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function nextRowset()
+ {
+ /**
+ * @see Zend_Db_Statement_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(
+ array(
+ 'code' => 'HYC00',
+ 'message' => 'Optional feature not implemented'
+ )
+ );
+ }
+
+ /**
+ * Returns the number of rows affected by the execution of the
+ * last INSERT, DELETE, or UPDATE statement executed by this
+ * statement object.
+ *
+ * @return int The number of rows affected.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function rowCount()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $num_rows = oci_num_rows($this->_stmt);
+
+ if ($num_rows === false) {
+ /**
+ * @see Zend_Db_Adapter_Oracle_Exception
+ */
+ require_once 'Zend/Db/Statement/Oracle/Exception.php';
+ throw new Zend_Db_Statement_Oracle_Exception(oci_error($this->_stmt));
+ }
+
+ return $num_rows;
+ }
+
+}
diff --git a/library/Zend/Db/Statement/Oracle/Exception.php b/library/Zend/Db/Statement/Oracle/Exception.php
new file mode 100644
index 0000000..26a610b
--- /dev/null
+++ b/library/Zend/Db/Statement/Oracle/Exception.php
@@ -0,0 +1,59 @@
+message = $error['code']." ".$error['message'];
+ } else {
+ $this->message = $error['code']." ".$error['message']." ";
+ $this->message .= substr($error['sqltext'], 0, $error['offset']);
+ $this->message .= "*";
+ $this->message .= substr($error['sqltext'], $error['offset']);
+ }
+ $this->code = $error['code'];
+ }
+ if (!$this->code && $code) {
+ $this->code = $code;
+ }
+ }
+}
+
diff --git a/library/Zend/Db/Statement/Pdo.php b/library/Zend/Db/Statement/Pdo.php
new file mode 100644
index 0000000..7db7e01
--- /dev/null
+++ b/library/Zend/Db/Statement/Pdo.php
@@ -0,0 +1,439 @@
+_stmt = $this->_adapter->getConnection()->prepare($sql);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Bind a column of the statement result set to a PHP variable.
+ *
+ * @param string $column Name the column in the result set, either by
+ * position or by name.
+ * @param mixed $param Reference to the PHP variable containing the value.
+ * @param mixed $type OPTIONAL
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function bindColumn($column, &$param, $type = null)
+ {
+ try {
+ if ($type === null) {
+ return $this->_stmt->bindColumn($column, $param);
+ } else {
+ return $this->_stmt->bindColumn($column, $param, $type);
+ }
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Binds a parameter to the specified variable name.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $variable Reference to PHP variable containing the value.
+ * @param mixed $type OPTIONAL Datatype of SQL parameter.
+ * @param mixed $length OPTIONAL Length of SQL parameter.
+ * @param mixed $options OPTIONAL Other options.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
+ {
+ try {
+ if ($type === null) {
+ if (is_bool($variable)) {
+ $type = PDO::PARAM_BOOL;
+ } elseif ($variable === null) {
+ $type = PDO::PARAM_NULL;
+ } elseif (is_integer($variable)) {
+ $type = PDO::PARAM_INT;
+ } else {
+ $type = PDO::PARAM_STR;
+ }
+ }
+ return $this->_stmt->bindParam($parameter, $variable, $type, $length, $options);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Binds a value to a parameter.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $value Scalar value to bind to the parameter.
+ * @param mixed $type OPTIONAL Datatype of the parameter.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function bindValue($parameter, $value, $type = null)
+ {
+ if (is_string($parameter) && $parameter[0] != ':') {
+ $parameter = ":$parameter";
+ }
+
+ $this->_bindParam[$parameter] = $value;
+
+ try {
+ if ($type === null) {
+ return $this->_stmt->bindValue($parameter, $value);
+ } else {
+ return $this->_stmt->bindValue($parameter, $value, $type);
+ }
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Closes the cursor, allowing the statement to be executed again.
+ *
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function closeCursor()
+ {
+ try {
+ return $this->_stmt->closeCursor();
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Returns the number of columns in the result set.
+ * Returns null if the statement has no result set metadata.
+ *
+ * @return int The number of columns.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function columnCount()
+ {
+ try {
+ return $this->_stmt->columnCount();
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Retrieves the error code, if any, associated with the last operation on
+ * the statement handle.
+ *
+ * @return string error code.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function errorCode()
+ {
+ try {
+ return $this->_stmt->errorCode();
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Retrieves an array of error information, if any, associated with the
+ * last operation on the statement handle.
+ *
+ * @return array
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function errorInfo()
+ {
+ try {
+ return $this->_stmt->errorInfo();
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Executes a prepared statement.
+ *
+ * @param array $params OPTIONAL Values to bind to parameter placeholders.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function _execute(array $params = null)
+ {
+ try {
+ if ($params !== null) {
+ return $this->_stmt->execute($params);
+ } else {
+ return $this->_stmt->execute();
+ }
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), (int) $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Fetches a row from the result set.
+ *
+ * @param int $style OPTIONAL Fetch mode for this fetch operation.
+ * @param int $cursor OPTIONAL Absolute, relative, or other.
+ * @param int $offset OPTIONAL Number for absolute or relative cursors.
+ * @return mixed Array, object, or scalar depending on fetch mode.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetch($style = null, $cursor = null, $offset = null)
+ {
+ if ($style === null) {
+ $style = $this->_fetchMode;
+ }
+ try {
+ return $this->_stmt->fetch($style, $cursor, $offset);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Required by IteratorAggregate interface
+ *
+ * @return IteratorIterator
+ */
+ public function getIterator()
+ {
+ return new IteratorIterator($this->_stmt);
+ }
+
+ /**
+ * Returns an array containing all of the result set rows.
+ *
+ * @param int $style OPTIONAL Fetch mode.
+ * @param int $col OPTIONAL Column number, if fetch mode is by column.
+ * @return array Collection of rows, each in a format by the fetch mode.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchAll($style = null, $col = null)
+ {
+ if ($style === null) {
+ $style = $this->_fetchMode;
+ }
+ try {
+ if ($style == PDO::FETCH_COLUMN) {
+ if ($col === null) {
+ $col = 0;
+ }
+ return $this->_stmt->fetchAll($style, $col);
+ } else {
+ return $this->_stmt->fetchAll($style);
+ }
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Returns a single column from the next row of a result set.
+ *
+ * @param int $col OPTIONAL Position of the column to fetch.
+ * @return string
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchColumn($col = 0)
+ {
+ try {
+ return $this->_stmt->fetchColumn($col);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Fetches the next row and returns it as an object.
+ *
+ * @param string $class OPTIONAL Name of the class to create.
+ * @param array $config OPTIONAL Constructor arguments for the class.
+ * @return mixed One object instance of the specified class.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchObject($class = 'stdClass', array $config = array())
+ {
+ try {
+ return $this->_stmt->fetchObject($class, $config);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Retrieve a statement attribute.
+ *
+ * @param integer $key Attribute name.
+ * @return mixed Attribute value.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function getAttribute($key)
+ {
+ try {
+ return $this->_stmt->getAttribute($key);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Returns metadata for a column in a result set.
+ *
+ * @param int $column
+ * @return mixed
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function getColumnMeta($column)
+ {
+ try {
+ return $this->_stmt->getColumnMeta($column);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Retrieves the next rowset (result set) for a SQL statement that has
+ * multiple result sets. An example is a stored procedure that returns
+ * the results of multiple queries.
+ *
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function nextRowset()
+ {
+ try {
+ return $this->_stmt->nextRowset();
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Returns the number of rows affected by the execution of the
+ * last INSERT, DELETE, or UPDATE statement executed by this
+ * statement object.
+ *
+ * @return int The number of rows affected.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function rowCount()
+ {
+ try {
+ return $this->_stmt->rowCount();
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Set a statement attribute.
+ *
+ * @param string $key Attribute name.
+ * @param mixed $val Attribute value.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function setAttribute($key, $val)
+ {
+ try {
+ return $this->_stmt->setAttribute($key, $val);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Set the default fetch mode for this statement.
+ *
+ * @param int $mode The fetch mode.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function setFetchMode($mode)
+ {
+ $this->_fetchMode = $mode;
+ try {
+ return $this->_stmt->setFetchMode($mode);
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+}
diff --git a/library/Zend/Db/Statement/Pdo/Ibm.php b/library/Zend/Db/Statement/Pdo/Ibm.php
new file mode 100644
index 0000000..52f3ef2
--- /dev/null
+++ b/library/Zend/Db/Statement/Pdo/Ibm.php
@@ -0,0 +1,94 @@
+_adapter->foldCase('ZEND_DB_ROWNUM');
+
+ foreach ($data as $row) {
+ if (is_array($row) && array_key_exists($remove, $row)) {
+ unset($row[$remove]);
+ }
+ $results[] = $row;
+ }
+ return $results;
+ }
+
+ /**
+ * Binds a parameter to the specified variable name.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $variable Reference to PHP variable containing the value.
+ * @param mixed $type OPTIONAL Datatype of SQL parameter.
+ * @param mixed $length OPTIONAL Length of SQL parameter.
+ * @param mixed $options OPTIONAL Other options.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
+ {
+ try {
+ if (($type === null) && ($length === null) && ($options === null)) {
+ return $this->_stmt->bindParam($parameter, $variable);
+ } else {
+ return $this->_stmt->bindParam($parameter, $variable, $type, $length, $options);
+ }
+ } catch (PDOException $e) {
+ require_once 'Zend/Db/Statement/Exception.php';
+ throw new Zend_Db_Statement_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/library/Zend/Db/Statement/Pdo/Oci.php b/library/Zend/Db/Statement/Pdo/Oci.php
new file mode 100644
index 0000000..90658b2
--- /dev/null
+++ b/library/Zend/Db/Statement/Pdo/Oci.php
@@ -0,0 +1,91 @@
+_adapter->foldCase('zend_db_rownum');
+
+ foreach ($data as $row) {
+ if (is_array($row) && array_key_exists($remove, $row)) {
+ unset($row[$remove]);
+ }
+ $results[] = $row;
+ }
+ return $results;
+ }
+
+
+ /**
+ * Fetches a row from the result set.
+ *
+ * @param int $style OPTIONAL Fetch mode for this fetch operation.
+ * @param int $cursor OPTIONAL Absolute, relative, or other.
+ * @param int $offset OPTIONAL Number for absolute or relative cursors.
+ * @return mixed Array, object, or scalar depending on fetch mode.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetch($style = null, $cursor = null, $offset = null)
+ {
+ $row = parent::fetch($style, $cursor, $offset);
+
+ $remove = $this->_adapter->foldCase('zend_db_rownum');
+ if (is_array($row) && array_key_exists($remove, $row)) {
+ unset($row[$remove]);
+ }
+
+ return $row;
+ }
+}
\ No newline at end of file
diff --git a/library/Zend/Db/Statement/Sqlsrv.php b/library/Zend/Db/Statement/Sqlsrv.php
new file mode 100644
index 0000000..8aaaa9d
--- /dev/null
+++ b/library/Zend/Db/Statement/Sqlsrv.php
@@ -0,0 +1,440 @@
+_adapter->getConnection();
+
+ $this->_stmt = sqlsrv_prepare($connection, $sql);
+
+ if (!$this->_stmt) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
+ }
+
+ $this->_originalSQL = $sql;
+ }
+
+ /**
+ * Binds a parameter to the specified variable name.
+ *
+ * @param mixed $parameter Name the parameter, either integer or string.
+ * @param mixed $variable Reference to PHP variable containing the value.
+ * @param mixed $type OPTIONAL Datatype of SQL parameter.
+ * @param mixed $length OPTIONAL Length of SQL parameter.
+ * @param mixed $options OPTIONAL Other options.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
+ {
+ //Sql server doesn't support bind by name
+ return true;
+ }
+
+ /**
+ * Closes the cursor, allowing the statement to be executed again.
+ *
+ * @return bool
+ */
+ public function closeCursor()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ sqlsrv_free_stmt($this->_stmt);
+ $this->_stmt = false;
+ return true;
+ }
+
+ /**
+ * Returns the number of columns in the result set.
+ * Returns null if the statement has no result set metadata.
+ *
+ * @return int The number of columns.
+ */
+ public function columnCount()
+ {
+ if ($this->_stmt && $this->_executed) {
+ return sqlsrv_num_fields($this->_stmt);
+ }
+
+ return 0;
+ }
+
+
+ /**
+ * Retrieves the error code, if any, associated with the last operation on
+ * the statement handle.
+ *
+ * @return string error code.
+ */
+ public function errorCode()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $error = sqlsrv_errors();
+ if (!$error) {
+ return false;
+ }
+
+ return $error[0]['code'];
+ }
+
+
+ /**
+ * Retrieves an array of error information, if any, associated with the
+ * last operation on the statement handle.
+ *
+ * @return array
+ */
+ public function errorInfo()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $error = sqlsrv_errors();
+ if (!$error) {
+ return false;
+ }
+
+ return array(
+ $error[0]['code'],
+ $error[0]['message'],
+ );
+ }
+
+
+ /**
+ * Executes a prepared statement.
+ *
+ * @param array $params OPTIONAL Values to bind to parameter placeholders.
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function _execute(array $params = null)
+ {
+ $connection = $this->_adapter->getConnection();
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if ($params !== null) {
+ if (!is_array($params)) {
+ $params = array($params);
+ }
+ $error = false;
+
+ // make all params passed by reference
+ $params_ = array();
+ $temp = array();
+ $i = 1;
+ foreach ($params as $param) {
+ $temp[$i] = $param;
+ $params_[] = &$temp[$i];
+ $i++;
+ }
+ $params = $params_;
+ }
+
+ $this->_stmt = sqlsrv_query($connection, $this->_originalSQL, $params);
+
+ if (!$this->_stmt) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
+ }
+
+ $this->_executed = true;
+
+ return (!$this->_stmt);
+ }
+
+ /**
+ * Fetches a row from the result set.
+ *
+ * @param int $style OPTIONAL Fetch mode for this fetch operation.
+ * @param int $cursor OPTIONAL Absolute, relative, or other.
+ * @param int $offset OPTIONAL Number for absolute or relative cursors.
+ * @return mixed Array, object, or scalar depending on fetch mode.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetch($style = null, $cursor = null, $offset = null)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if (null === $style) {
+ $style = $this->_fetchMode;
+ }
+
+ $values = sqlsrv_fetch_array($this->_stmt, SQLSRV_FETCH_ASSOC);
+
+ if (!$values && (null !== $error = sqlsrv_errors())) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception($error);
+ }
+
+ if (null === $values) {
+ return null;
+ }
+
+ if (!$this->_keys) {
+ foreach ($values as $key => $value) {
+ $this->_keys[] = $this->_adapter->foldCase($key);
+ }
+ }
+
+ $values = array_values($values);
+
+ $row = false;
+ switch ($style) {
+ case Zend_Db::FETCH_NUM:
+ $row = $values;
+ break;
+ case Zend_Db::FETCH_ASSOC:
+ $row = array_combine($this->_keys, $values);
+ break;
+ case Zend_Db::FETCH_BOTH:
+ $assoc = array_combine($this->_keys, $values);
+ $row = array_merge($values, $assoc);
+ break;
+ case Zend_Db::FETCH_OBJ:
+ $row = (object) array_combine($this->_keys, $values);
+ break;
+ case Zend_Db::FETCH_BOUND:
+ $assoc = array_combine($this->_keys, $values);
+ $row = array_merge($values, $assoc);
+ $row = $this->_fetchBound($row);
+ break;
+ default:
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception("Invalid fetch mode '$style' specified");
+ break;
+ }
+
+ return $row;
+ }
+
+ /**
+ * Returns a single column from the next row of a result set.
+ *
+ * @param int $col OPTIONAL Position of the column to fetch.
+ * @return string
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchColumn($col = 0)
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if (!sqlsrv_fetch($this->_stmt)) {
+ if (null !== $error = sqlsrv_errors()) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception($error);
+ }
+
+ // If no error, there is simply no record
+ return false;
+ }
+
+ $data = sqlsrv_get_field($this->_stmt, $col); //0-based
+ if ($data === false) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
+ }
+
+ return $data;
+ }
+
+ /**
+ * Fetches the next row and returns it as an object.
+ *
+ * @param string $class OPTIONAL Name of the class to create.
+ * @param array $config OPTIONAL Constructor arguments for the class.
+ * @return mixed One object instance of the specified class.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function fetchObject($class = 'stdClass', array $config = array())
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ $obj = sqlsrv_fetch_object($this->_stmt);
+
+ if ($error = sqlsrv_errors()) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception($error);
+ }
+
+ /* @todo XXX handle parameters */
+
+ if (null === $obj) {
+ return false;
+ }
+
+ return $obj;
+ }
+
+ /**
+ * Returns metadata for a column in a result set.
+ *
+ * @param int $column
+ * @return mixed
+ * @throws Zend_Db_Statement_Sqlsrv_Exception
+ */
+ public function getColumnMeta($column)
+ {
+ $fields = sqlsrv_field_metadata($this->_stmt);
+
+ if (!$fields) {
+ throw new Zend_Db_Statement_Sqlsrv_Exception('Column metadata can not be fetched');
+ }
+
+ if (!isset($fields[$column])) {
+ throw new Zend_Db_Statement_Sqlsrv_Exception('Column index does not exist in statement');
+ }
+
+ return $fields[$column];
+ }
+
+ /**
+ * Retrieves the next rowset (result set) for a SQL statement that has
+ * multiple result sets. An example is a stored procedure that returns
+ * the results of multiple queries.
+ *
+ * @return bool
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function nextRowset()
+ {
+ if (sqlsrv_next_result($this->_stmt) === false) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
+ }
+
+ // reset column keys
+ $this->_keys = null;
+
+ return true;
+ }
+
+ /**
+ * Returns the number of rows affected by the execution of the
+ * last INSERT, DELETE, or UPDATE statement executed by this
+ * statement object.
+ *
+ * @return int The number of rows affected.
+ * @throws Zend_Db_Statement_Exception
+ */
+ public function rowCount()
+ {
+ if (!$this->_stmt) {
+ return false;
+ }
+
+ if (!$this->_executed) {
+ return 0;
+ }
+
+ $num_rows = sqlsrv_rows_affected($this->_stmt);
+
+ // Strict check is necessary; 0 is a valid return value
+ if ($num_rows === false) {
+ require_once 'Zend/Db/Statement/Sqlsrv/Exception.php';
+ throw new Zend_Db_Statement_Sqlsrv_Exception(sqlsrv_errors());
+ }
+
+ return $num_rows;
+ }
+
+ /**
+ * Returns an array containing all of the result set rows.
+ *
+ * @param int $style OPTIONAL Fetch mode.
+ * @param int $col OPTIONAL Column number, if fetch mode is by column.
+ * @return array Collection of rows, each in a format by the fetch mode.
+ *
+ * Behaves like parent, but if limit()
+ * is used, the final result removes the extra column
+ * 'zend_db_rownum'
+ */
+ public function fetchAll($style = null, $col = null)
+ {
+ $data = parent::fetchAll($style, $col);
+ $results = array();
+ $remove = $this->_adapter->foldCase('ZEND_DB_ROWNUM');
+
+ foreach ($data as $row) {
+ if (is_array($row) && array_key_exists($remove, $row)) {
+ unset($row[$remove]);
+ }
+ $results[] = $row;
+ }
+ return $results;
+ }
+}
diff --git a/library/Zend/Db/Statement/Sqlsrv/Exception.php b/library/Zend/Db/Statement/Sqlsrv/Exception.php
new file mode 100644
index 0000000..3c44b14
--- /dev/null
+++ b/library/Zend/Db/Statement/Sqlsrv/Exception.php
@@ -0,0 +1,61 @@
+ $config);
+ } else {
+ // process this as table with or without a definition
+ if ($definition instanceof Zend_Db_Table_Definition
+ && $definition->hasTableConfig($config)) {
+ // this will have DEFINITION_CONFIG_NAME & DEFINITION
+ $config = $definition->getTableConfig($config);
+ } else {
+ $config = array(self::NAME => $config);
+ }
+ }
+ }
+
+ parent::__construct($config);
+ }
+}
diff --git a/library/Zend/Db/Table/Abstract.php b/library/Zend/Db/Table/Abstract.php
new file mode 100644
index 0000000..c97d874
--- /dev/null
+++ b/library/Zend/Db/Table/Abstract.php
@@ -0,0 +1,1534 @@
+ $config);
+ }
+
+ if ($config) {
+ $this->setOptions($config);
+ }
+
+ $this->_setup();
+ $this->init();
+ }
+
+ /**
+ * setOptions()
+ *
+ * @param array $options
+ * @return Zend_Db_Table_Abstract
+ */
+ public function setOptions(Array $options)
+ {
+ foreach ($options as $key => $value) {
+ switch ($key) {
+ case self::ADAPTER:
+ $this->_setAdapter($value);
+ break;
+ case self::DEFINITION:
+ $this->setDefinition($value);
+ break;
+ case self::DEFINITION_CONFIG_NAME:
+ $this->setDefinitionConfigName($value);
+ break;
+ case self::SCHEMA:
+ $this->_schema = (string) $value;
+ break;
+ case self::NAME:
+ $this->_name = (string) $value;
+ break;
+ case self::PRIMARY:
+ $this->_primary = (array) $value;
+ break;
+ case self::ROW_CLASS:
+ $this->setRowClass($value);
+ break;
+ case self::ROWSET_CLASS:
+ $this->setRowsetClass($value);
+ break;
+ case self::REFERENCE_MAP:
+ $this->setReferences($value);
+ break;
+ case self::DEPENDENT_TABLES:
+ $this->setDependentTables($value);
+ break;
+ case self::METADATA_CACHE:
+ $this->_setMetadataCache($value);
+ break;
+ case self::METADATA_CACHE_IN_CLASS:
+ $this->setMetadataCacheInClass($value);
+ break;
+ case self::SEQUENCE:
+ $this->_setSequence($value);
+ break;
+ default:
+ // ignore unrecognized configuration directive
+ break;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * setDefinition()
+ *
+ * @param Zend_Db_Table_Definition $definition
+ * @return Zend_Db_Table_Abstract
+ */
+ public function setDefinition(Zend_Db_Table_Definition $definition)
+ {
+ $this->_definition = $definition;
+ return $this;
+ }
+
+ /**
+ * getDefinition()
+ *
+ * @return Zend_Db_Table_Definition|null
+ */
+ public function getDefinition()
+ {
+ return $this->_definition;
+ }
+
+ /**
+ * setDefinitionConfigName()
+ *
+ * @param string $definition
+ * @return Zend_Db_Table_Abstract
+ */
+ public function setDefinitionConfigName($definitionConfigName)
+ {
+ $this->_definitionConfigName = $definitionConfigName;
+ return $this;
+ }
+
+ /**
+ * getDefinitionConfigName()
+ *
+ * @return string
+ */
+ public function getDefinitionConfigName()
+ {
+ return $this->_definitionConfigName;
+ }
+
+ /**
+ * @param string $classname
+ * @return Zend_Db_Table_Abstract Provides a fluent interface
+ */
+ public function setRowClass($classname)
+ {
+ $this->_rowClass = (string) $classname;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRowClass()
+ {
+ return $this->_rowClass;
+ }
+
+ /**
+ * @param string $classname
+ * @return Zend_Db_Table_Abstract Provides a fluent interface
+ */
+ public function setRowsetClass($classname)
+ {
+ $this->_rowsetClass = (string) $classname;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRowsetClass()
+ {
+ return $this->_rowsetClass;
+ }
+
+ /**
+ * Add a reference to the reference map
+ *
+ * @param string $ruleKey
+ * @param string|array $columns
+ * @param string $refTableClass
+ * @param string|array $refColumns
+ * @param string $onDelete
+ * @param string $onUpdate
+ * @return Zend_Db_Table_Abstract
+ */
+ public function addReference($ruleKey, $columns, $refTableClass, $refColumns,
+ $onDelete = null, $onUpdate = null)
+ {
+ $reference = array(self::COLUMNS => (array) $columns,
+ self::REF_TABLE_CLASS => $refTableClass,
+ self::REF_COLUMNS => (array) $refColumns);
+
+ if (!empty($onDelete)) {
+ $reference[self::ON_DELETE] = $onDelete;
+ }
+
+ if (!empty($onUpdate)) {
+ $reference[self::ON_UPDATE] = $onUpdate;
+ }
+
+ $this->_referenceMap[$ruleKey] = $reference;
+
+ return $this;
+ }
+
+ /**
+ * @param array $referenceMap
+ * @return Zend_Db_Table_Abstract Provides a fluent interface
+ */
+ public function setReferences(array $referenceMap)
+ {
+ $this->_referenceMap = $referenceMap;
+
+ return $this;
+ }
+
+ /**
+ * @param string $tableClassname
+ * @param string $ruleKey OPTIONAL
+ * @return array
+ * @throws Zend_Db_Table_Exception
+ */
+ public function getReference($tableClassname, $ruleKey = null)
+ {
+ $thisClass = get_class($this);
+ if ($thisClass === 'Zend_Db_Table') {
+ $thisClass = $this->_definitionConfigName;
+ }
+ $refMap = $this->_getReferenceMapNormalized();
+ if ($ruleKey !== null) {
+ if (!isset($refMap[$ruleKey])) {
+ require_once "Zend/Db/Table/Exception.php";
+ throw new Zend_Db_Table_Exception("No reference rule \"$ruleKey\" from table $thisClass to table $tableClassname");
+ }
+ if ($refMap[$ruleKey][self::REF_TABLE_CLASS] != $tableClassname) {
+ require_once "Zend/Db/Table/Exception.php";
+ throw new Zend_Db_Table_Exception("Reference rule \"$ruleKey\" does not reference table $tableClassname");
+ }
+ return $refMap[$ruleKey];
+ }
+ foreach ($refMap as $reference) {
+ if ($reference[self::REF_TABLE_CLASS] == $tableClassname) {
+ return $reference;
+ }
+ }
+ require_once "Zend/Db/Table/Exception.php";
+ throw new Zend_Db_Table_Exception("No reference from table $thisClass to table $tableClassname");
+ }
+
+ /**
+ * @param array $dependentTables
+ * @return Zend_Db_Table_Abstract Provides a fluent interface
+ */
+ public function setDependentTables(array $dependentTables)
+ {
+ $this->_dependentTables = $dependentTables;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getDependentTables()
+ {
+ return $this->_dependentTables;
+ }
+
+ /**
+ * set the defaultSource property - this tells the table class where to find default values
+ *
+ * @param string $defaultSource
+ * @return Zend_Db_Table_Abstract
+ */
+ public function setDefaultSource($defaultSource = self::DEFAULT_NONE)
+ {
+ if (!in_array($defaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE))) {
+ $defaultSource = self::DEFAULT_NONE;
+ }
+
+ $this->_defaultSource = $defaultSource;
+ return $this;
+ }
+
+ /**
+ * returns the default source flag that determines where defaultSources come from
+ *
+ * @return unknown
+ */
+ public function getDefaultSource()
+ {
+ return $this->_defaultSource;
+ }
+
+ /**
+ * set the default values for the table class
+ *
+ * @param array $defaultValues
+ * @return Zend_Db_Table_Abstract
+ */
+ public function setDefaultValues(Array $defaultValues)
+ {
+ foreach ($defaultValues as $defaultName => $defaultValue) {
+ if (array_key_exists($defaultName, $this->_metadata)) {
+ $this->_defaultValues[$defaultName] = $defaultValue;
+ }
+ }
+ return $this;
+ }
+
+ public function getDefaultValues()
+ {
+ return $this->_defaultValues;
+ }
+
+
+ /**
+ * Sets the default Zend_Db_Adapter_Abstract for all Zend_Db_Table objects.
+ *
+ * @param mixed $db Either an Adapter object, or a string naming a Registry key
+ * @return void
+ */
+ public static function setDefaultAdapter($db = null)
+ {
+ self::$_defaultDb = self::_setupAdapter($db);
+ }
+
+ /**
+ * Gets the default Zend_Db_Adapter_Abstract for all Zend_Db_Table objects.
+ *
+ * @return Zend_Db_Adapter_Abstract or null
+ */
+ public static function getDefaultAdapter()
+ {
+ return self::$_defaultDb;
+ }
+
+ /**
+ * @param mixed $db Either an Adapter object, or a string naming a Registry key
+ * @return Zend_Db_Table_Abstract Provides a fluent interface
+ */
+ protected function _setAdapter($db)
+ {
+ $this->_db = self::_setupAdapter($db);
+ return $this;
+ }
+
+ /**
+ * Gets the Zend_Db_Adapter_Abstract for this particular Zend_Db_Table object.
+ *
+ * @return Zend_Db_Adapter_Abstract
+ */
+ public function getAdapter()
+ {
+ return $this->_db;
+ }
+
+ /**
+ * @param mixed $db Either an Adapter object, or a string naming a Registry key
+ * @return Zend_Db_Adapter_Abstract
+ * @throws Zend_Db_Table_Exception
+ */
+ protected static function _setupAdapter($db)
+ {
+ if ($db === null) {
+ return null;
+ }
+ if (is_string($db)) {
+ require_once 'Zend/Registry.php';
+ $db = Zend_Registry::get($db);
+ }
+ if (!$db instanceof Zend_Db_Adapter_Abstract) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception('Argument must be of type Zend_Db_Adapter_Abstract, or a Registry key where a Zend_Db_Adapter_Abstract object is stored');
+ }
+ return $db;
+ }
+
+ /**
+ * Sets the default metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
+ *
+ * If $defaultMetadataCache is null, then no metadata cache is used by default.
+ *
+ * @param mixed $metadataCache Either a Cache object, or a string naming a Registry key
+ * @return void
+ */
+ public static function setDefaultMetadataCache($metadataCache = null)
+ {
+ self::$_defaultMetadataCache = self::_setupMetadataCache($metadataCache);
+ }
+
+ /**
+ * Gets the default metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
+ *
+ * @return Zend_Cache_Core or null
+ */
+ public static function getDefaultMetadataCache()
+ {
+ return self::$_defaultMetadataCache;
+ }
+
+ /**
+ * Sets the metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
+ *
+ * If $metadataCache is null, then no metadata cache is used. Since there is no opportunity to reload metadata
+ * after instantiation, this method need not be public, particularly because that it would have no effect
+ * results in unnecessary API complexity. To configure the metadata cache, use the metadataCache configuration
+ * option for the class constructor upon instantiation.
+ *
+ * @param mixed $metadataCache Either a Cache object, or a string naming a Registry key
+ * @return Zend_Db_Table_Abstract Provides a fluent interface
+ */
+ protected function _setMetadataCache($metadataCache)
+ {
+ $this->_metadataCache = self::_setupMetadataCache($metadataCache);
+ return $this;
+ }
+
+ /**
+ * Gets the metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
+ *
+ * @return Zend_Cache_Core or null
+ */
+ public function getMetadataCache()
+ {
+ return $this->_metadataCache;
+ }
+
+ /**
+ * Indicate whether metadata should be cached in the class for the duration
+ * of the instance
+ *
+ * @param bool $flag
+ * @return Zend_Db_Table_Abstract
+ */
+ public function setMetadataCacheInClass($flag)
+ {
+ $this->_metadataCacheInClass = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Retrieve flag indicating if metadata should be cached for duration of
+ * instance
+ *
+ * @return bool
+ */
+ public function metadataCacheInClass()
+ {
+ return $this->_metadataCacheInClass;
+ }
+
+ /**
+ * @param mixed $metadataCache Either a Cache object, or a string naming a Registry key
+ * @return Zend_Cache_Core
+ * @throws Zend_Db_Table_Exception
+ */
+ protected static function _setupMetadataCache($metadataCache)
+ {
+ if ($metadataCache === null) {
+ return null;
+ }
+ if (is_string($metadataCache)) {
+ require_once 'Zend/Registry.php';
+ $metadataCache = Zend_Registry::get($metadataCache);
+ }
+ if (!$metadataCache instanceof Zend_Cache_Core) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception('Argument must be of type Zend_Cache_Core, or a Registry key where a Zend_Cache_Core object is stored');
+ }
+ return $metadataCache;
+ }
+
+ /**
+ * Sets the sequence member, which defines the behavior for generating
+ * primary key values in new rows.
+ * - If this is a string, then the string names the sequence object.
+ * - If this is boolean true, then the key uses an auto-incrementing
+ * or identity mechanism.
+ * - If this is boolean false, then the key is user-defined.
+ * Use this for natural keys, for example.
+ *
+ * @param mixed $sequence
+ * @return Zend_Db_Table_Adapter_Abstract Provides a fluent interface
+ */
+ protected function _setSequence($sequence)
+ {
+ $this->_sequence = $sequence;
+
+ return $this;
+ }
+
+ /**
+ * Turnkey for initialization of a table object.
+ * Calls other protected methods for individual tasks, to make it easier
+ * for a subclass to override part of the setup logic.
+ *
+ * @return void
+ */
+ protected function _setup()
+ {
+ $this->_setupDatabaseAdapter();
+ $this->_setupTableName();
+ }
+
+ /**
+ * Initialize database adapter.
+ *
+ * @return void
+ * @throws Zend_Db_Table_Exception
+ */
+ protected function _setupDatabaseAdapter()
+ {
+ if (! $this->_db) {
+ $this->_db = self::getDefaultAdapter();
+ if (!$this->_db instanceof Zend_Db_Adapter_Abstract) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception('No adapter found for ' . get_class($this));
+ }
+ }
+ }
+
+ /**
+ * Initialize table and schema names.
+ *
+ * If the table name is not set in the class definition,
+ * use the class name itself as the table name.
+ *
+ * A schema name provided with the table name (e.g., "schema.table") overrides
+ * any existing value for $this->_schema.
+ *
+ * @return void
+ */
+ protected function _setupTableName()
+ {
+ if (! $this->_name) {
+ $this->_name = get_class($this);
+ } else if (strpos($this->_name, '.')) {
+ list($this->_schema, $this->_name) = explode('.', $this->_name);
+ }
+ }
+
+ /**
+ * Initializes metadata.
+ *
+ * If metadata cannot be loaded from cache, adapter's describeTable() method is called to discover metadata
+ * information. Returns true if and only if the metadata are loaded from cache.
+ *
+ * @return boolean
+ * @throws Zend_Db_Table_Exception
+ */
+ protected function _setupMetadata()
+ {
+ if ($this->metadataCacheInClass() && (count($this->_metadata) > 0)) {
+ return true;
+ }
+
+ // Assume that metadata will be loaded from cache
+ $isMetadataFromCache = true;
+
+ // If $this has no metadata cache but the class has a default metadata cache
+ if (null === $this->_metadataCache && null !== self::$_defaultMetadataCache) {
+ // Make $this use the default metadata cache of the class
+ $this->_setMetadataCache(self::$_defaultMetadataCache);
+ }
+
+ // If $this has a metadata cache
+ if (null !== $this->_metadataCache) {
+ // Define the cache identifier where the metadata are saved
+
+ //get db configuration
+ $dbConfig = $this->_db->getConfig();
+
+ $port = isset($dbConfig['options']['port'])
+ ? ':'.$dbConfig['options']['port']
+ : (isset($dbConfig['port'])
+ ? ':'.$dbConfig['port']
+ : null);
+
+ $host = isset($dbConfig['options']['host'])
+ ? ':'.$dbConfig['options']['host']
+ : (isset($dbConfig['host'])
+ ? ':'.$dbConfig['host']
+ : null);
+
+ // Define the cache identifier where the metadata are saved
+ $cacheId = md5( // port:host/dbname:schema.table (based on availabilty)
+ $port . $host . '/'. $dbConfig['dbname'] . ':'
+ . $this->_schema. '.' . $this->_name
+ );
+ }
+
+ // If $this has no metadata cache or metadata cache misses
+ if (null === $this->_metadataCache || !($metadata = $this->_metadataCache->load($cacheId))) {
+ // Metadata are not loaded from cache
+ $isMetadataFromCache = false;
+ // Fetch metadata from the adapter's describeTable() method
+ $metadata = $this->_db->describeTable($this->_name, $this->_schema);
+ // If $this has a metadata cache, then cache the metadata
+ if (null !== $this->_metadataCache && !$this->_metadataCache->save($metadata, $cacheId)) {
+ trigger_error('Failed saving metadata to metadataCache', E_USER_NOTICE);
+ }
+ }
+
+ // Assign the metadata to $this
+ $this->_metadata = $metadata;
+
+ // Return whether the metadata were loaded from cache
+ return $isMetadataFromCache;
+ }
+
+ /**
+ * Retrieve table columns
+ *
+ * @return array
+ */
+ protected function _getCols()
+ {
+ if (null === $this->_cols) {
+ $this->_setupMetadata();
+ $this->_cols = array_keys($this->_metadata);
+ }
+ return $this->_cols;
+ }
+
+ /**
+ * Initialize primary key from metadata.
+ * If $_primary is not defined, discover primary keys
+ * from the information returned by describeTable().
+ *
+ * @return void
+ * @throws Zend_Db_Table_Exception
+ */
+ protected function _setupPrimaryKey()
+ {
+ if (!$this->_primary) {
+ $this->_setupMetadata();
+ $this->_primary = array();
+ foreach ($this->_metadata as $col) {
+ if ($col['PRIMARY']) {
+ $this->_primary[ $col['PRIMARY_POSITION'] ] = $col['COLUMN_NAME'];
+ if ($col['IDENTITY']) {
+ $this->_identity = $col['PRIMARY_POSITION'];
+ }
+ }
+ }
+ // if no primary key was specified and none was found in the metadata
+ // then throw an exception.
+ if (empty($this->_primary)) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception('A table must have a primary key, but none was found');
+ }
+ } else if (!is_array($this->_primary)) {
+ $this->_primary = array(1 => $this->_primary);
+ } else if (isset($this->_primary[0])) {
+ array_unshift($this->_primary, null);
+ unset($this->_primary[0]);
+ }
+
+ $cols = $this->_getCols();
+ if (! array_intersect((array) $this->_primary, $cols) == (array) $this->_primary) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception("Primary key column(s) ("
+ . implode(',', (array) $this->_primary)
+ . ") are not columns in this table ("
+ . implode(',', $cols)
+ . ")");
+ }
+
+ $primary = (array) $this->_primary;
+ $pkIdentity = $primary[(int) $this->_identity];
+
+ /**
+ * Special case for PostgreSQL: a SERIAL key implicitly uses a sequence
+ * object whose name is "__seq".
+ */
+ if ($this->_sequence === true && $this->_db instanceof Zend_Db_Adapter_Pdo_Pgsql) {
+ $this->_sequence = $this->_db->quoteIdentifier("{$this->_name}_{$pkIdentity}_seq");
+ if ($this->_schema) {
+ $this->_sequence = $this->_db->quoteIdentifier($this->_schema) . '.' . $this->_sequence;
+ }
+ }
+ }
+
+ /**
+ * Returns a normalized version of the reference map
+ *
+ * @return array
+ */
+ protected function _getReferenceMapNormalized()
+ {
+ $referenceMapNormalized = array();
+
+ foreach ($this->_referenceMap as $rule => $map) {
+
+ $referenceMapNormalized[$rule] = array();
+
+ foreach ($map as $key => $value) {
+ switch ($key) {
+
+ // normalize COLUMNS and REF_COLUMNS to arrays
+ case self::COLUMNS:
+ case self::REF_COLUMNS:
+ if (!is_array($value)) {
+ $referenceMapNormalized[$rule][$key] = array($value);
+ } else {
+ $referenceMapNormalized[$rule][$key] = $value;
+ }
+ break;
+
+ // other values are copied as-is
+ default:
+ $referenceMapNormalized[$rule][$key] = $value;
+ break;
+ }
+ }
+ }
+
+ return $referenceMapNormalized;
+ }
+
+ /**
+ * Initialize object
+ *
+ * Called from {@link __construct()} as final step of object instantiation.
+ *
+ * @return void
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * Returns table information.
+ *
+ * You can elect to return only a part of this information by supplying its key name,
+ * otherwise all information is returned as an array.
+ *
+ * @param string $key The specific info part to return OPTIONAL
+ * @return mixed
+ * @throws Zend_Db_Table_Exception
+ */
+ public function info($key = null)
+ {
+ $this->_setupPrimaryKey();
+
+ $info = array(
+ self::SCHEMA => $this->_schema,
+ self::NAME => $this->_name,
+ self::COLS => $this->_getCols(),
+ self::PRIMARY => (array) $this->_primary,
+ self::METADATA => $this->_metadata,
+ self::ROW_CLASS => $this->getRowClass(),
+ self::ROWSET_CLASS => $this->getRowsetClass(),
+ self::REFERENCE_MAP => $this->_referenceMap,
+ self::DEPENDENT_TABLES => $this->_dependentTables,
+ self::SEQUENCE => $this->_sequence
+ );
+
+ if ($key === null) {
+ return $info;
+ }
+
+ if (!array_key_exists($key, $info)) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception('There is no table information for the key "' . $key . '"');
+ }
+
+ return $info[$key];
+ }
+
+ /**
+ * Returns an instance of a Zend_Db_Table_Select object.
+ *
+ * @param bool $withFromPart Whether or not to include the from part of the select based on the table
+ * @return Zend_Db_Table_Select
+ */
+ public function select($withFromPart = self::SELECT_WITHOUT_FROM_PART)
+ {
+ require_once 'Zend/Db/Table/Select.php';
+ $select = new Zend_Db_Table_Select($this);
+ if ($withFromPart == self::SELECT_WITH_FROM_PART) {
+ $select->from($this->info(self::NAME), Zend_Db_Table_Select::SQL_WILDCARD, $this->info(self::SCHEMA));
+ }
+ return $select;
+ }
+
+ /**
+ * Inserts a new row.
+ *
+ * @param array $data Column-value pairs.
+ * @return mixed The primary key of the row inserted.
+ */
+ public function insert(array $data)
+ {
+ $this->_setupPrimaryKey();
+
+ /**
+ * Zend_Db_Table assumes that if you have a compound primary key
+ * and one of the columns in the key uses a sequence,
+ * it's the _first_ column in the compound key.
+ */
+ $primary = (array) $this->_primary;
+ $pkIdentity = $primary[(int)$this->_identity];
+
+ /**
+ * If this table uses a database sequence object and the data does not
+ * specify a value, then get the next ID from the sequence and add it
+ * to the row. We assume that only the first column in a compound
+ * primary key takes a value from a sequence.
+ */
+ if (is_string($this->_sequence) && !isset($data[$pkIdentity])) {
+ $data[$pkIdentity] = $this->_db->nextSequenceId($this->_sequence);
+ $pkSuppliedBySequence = true;
+ }
+
+ /**
+ * If the primary key can be generated automatically, and no value was
+ * specified in the user-supplied data, then omit it from the tuple.
+ *
+ * Note: this checks for sensible values in the supplied primary key
+ * position of the data. The following values are considered empty:
+ * null, false, true, '', array()
+ */
+ if (!isset($pkSuppliedBySequence) && array_key_exists($pkIdentity, $data)) {
+ if ($data[$pkIdentity] === null // null
+ || $data[$pkIdentity] === '' // empty string
+ || is_bool($data[$pkIdentity]) // boolean
+ || (is_array($data[$pkIdentity]) && empty($data[$pkIdentity]))) { // empty array
+ unset($data[$pkIdentity]);
+ }
+ }
+
+ /**
+ * INSERT the new row.
+ */
+ $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
+ $this->_db->insert($tableSpec, $data);
+
+ /**
+ * Fetch the most recent ID generated by an auto-increment
+ * or IDENTITY column, unless the user has specified a value,
+ * overriding the auto-increment mechanism.
+ */
+ if ($this->_sequence === true && !isset($data[$pkIdentity])) {
+ $data[$pkIdentity] = $this->_db->lastInsertId();
+ }
+
+ /**
+ * Return the primary key value if the PK is a single column,
+ * else return an associative array of the PK column/value pairs.
+ */
+ $pkData = array_intersect_key($data, array_flip($primary));
+ if (count($primary) == 1) {
+ reset($pkData);
+ return current($pkData);
+ }
+
+ return $pkData;
+ }
+
+ /**
+ * Check if the provided column is an identity of the table
+ *
+ * @param string $column
+ * @throws Zend_Db_Table_Exception
+ * @return boolean
+ */
+ public function isIdentity($column)
+ {
+ $this->_setupPrimaryKey();
+
+ if (!isset($this->_metadata[$column])) {
+ /**
+ * @see Zend_Db_Table_Exception
+ */
+ require_once 'Zend/Db/Table/Exception.php';
+
+ throw new Zend_Db_Table_Exception('Column "' . $column . '" not found in table.');
+ }
+
+ return (bool) $this->_metadata[$column]['IDENTITY'];
+ }
+
+ /**
+ * Updates existing rows.
+ *
+ * @param array $data Column-value pairs.
+ * @param array|string $where An SQL WHERE clause, or an array of SQL WHERE clauses.
+ * @return int The number of rows updated.
+ */
+ public function update(array $data, $where)
+ {
+ $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
+ return $this->_db->update($tableSpec, $data, $where);
+ }
+
+ /**
+ * Called by a row object for the parent table's class during save() method.
+ *
+ * @param string $parentTableClassname
+ * @param array $oldPrimaryKey
+ * @param array $newPrimaryKey
+ * @return int
+ */
+ public function _cascadeUpdate($parentTableClassname, array $oldPrimaryKey, array $newPrimaryKey)
+ {
+ $this->_setupMetadata();
+ $rowsAffected = 0;
+ foreach ($this->_getReferenceMapNormalized() as $map) {
+ if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_UPDATE])) {
+ switch ($map[self::ON_UPDATE]) {
+ case self::CASCADE:
+ $newRefs = array();
+ $where = array();
+ for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
+ $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
+ $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
+ if (array_key_exists($refCol, $newPrimaryKey)) {
+ $newRefs[$col] = $newPrimaryKey[$refCol];
+ }
+ $type = $this->_metadata[$col]['DATA_TYPE'];
+ $where[] = $this->_db->quoteInto(
+ $this->_db->quoteIdentifier($col, true) . ' = ?',
+ $oldPrimaryKey[$refCol], $type);
+ }
+ $rowsAffected += $this->update($newRefs, $where);
+ break;
+ default:
+ // no action
+ break;
+ }
+ }
+ }
+ return $rowsAffected;
+ }
+
+ /**
+ * Deletes existing rows.
+ *
+ * @param array|string $where SQL WHERE clause(s).
+ * @return int The number of rows deleted.
+ */
+ public function delete($where)
+ {
+ $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
+ return $this->_db->delete($tableSpec, $where);
+ }
+
+ /**
+ * Called by parent table's class during delete() method.
+ *
+ * @param string $parentTableClassname
+ * @param array $primaryKey
+ * @return int Number of affected rows
+ */
+ public function _cascadeDelete($parentTableClassname, array $primaryKey)
+ {
+ $this->_setupMetadata();
+ $rowsAffected = 0;
+ foreach ($this->_getReferenceMapNormalized() as $map) {
+ if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_DELETE])) {
+ switch ($map[self::ON_DELETE]) {
+ case self::CASCADE:
+ $where = array();
+ for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
+ $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
+ $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
+ $type = $this->_metadata[$col]['DATA_TYPE'];
+ $where[] = $this->_db->quoteInto(
+ $this->_db->quoteIdentifier($col, true) . ' = ?',
+ $primaryKey[$refCol], $type);
+ }
+ $rowsAffected += $this->delete($where);
+ break;
+ default:
+ // no action
+ break;
+ }
+ }
+ }
+ return $rowsAffected;
+ }
+
+ /**
+ * Fetches rows by primary key. The argument specifies one or more primary
+ * key value(s). To find multiple rows by primary key, the argument must
+ * be an array.
+ *
+ * This method accepts a variable number of arguments. If the table has a
+ * multi-column primary key, the number of arguments must be the same as
+ * the number of columns in the primary key. To find multiple rows in a
+ * table with a multi-column primary key, each argument must be an array
+ * with the same number of elements.
+ *
+ * The find() method always returns a Rowset object, even if only one row
+ * was found.
+ *
+ * @param mixed $key The value(s) of the primary keys.
+ * @return Zend_Db_Table_Rowset_Abstract Row(s) matching the criteria.
+ * @throws Zend_Db_Table_Exception
+ */
+ public function find()
+ {
+ $this->_setupPrimaryKey();
+ $args = func_get_args();
+ $keyNames = array_values((array) $this->_primary);
+
+ if (count($args) < count($keyNames)) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception("Too few columns for the primary key");
+ }
+
+ if (count($args) > count($keyNames)) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception("Too many columns for the primary key");
+ }
+
+ $whereList = array();
+ $numberTerms = 0;
+ foreach ($args as $keyPosition => $keyValues) {
+ $keyValuesCount = count($keyValues);
+ // Coerce the values to an array.
+ // Don't simply typecast to array, because the values
+ // might be Zend_Db_Expr objects.
+ if (!is_array($keyValues)) {
+ $keyValues = array($keyValues);
+ }
+ if ($numberTerms == 0) {
+ $numberTerms = $keyValuesCount;
+ } else if ($keyValuesCount != $numberTerms) {
+ require_once 'Zend/Db/Table/Exception.php';
+ throw new Zend_Db_Table_Exception("Missing value(s) for the primary key");
+ }
+ $keyValues = array_values($keyValues);
+ for ($i = 0; $i < $keyValuesCount; ++$i) {
+ if (!isset($whereList[$i])) {
+ $whereList[$i] = array();
+ }
+ $whereList[$i][$keyPosition] = $keyValues[$i];
+ }
+ }
+
+ $whereClause = null;
+ if (count($whereList)) {
+ $whereOrTerms = array();
+ $tableName = $this->_db->quoteTableAs($this->_name, null, true);
+ foreach ($whereList as $keyValueSets) {
+ $whereAndTerms = array();
+ foreach ($keyValueSets as $keyPosition => $keyValue) {
+ $type = $this->_metadata[$keyNames[$keyPosition]]['DATA_TYPE'];
+ $columnName = $this->_db->quoteIdentifier($keyNames[$keyPosition], true);
+ $whereAndTerms[] = $this->_db->quoteInto(
+ $tableName . '.' . $columnName . ' = ?',
+ $keyValue, $type);
+ }
+ $whereOrTerms[] = '(' . implode(' AND ', $whereAndTerms) . ')';
+ }
+ $whereClause = '(' . implode(' OR ', $whereOrTerms) . ')';
+ }
+
+ // issue ZF-5775 (empty where clause should return empty rowset)
+ if ($whereClause == null) {
+ $rowsetClass = $this->getRowsetClass();
+ if (!class_exists($rowsetClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($rowsetClass);
+ }
+ return new $rowsetClass(array('table' => $this, 'rowClass' => $this->getRowClass(), 'stored' => true));
+ }
+
+ return $this->fetchAll($whereClause);
+ }
+
+ /**
+ * Fetches all rows.
+ *
+ * Honors the Zend_Db_Adapter fetch mode.
+ *
+ * @param string|array|Zend_Db_Table_Select $where OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
+ * @param string|array $order OPTIONAL An SQL ORDER clause.
+ * @param int $count OPTIONAL An SQL LIMIT count.
+ * @param int $offset OPTIONAL An SQL LIMIT offset.
+ * @return Zend_Db_Table_Rowset_Abstract The row results per the Zend_Db_Adapter fetch mode.
+ */
+ public function fetchAll($where = null, $order = null, $count = null, $offset = null)
+ {
+ if (!($where instanceof Zend_Db_Table_Select)) {
+ $select = $this->select();
+
+ if ($where !== null) {
+ $this->_where($select, $where);
+ }
+
+ if ($order !== null) {
+ $this->_order($select, $order);
+ }
+
+ if ($count !== null || $offset !== null) {
+ $select->limit($count, $offset);
+ }
+
+ } else {
+ $select = $where;
+ }
+
+ $rows = $this->_fetch($select);
+
+ $data = array(
+ 'table' => $this,
+ 'data' => $rows,
+ 'readOnly' => $select->isReadOnly(),
+ 'rowClass' => $this->getRowClass(),
+ 'stored' => true
+ );
+
+ $rowsetClass = $this->getRowsetClass();
+ if (!class_exists($rowsetClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($rowsetClass);
+ }
+ return new $rowsetClass($data);
+ }
+
+ /**
+ * Fetches one row in an object of type Zend_Db_Table_Row_Abstract,
+ * or returns null if no row matches the specified criteria.
+ *
+ * @param string|array|Zend_Db_Table_Select $where OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
+ * @param string|array $order OPTIONAL An SQL ORDER clause.
+ * @param int $offset OPTIONAL An SQL OFFSET value.
+ * @return Zend_Db_Table_Row_Abstract|null The row results per the
+ * Zend_Db_Adapter fetch mode, or null if no row found.
+ */
+ public function fetchRow($where = null, $order = null, $offset = null)
+ {
+ if (!($where instanceof Zend_Db_Table_Select)) {
+ $select = $this->select();
+
+ if ($where !== null) {
+ $this->_where($select, $where);
+ }
+
+ if ($order !== null) {
+ $this->_order($select, $order);
+ }
+
+ $select->limit(1, ((is_numeric($offset)) ? (int) $offset : null));
+
+ } else {
+ $select = $where->limit(1, $where->getPart(Zend_Db_Select::LIMIT_OFFSET));
+ }
+
+ $rows = $this->_fetch($select);
+
+ if (count($rows) == 0) {
+ return null;
+ }
+
+ $data = array(
+ 'table' => $this,
+ 'data' => $rows[0],
+ 'readOnly' => $select->isReadOnly(),
+ 'stored' => true
+ );
+
+ $rowClass = $this->getRowClass();
+ if (!class_exists($rowClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($rowClass);
+ }
+ return new $rowClass($data);
+ }
+
+ /**
+ * Fetches a new blank row (not from the database).
+ *
+ * @return Zend_Db_Table_Row_Abstract
+ * @deprecated since 0.9.3 - use createRow() instead.
+ */
+ public function fetchNew()
+ {
+ return $this->createRow();
+ }
+
+ /**
+ * Fetches a new blank row (not from the database).
+ *
+ * @param array $data OPTIONAL data to populate in the new row.
+ * @param string $defaultSource OPTIONAL flag to force default values into new row
+ * @return Zend_Db_Table_Row_Abstract
+ */
+ public function createRow(array $data = array(), $defaultSource = null)
+ {
+ $cols = $this->_getCols();
+ $defaults = array_combine($cols, array_fill(0, count($cols), null));
+
+ // nothing provided at call-time, take the class value
+ if ($defaultSource == null) {
+ $defaultSource = $this->_defaultSource;
+ }
+
+ if (!in_array($defaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE))) {
+ $defaultSource = self::DEFAULT_NONE;
+ }
+
+ if ($defaultSource == self::DEFAULT_DB) {
+ foreach ($this->_metadata as $metadataName => $metadata) {
+ if (($metadata['DEFAULT'] != null) &&
+ ($metadata['NULLABLE'] !== true || ($metadata['NULLABLE'] === true && isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === true)) &&
+ (!(isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === false))) {
+ $defaults[$metadataName] = $metadata['DEFAULT'];
+ }
+ }
+ } elseif ($defaultSource == self::DEFAULT_CLASS && $this->_defaultValues) {
+ foreach ($this->_defaultValues as $defaultName => $defaultValue) {
+ if (array_key_exists($defaultName, $defaults)) {
+ $defaults[$defaultName] = $defaultValue;
+ }
+ }
+ }
+
+ $config = array(
+ 'table' => $this,
+ 'data' => $defaults,
+ 'readOnly' => false,
+ 'stored' => false
+ );
+
+ $rowClass = $this->getRowClass();
+ if (!class_exists($rowClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($rowClass);
+ }
+ $row = new $rowClass($config);
+ $row->setFromArray($data);
+ return $row;
+ }
+
+ /**
+ * Generate WHERE clause from user-supplied string or array
+ *
+ * @param string|array $where OPTIONAL An SQL WHERE clause.
+ * @return Zend_Db_Table_Select
+ */
+ protected function _where(Zend_Db_Table_Select $select, $where)
+ {
+ $where = (array) $where;
+
+ foreach ($where as $key => $val) {
+ // is $key an int?
+ if (is_int($key)) {
+ // $val is the full condition
+ $select->where($val);
+ } else {
+ // $key is the condition with placeholder,
+ // and $val is quoted into the condition
+ $select->where($key, $val);
+ }
+ }
+
+ return $select;
+ }
+
+ /**
+ * Generate ORDER clause from user-supplied string or array
+ *
+ * @param string|array $order OPTIONAL An SQL ORDER clause.
+ * @return Zend_Db_Table_Select
+ */
+ protected function _order(Zend_Db_Table_Select $select, $order)
+ {
+ if (!is_array($order)) {
+ $order = array($order);
+ }
+
+ foreach ($order as $val) {
+ $select->order($val);
+ }
+
+ return $select;
+ }
+
+ /**
+ * Support method for fetching rows.
+ *
+ * @param Zend_Db_Table_Select $select query options.
+ * @return array An array containing the row results in FETCH_ASSOC mode.
+ */
+ protected function _fetch(Zend_Db_Table_Select $select)
+ {
+ $stmt = $this->_db->query($select);
+ $data = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
+ return $data;
+ }
+
+}
diff --git a/library/Zend/Db/Table/Definition.php b/library/Zend/Db/Table/Definition.php
new file mode 100644
index 0000000..2d4cfcf
--- /dev/null
+++ b/library/Zend/Db/Table/Definition.php
@@ -0,0 +1,131 @@
+setConfig($options);
+ } elseif (is_array($options)) {
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * setConfig()
+ *
+ * @param Zend_Config $config
+ * @return Zend_Db_Table_Definition
+ */
+ public function setConfig(Zend_Config $config)
+ {
+ $this->setOptions($config->toArray());
+ return $this;
+ }
+
+ /**
+ * setOptions()
+ *
+ * @param array $options
+ * @return Zend_Db_Table_Definition
+ */
+ public function setOptions(Array $options)
+ {
+ foreach ($options as $optionName => $optionValue) {
+ $this->setTableConfig($optionName, $optionValue);
+ }
+ return $this;
+ }
+
+ /**
+ * @param string $tableName
+ * @param array $tableConfig
+ * @return Zend_Db_Table_Definition
+ */
+ public function setTableConfig($tableName, array $tableConfig)
+ {
+ // @todo logic here
+ $tableConfig[Zend_Db_Table::DEFINITION_CONFIG_NAME] = $tableName;
+ $tableConfig[Zend_Db_Table::DEFINITION] = $this;
+
+ if (!isset($tableConfig[Zend_Db_Table::NAME])) {
+ $tableConfig[Zend_Db_Table::NAME] = $tableName;
+ }
+
+ $this->_tableConfigs[$tableName] = $tableConfig;
+ return $this;
+ }
+
+ /**
+ * getTableConfig()
+ *
+ * @param string $tableName
+ * @return array
+ */
+ public function getTableConfig($tableName)
+ {
+ return $this->_tableConfigs[$tableName];
+ }
+
+ /**
+ * removeTableConfig()
+ *
+ * @param string $tableName
+ */
+ public function removeTableConfig($tableName)
+ {
+ unset($this->_tableConfigs[$tableName]);
+ }
+
+ /**
+ * hasTableConfig()
+ *
+ * @param string $tableName
+ * @return bool
+ */
+ public function hasTableConfig($tableName)
+ {
+ return (isset($this->_tableConfigs[$tableName]));
+ }
+
+}
diff --git a/library/Zend/Db/Table/Exception.php b/library/Zend/Db/Table/Exception.php
new file mode 100644
index 0000000..32f18df
--- /dev/null
+++ b/library/Zend/Db/Table/Exception.php
@@ -0,0 +1,38 @@
+ value).
+ * The keys must match the physical names of columns in the
+ * table for which this row is defined.
+ *
+ * @var array
+ */
+ protected $_data = array();
+
+ /**
+ * This is set to a copy of $_data when the data is fetched from
+ * a database, specified as a new tuple in the constructor, or
+ * when dirty data is posted to the database with save().
+ *
+ * @var array
+ */
+ protected $_cleanData = array();
+
+ /**
+ * Tracks columns where data has been updated. Allows more specific insert and
+ * update operations.
+ *
+ * @var array
+ */
+ protected $_modifiedFields = array();
+
+ /**
+ * Zend_Db_Table_Abstract parent class or instance.
+ *
+ * @var Zend_Db_Table_Abstract
+ */
+ protected $_table = null;
+
+ /**
+ * Connected is true if we have a reference to a live
+ * Zend_Db_Table_Abstract object.
+ * This is false after the Rowset has been deserialized.
+ *
+ * @var boolean
+ */
+ protected $_connected = true;
+
+ /**
+ * A row is marked read only if it contains columns that are not physically represented within
+ * the database schema (e.g. evaluated columns/Zend_Db_Expr columns). This can also be passed
+ * as a run-time config options as a means of protecting row data.
+ *
+ * @var boolean
+ */
+ protected $_readOnly = false;
+
+ /**
+ * Name of the class of the Zend_Db_Table_Abstract object.
+ *
+ * @var string
+ */
+ protected $_tableClass = null;
+
+ /**
+ * Primary row key(s).
+ *
+ * @var array
+ */
+ protected $_primary;
+
+ /**
+ * Constructor.
+ *
+ * Supported params for $config are:-
+ * - table = class name or object of type Zend_Db_Table_Abstract
+ * - data = values of columns in this row.
+ *
+ * @param array $config OPTIONAL Array of user-specified config options.
+ * @return void
+ * @throws Zend_Db_Table_Row_Exception
+ */
+ public function __construct(array $config = array())
+ {
+ if (isset($config['table']) && $config['table'] instanceof Zend_Db_Table_Abstract) {
+ $this->_table = $config['table'];
+ $this->_tableClass = get_class($this->_table);
+ } elseif ($this->_tableClass !== null) {
+ $this->_table = $this->_getTableFromString($this->_tableClass);
+ }
+
+ if (isset($config['data'])) {
+ if (!is_array($config['data'])) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('Data must be an array');
+ }
+ $this->_data = $config['data'];
+ }
+ if (isset($config['stored']) && $config['stored'] === true) {
+ $this->_cleanData = $this->_data;
+ }
+
+ if (isset($config['readOnly']) && $config['readOnly'] === true) {
+ $this->setReadOnly(true);
+ }
+
+ // Retrieve primary keys from table schema
+ if (($table = $this->_getTable())) {
+ $info = $table->info();
+ $this->_primary = (array) $info['primary'];
+ }
+
+ $this->init();
+ }
+
+ /**
+ * Transform a column name from the user-specified form
+ * to the physical form used in the database.
+ * You can override this method in a custom Row class
+ * to implement column name mappings, for example inflection.
+ *
+ * @param string $columnName Column name given.
+ * @return string The column name after transformation applied (none by default).
+ * @throws Zend_Db_Table_Row_Exception if the $columnName is not a string.
+ */
+ protected function _transformColumn($columnName)
+ {
+ if (!is_string($columnName)) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('Specified column is not a string');
+ }
+ // Perform no transformation by default
+ return $columnName;
+ }
+
+ /**
+ * Retrieve row field value
+ *
+ * @param string $columnName The user-specified column name.
+ * @return string The corresponding column value.
+ * @throws Zend_Db_Table_Row_Exception if the $columnName is not a column in the row.
+ */
+ public function __get($columnName)
+ {
+ $columnName = $this->_transformColumn($columnName);
+ if (!array_key_exists($columnName, $this->_data)) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
+ }
+ return $this->_data[$columnName];
+ }
+
+ /**
+ * Set row field value
+ *
+ * @param string $columnName The column key.
+ * @param mixed $value The value for the property.
+ * @return void
+ * @throws Zend_Db_Table_Row_Exception
+ */
+ public function __set($columnName, $value)
+ {
+ $columnName = $this->_transformColumn($columnName);
+ if (!array_key_exists($columnName, $this->_data)) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
+ }
+ $this->_data[$columnName] = $value;
+ $this->_modifiedFields[$columnName] = true;
+ }
+
+ /**
+ * Unset row field value
+ *
+ * @param string $columnName The column key.
+ * @return Zend_Db_Table_Row_Abstract
+ * @throws Zend_Db_Table_Row_Exception
+ */
+ public function __unset($columnName)
+ {
+ $columnName = $this->_transformColumn($columnName);
+ if (!array_key_exists($columnName, $this->_data)) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
+ }
+ if ($this->isConnected() && in_array($columnName, $this->_table->info('primary'))) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is a primary key and should not be unset");
+ }
+ unset($this->_data[$columnName]);
+ return $this;
+ }
+
+ /**
+ * Test existence of row field
+ *
+ * @param string $columnName The column key.
+ * @return boolean
+ */
+ public function __isset($columnName)
+ {
+ $columnName = $this->_transformColumn($columnName);
+ return array_key_exists($columnName, $this->_data);
+ }
+
+ /**
+ * Store table, primary key and data in serialized object
+ *
+ * @return array
+ */
+ public function __sleep()
+ {
+ return array('_tableClass', '_primary', '_data', '_cleanData', '_readOnly' ,'_modifiedFields');
+ }
+
+ /**
+ * Setup to do on wakeup.
+ * A de-serialized Row should not be assumed to have access to a live
+ * database connection, so set _connected = false.
+ *
+ * @return void
+ */
+ public function __wakeup()
+ {
+ $this->_connected = false;
+ }
+
+ /**
+ * Proxy to __isset
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ * @return boolean
+ */
+ public function offsetExists($offset)
+ {
+ return $this->__isset($offset);
+ }
+
+ /**
+ * Proxy to __get
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ * @return string
+ */
+ public function offsetGet($offset)
+ {
+ return $this->__get($offset);
+ }
+
+ /**
+ * Proxy to __set
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ * @param mixed $value
+ */
+ public function offsetSet($offset, $value)
+ {
+ $this->__set($offset, $value);
+ }
+
+ /**
+ * Proxy to __unset
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ */
+ public function offsetUnset($offset)
+ {
+ return $this->__unset($offset);
+ }
+
+ /**
+ * Initialize object
+ *
+ * Called from {@link __construct()} as final step of object instantiation.
+ *
+ * @return void
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * Returns the table object, or null if this is disconnected row
+ *
+ * @return Zend_Db_Table_Abstract|null
+ */
+ public function getTable()
+ {
+ return $this->_table;
+ }
+
+ /**
+ * Set the table object, to re-establish a live connection
+ * to the database for a Row that has been de-serialized.
+ *
+ * @param Zend_Db_Table_Abstract $table
+ * @return boolean
+ * @throws Zend_Db_Table_Row_Exception
+ */
+ public function setTable(Zend_Db_Table_Abstract $table = null)
+ {
+ if ($table == null) {
+ $this->_table = null;
+ $this->_connected = false;
+ return false;
+ }
+
+ $tableClass = get_class($table);
+ if (! $table instanceof $this->_tableClass) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("The specified Table is of class $tableClass, expecting class to be instance of $this->_tableClass");
+ }
+
+ $this->_table = $table;
+ $this->_tableClass = $tableClass;
+
+ $info = $this->_table->info();
+
+ if ($info['cols'] != array_keys($this->_data)) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('The specified Table does not have the same columns as the Row');
+ }
+
+ if (! array_intersect((array) $this->_primary, $info['primary']) == (array) $this->_primary) {
+
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("The specified Table '$tableClass' does not have the same primary key as the Row");
+ }
+
+ $this->_connected = true;
+ return true;
+ }
+
+ /**
+ * Query the class name of the Table object for which this
+ * Row was created.
+ *
+ * @return string
+ */
+ public function getTableClass()
+ {
+ return $this->_tableClass;
+ }
+
+ /**
+ * Test the connected status of the row.
+ *
+ * @return boolean
+ */
+ public function isConnected()
+ {
+ return $this->_connected;
+ }
+
+ /**
+ * Test the read-only status of the row.
+ *
+ * @return boolean
+ */
+ public function isReadOnly()
+ {
+ return $this->_readOnly;
+ }
+
+ /**
+ * Set the read-only status of the row.
+ *
+ * @param boolean $flag
+ * @return boolean
+ */
+ public function setReadOnly($flag)
+ {
+ $this->_readOnly = (bool) $flag;
+ }
+
+ /**
+ * Returns an instance of the parent table's Zend_Db_Table_Select object.
+ *
+ * @return Zend_Db_Table_Select
+ */
+ public function select()
+ {
+ return $this->getTable()->select();
+ }
+
+ /**
+ * Saves the properties to the database.
+ *
+ * This performs an intelligent insert/update, and reloads the
+ * properties with fresh data from the table on success.
+ *
+ * @return mixed The primary key value(s), as an associative array if the
+ * key is compound, or a scalar if the key is single-column.
+ */
+ public function save()
+ {
+ /**
+ * If the _cleanData array is empty,
+ * this is an INSERT of a new row.
+ * Otherwise it is an UPDATE.
+ */
+ if (empty($this->_cleanData)) {
+ return $this->_doInsert();
+ } else {
+ return $this->_doUpdate();
+ }
+ }
+
+ /**
+ * @return mixed The primary key value(s), as an associative array if the
+ * key is compound, or a scalar if the key is single-column.
+ */
+ protected function _doInsert()
+ {
+ /**
+ * A read-only row cannot be saved.
+ */
+ if ($this->_readOnly === true) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('This row has been marked read-only');
+ }
+
+ /**
+ * Run pre-INSERT logic
+ */
+ $this->_insert();
+
+ /**
+ * Execute the INSERT (this may throw an exception)
+ */
+ $data = array_intersect_key($this->_data, $this->_modifiedFields);
+ $primaryKey = $this->_getTable()->insert($data);
+
+ /**
+ * Normalize the result to an array indexed by primary key column(s).
+ * The table insert() method may return a scalar.
+ */
+ if (is_array($primaryKey)) {
+ $newPrimaryKey = $primaryKey;
+ } else {
+ //ZF-6167 Use tempPrimaryKey temporary to avoid that zend encoding fails.
+ $tempPrimaryKey = (array) $this->_primary;
+ $newPrimaryKey = array(current($tempPrimaryKey) => $primaryKey);
+ }
+
+ /**
+ * Save the new primary key value in _data. The primary key may have
+ * been generated by a sequence or auto-increment mechanism, and this
+ * merge should be done before the _postInsert() method is run, so the
+ * new values are available for logging, etc.
+ */
+ $this->_data = array_merge($this->_data, $newPrimaryKey);
+
+ /**
+ * Run post-INSERT logic
+ */
+ $this->_postInsert();
+
+ /**
+ * Update the _cleanData to reflect that the data has been inserted.
+ */
+ $this->_refresh();
+
+ return $primaryKey;
+ }
+
+ /**
+ * @return mixed The primary key value(s), as an associative array if the
+ * key is compound, or a scalar if the key is single-column.
+ */
+ protected function _doUpdate()
+ {
+ /**
+ * A read-only row cannot be saved.
+ */
+ if ($this->_readOnly === true) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('This row has been marked read-only');
+ }
+
+ /**
+ * Get expressions for a WHERE clause
+ * based on the primary key value(s).
+ */
+ $where = $this->_getWhereQuery(false);
+
+ /**
+ * Run pre-UPDATE logic
+ */
+ $this->_update();
+
+ /**
+ * Compare the data to the modified fields array to discover
+ * which columns have been changed.
+ */
+ $diffData = array_intersect_key($this->_data, $this->_modifiedFields);
+
+ /**
+ * Were any of the changed columns part of the primary key?
+ */
+ $pkDiffData = array_intersect_key($diffData, array_flip((array)$this->_primary));
+
+ /**
+ * Execute cascading updates against dependent tables.
+ * Do this only if primary key value(s) were changed.
+ */
+ if (count($pkDiffData) > 0) {
+ $depTables = $this->_getTable()->getDependentTables();
+ if (!empty($depTables)) {
+ $pkNew = $this->_getPrimaryKey(true);
+ $pkOld = $this->_getPrimaryKey(false);
+ foreach ($depTables as $tableClass) {
+ $t = $this->_getTableFromString($tableClass);
+ $t->_cascadeUpdate($this->getTableClass(), $pkOld, $pkNew);
+ }
+ }
+ }
+
+ /**
+ * Execute the UPDATE (this may throw an exception)
+ * Do this only if data values were changed.
+ * Use the $diffData variable, so the UPDATE statement
+ * includes SET terms only for data values that changed.
+ */
+ if (count($diffData) > 0) {
+ $this->_getTable()->update($diffData, $where);
+ }
+
+ /**
+ * Run post-UPDATE logic. Do this before the _refresh()
+ * so the _postUpdate() function can tell the difference
+ * between changed data and clean (pre-changed) data.
+ */
+ $this->_postUpdate();
+
+ /**
+ * Refresh the data just in case triggers in the RDBMS changed
+ * any columns. Also this resets the _cleanData.
+ */
+ $this->_refresh();
+
+ /**
+ * Return the primary key value(s) as an array
+ * if the key is compound or a scalar if the key
+ * is a scalar.
+ */
+ $primaryKey = $this->_getPrimaryKey(true);
+ if (count($primaryKey) == 1) {
+ return current($primaryKey);
+ }
+
+ return $primaryKey;
+ }
+
+ /**
+ * Deletes existing rows.
+ *
+ * @return int The number of rows deleted.
+ */
+ public function delete()
+ {
+ /**
+ * A read-only row cannot be deleted.
+ */
+ if ($this->_readOnly === true) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('This row has been marked read-only');
+ }
+
+ $where = $this->_getWhereQuery();
+
+ /**
+ * Execute pre-DELETE logic
+ */
+ $this->_delete();
+
+ /**
+ * Execute cascading deletes against dependent tables
+ */
+ $depTables = $this->_getTable()->getDependentTables();
+ if (!empty($depTables)) {
+ $pk = $this->_getPrimaryKey();
+ foreach ($depTables as $tableClass) {
+ $t = $this->_getTableFromString($tableClass);
+ $t->_cascadeDelete($this->getTableClass(), $pk);
+ }
+ }
+
+ /**
+ * Execute the DELETE (this may throw an exception)
+ */
+ $result = $this->_getTable()->delete($where);
+
+ /**
+ * Execute post-DELETE logic
+ */
+ $this->_postDelete();
+
+ /**
+ * Reset all fields to null to indicate that the row is not there
+ */
+ $this->_data = array_combine(
+ array_keys($this->_data),
+ array_fill(0, count($this->_data), null)
+ );
+
+ return $result;
+ }
+
+ public function getIterator()
+ {
+ return new ArrayIterator((array) $this->_data);
+ }
+
+ /**
+ * Returns the column/value data as an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return (array)$this->_data;
+ }
+
+ /**
+ * Sets all data in the row from an array.
+ *
+ * @param array $data
+ * @return Zend_Db_Table_Row_Abstract Provides a fluent interface
+ */
+ public function setFromArray(array $data)
+ {
+ $data = array_intersect_key($data, $this->_data);
+
+ foreach ($data as $columnName => $value) {
+ $this->__set($columnName, $value);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Refreshes properties from the database.
+ *
+ * @return void
+ */
+ public function refresh()
+ {
+ return $this->_refresh();
+ }
+
+ /**
+ * Retrieves an instance of the parent table.
+ *
+ * @return Zend_Db_Table_Abstract
+ */
+ protected function _getTable()
+ {
+ if (!$this->_connected) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('Cannot save a Row unless it is connected');
+ }
+ return $this->_table;
+ }
+
+ /**
+ * Retrieves an associative array of primary keys.
+ *
+ * @param bool $useDirty
+ * @return array
+ */
+ protected function _getPrimaryKey($useDirty = true)
+ {
+ if (!is_array($this->_primary)) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("The primary key must be set as an array");
+ }
+
+ $primary = array_flip($this->_primary);
+ if ($useDirty) {
+ $array = array_intersect_key($this->_data, $primary);
+ } else {
+ $array = array_intersect_key($this->_cleanData, $primary);
+ }
+ if (count($primary) != count($array)) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("The specified Table '$this->_tableClass' does not have the same primary key as the Row");
+ }
+ return $array;
+ }
+
+ /**
+ * Constructs where statement for retrieving row(s).
+ *
+ * @param bool $useDirty
+ * @return array
+ */
+ protected function _getWhereQuery($useDirty = true)
+ {
+ $where = array();
+ $db = $this->_getTable()->getAdapter();
+ $primaryKey = $this->_getPrimaryKey($useDirty);
+ $info = $this->_getTable()->info();
+ $metadata = $info[Zend_Db_Table_Abstract::METADATA];
+
+ // retrieve recently updated row using primary keys
+ $where = array();
+ foreach ($primaryKey as $column => $value) {
+ $tableName = $db->quoteIdentifier($info[Zend_Db_Table_Abstract::NAME], true);
+ $type = $metadata[$column]['DATA_TYPE'];
+ $columnName = $db->quoteIdentifier($column, true);
+ $where[] = $db->quoteInto("{$tableName}.{$columnName} = ?", $value, $type);
+ }
+ return $where;
+ }
+
+ /**
+ * Refreshes properties from the database.
+ *
+ * @return void
+ */
+ protected function _refresh()
+ {
+ $where = $this->_getWhereQuery();
+ $row = $this->_getTable()->fetchRow($where);
+
+ if (null === $row) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception('Cannot refresh row as parent is missing');
+ }
+
+ $this->_data = $row->toArray();
+ $this->_cleanData = $this->_data;
+ $this->_modifiedFields = array();
+ }
+
+ /**
+ * Allows pre-insert logic to be applied to row.
+ * Subclasses may override this method.
+ *
+ * @return void
+ */
+ protected function _insert()
+ {
+ }
+
+ /**
+ * Allows post-insert logic to be applied to row.
+ * Subclasses may override this method.
+ *
+ * @return void
+ */
+ protected function _postInsert()
+ {
+ }
+
+ /**
+ * Allows pre-update logic to be applied to row.
+ * Subclasses may override this method.
+ *
+ * @return void
+ */
+ protected function _update()
+ {
+ }
+
+ /**
+ * Allows post-update logic to be applied to row.
+ * Subclasses may override this method.
+ *
+ * @return void
+ */
+ protected function _postUpdate()
+ {
+ }
+
+ /**
+ * Allows pre-delete logic to be applied to row.
+ * Subclasses may override this method.
+ *
+ * @return void
+ */
+ protected function _delete()
+ {
+ }
+
+ /**
+ * Allows post-delete logic to be applied to row.
+ * Subclasses may override this method.
+ *
+ * @return void
+ */
+ protected function _postDelete()
+ {
+ }
+
+ /**
+ * Prepares a table reference for lookup.
+ *
+ * Ensures all reference keys are set and properly formatted.
+ *
+ * @param Zend_Db_Table_Abstract $dependentTable
+ * @param Zend_Db_Table_Abstract $parentTable
+ * @param string $ruleKey
+ * @return array
+ */
+ protected function _prepareReference(Zend_Db_Table_Abstract $dependentTable, Zend_Db_Table_Abstract $parentTable, $ruleKey)
+ {
+ $parentTableName = (get_class($parentTable) === 'Zend_Db_Table') ? $parentTable->getDefinitionConfigName() : get_class($parentTable);
+ $map = $dependentTable->getReference($parentTableName, $ruleKey);
+
+ if (!isset($map[Zend_Db_Table_Abstract::REF_COLUMNS])) {
+ $parentInfo = $parentTable->info();
+ $map[Zend_Db_Table_Abstract::REF_COLUMNS] = array_values((array) $parentInfo['primary']);
+ }
+
+ $map[Zend_Db_Table_Abstract::COLUMNS] = (array) $map[Zend_Db_Table_Abstract::COLUMNS];
+ $map[Zend_Db_Table_Abstract::REF_COLUMNS] = (array) $map[Zend_Db_Table_Abstract::REF_COLUMNS];
+
+ return $map;
+ }
+
+ /**
+ * Query a dependent table to retrieve rows matching the current row.
+ *
+ * @param string|Zend_Db_Table_Abstract $dependentTable
+ * @param string OPTIONAL $ruleKey
+ * @param Zend_Db_Table_Select OPTIONAL $select
+ * @return Zend_Db_Table_Rowset_Abstract Query result from $dependentTable
+ * @throws Zend_Db_Table_Row_Exception If $dependentTable is not a table or is not loadable.
+ */
+ public function findDependentRowset($dependentTable, $ruleKey = null, Zend_Db_Table_Select $select = null)
+ {
+ $db = $this->_getTable()->getAdapter();
+
+ if (is_string($dependentTable)) {
+ $dependentTable = $this->_getTableFromString($dependentTable);
+ }
+
+ if (!$dependentTable instanceof Zend_Db_Table_Abstract) {
+ $type = gettype($dependentTable);
+ if ($type == 'object') {
+ $type = get_class($dependentTable);
+ }
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Dependent table must be a Zend_Db_Table_Abstract, but it is $type");
+ }
+
+ // even if we are interacting between a table defined in a class and a
+ // table via extension, ensure to persist the definition
+ if (($tableDefinition = $this->_table->getDefinition()) !== null
+ && ($dependentTable->getDefinition() == null)) {
+ $dependentTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
+ }
+
+ if ($select === null) {
+ $select = $dependentTable->select();
+ } else {
+ $select->setTable($dependentTable);
+ }
+
+ $map = $this->_prepareReference($dependentTable, $this->_getTable(), $ruleKey);
+
+ for ($i = 0; $i < count($map[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
+ $parentColumnName = $db->foldCase($map[Zend_Db_Table_Abstract::REF_COLUMNS][$i]);
+ $value = $this->_data[$parentColumnName];
+ // Use adapter from dependent table to ensure correct query construction
+ $dependentDb = $dependentTable->getAdapter();
+ $dependentColumnName = $dependentDb->foldCase($map[Zend_Db_Table_Abstract::COLUMNS][$i]);
+ $dependentColumn = $dependentDb->quoteIdentifier($dependentColumnName, true);
+ $dependentInfo = $dependentTable->info();
+ $type = $dependentInfo[Zend_Db_Table_Abstract::METADATA][$dependentColumnName]['DATA_TYPE'];
+ $select->where("$dependentColumn = ?", $value, $type);
+ }
+
+ return $dependentTable->fetchAll($select);
+ }
+
+ /**
+ * Query a parent table to retrieve the single row matching the current row.
+ *
+ * @param string|Zend_Db_Table_Abstract $parentTable
+ * @param string OPTIONAL $ruleKey
+ * @param Zend_Db_Table_Select OPTIONAL $select
+ * @return Zend_Db_Table_Row_Abstract Query result from $parentTable
+ * @throws Zend_Db_Table_Row_Exception If $parentTable is not a table or is not loadable.
+ */
+ public function findParentRow($parentTable, $ruleKey = null, Zend_Db_Table_Select $select = null)
+ {
+ $db = $this->_getTable()->getAdapter();
+
+ if (is_string($parentTable)) {
+ $parentTable = $this->_getTableFromString($parentTable);
+ }
+
+ if (!$parentTable instanceof Zend_Db_Table_Abstract) {
+ $type = gettype($parentTable);
+ if ($type == 'object') {
+ $type = get_class($parentTable);
+ }
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Parent table must be a Zend_Db_Table_Abstract, but it is $type");
+ }
+
+ // even if we are interacting between a table defined in a class and a
+ // table via extension, ensure to persist the definition
+ if (($tableDefinition = $this->_table->getDefinition()) !== null
+ && ($parentTable->getDefinition() == null)) {
+ $parentTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
+ }
+
+ if ($select === null) {
+ $select = $parentTable->select();
+ } else {
+ $select->setTable($parentTable);
+ }
+
+ $map = $this->_prepareReference($this->_getTable(), $parentTable, $ruleKey);
+
+ // iterate the map, creating the proper wheres
+ for ($i = 0; $i < count($map[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
+ $dependentColumnName = $db->foldCase($map[Zend_Db_Table_Abstract::COLUMNS][$i]);
+ $value = $this->_data[$dependentColumnName];
+ // Use adapter from parent table to ensure correct query construction
+ $parentDb = $parentTable->getAdapter();
+ $parentColumnName = $parentDb->foldCase($map[Zend_Db_Table_Abstract::REF_COLUMNS][$i]);
+ $parentColumn = $parentDb->quoteIdentifier($parentColumnName, true);
+ $parentInfo = $parentTable->info();
+
+ // determine where part
+ $type = $parentInfo[Zend_Db_Table_Abstract::METADATA][$parentColumnName]['DATA_TYPE'];
+ $nullable = $parentInfo[Zend_Db_Table_Abstract::METADATA][$parentColumnName]['NULLABLE'];
+ if ($value === null && $nullable == true) {
+ $select->where("$parentColumn IS NULL");
+ } elseif ($value === null && $nullable == false) {
+ return null;
+ } else {
+ $select->where("$parentColumn = ?", $value, $type);
+ }
+
+ }
+
+ return $parentTable->fetchRow($select);
+ }
+
+ /**
+ * @param string|Zend_Db_Table_Abstract $matchTable
+ * @param string|Zend_Db_Table_Abstract $intersectionTable
+ * @param string OPTIONAL $callerRefRule
+ * @param string OPTIONAL $matchRefRule
+ * @param Zend_Db_Table_Select OPTIONAL $select
+ * @return Zend_Db_Table_Rowset_Abstract Query result from $matchTable
+ * @throws Zend_Db_Table_Row_Exception If $matchTable or $intersectionTable is not a table class or is not loadable.
+ */
+ public function findManyToManyRowset($matchTable, $intersectionTable, $callerRefRule = null,
+ $matchRefRule = null, Zend_Db_Table_Select $select = null)
+ {
+ $db = $this->_getTable()->getAdapter();
+
+ if (is_string($intersectionTable)) {
+ $intersectionTable = $this->_getTableFromString($intersectionTable);
+ }
+
+ if (!$intersectionTable instanceof Zend_Db_Table_Abstract) {
+ $type = gettype($intersectionTable);
+ if ($type == 'object') {
+ $type = get_class($intersectionTable);
+ }
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Intersection table must be a Zend_Db_Table_Abstract, but it is $type");
+ }
+
+ // even if we are interacting between a table defined in a class and a
+ // table via extension, ensure to persist the definition
+ if (($tableDefinition = $this->_table->getDefinition()) !== null
+ && ($intersectionTable->getDefinition() == null)) {
+ $intersectionTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
+ }
+
+ if (is_string($matchTable)) {
+ $matchTable = $this->_getTableFromString($matchTable);
+ }
+
+ if (! $matchTable instanceof Zend_Db_Table_Abstract) {
+ $type = gettype($matchTable);
+ if ($type == 'object') {
+ $type = get_class($matchTable);
+ }
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Match table must be a Zend_Db_Table_Abstract, but it is $type");
+ }
+
+ // even if we are interacting between a table defined in a class and a
+ // table via extension, ensure to persist the definition
+ if (($tableDefinition = $this->_table->getDefinition()) !== null
+ && ($matchTable->getDefinition() == null)) {
+ $matchTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
+ }
+
+ if ($select === null) {
+ $select = $matchTable->select();
+ } else {
+ $select->setTable($matchTable);
+ }
+
+ // Use adapter from intersection table to ensure correct query construction
+ $interInfo = $intersectionTable->info();
+ $interDb = $intersectionTable->getAdapter();
+ $interName = $interInfo['name'];
+ $interSchema = isset($interInfo['schema']) ? $interInfo['schema'] : null;
+ $matchInfo = $matchTable->info();
+ $matchName = $matchInfo['name'];
+ $matchSchema = isset($matchInfo['schema']) ? $matchInfo['schema'] : null;
+
+ $matchMap = $this->_prepareReference($intersectionTable, $matchTable, $matchRefRule);
+
+ for ($i = 0; $i < count($matchMap[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
+ $interCol = $interDb->quoteIdentifier('i' . '.' . $matchMap[Zend_Db_Table_Abstract::COLUMNS][$i], true);
+ $matchCol = $interDb->quoteIdentifier('m' . '.' . $matchMap[Zend_Db_Table_Abstract::REF_COLUMNS][$i], true);
+ $joinCond[] = "$interCol = $matchCol";
+ }
+ $joinCond = implode(' AND ', $joinCond);
+
+ $select->from(array('i' => $interName), array(), $interSchema)
+ ->joinInner(array('m' => $matchName), $joinCond, Zend_Db_Select::SQL_WILDCARD, $matchSchema)
+ ->setIntegrityCheck(false);
+
+ $callerMap = $this->_prepareReference($intersectionTable, $this->_getTable(), $callerRefRule);
+
+ for ($i = 0; $i < count($callerMap[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
+ $callerColumnName = $db->foldCase($callerMap[Zend_Db_Table_Abstract::REF_COLUMNS][$i]);
+ $value = $this->_data[$callerColumnName];
+ $interColumnName = $interDb->foldCase($callerMap[Zend_Db_Table_Abstract::COLUMNS][$i]);
+ $interCol = $interDb->quoteIdentifier("i.$interColumnName", true);
+ $interInfo = $intersectionTable->info();
+ $type = $interInfo[Zend_Db_Table_Abstract::METADATA][$interColumnName]['DATA_TYPE'];
+ $select->where($interDb->quoteInto("$interCol = ?", $value, $type));
+ }
+
+ $stmt = $select->query();
+
+ $config = array(
+ 'table' => $matchTable,
+ 'data' => $stmt->fetchAll(Zend_Db::FETCH_ASSOC),
+ 'rowClass' => $matchTable->getRowClass(),
+ 'readOnly' => false,
+ 'stored' => true
+ );
+
+ $rowsetClass = $matchTable->getRowsetClass();
+ if (!class_exists($rowsetClass)) {
+ try {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($rowsetClass);
+ } catch (Zend_Exception $e) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ $rowset = new $rowsetClass($config);
+ return $rowset;
+ }
+
+ /**
+ * Turn magic function calls into non-magic function calls
+ * to the above methods.
+ *
+ * @param string $method
+ * @param array $args OPTIONAL Zend_Db_Table_Select query modifier
+ * @return Zend_Db_Table_Row_Abstract|Zend_Db_Table_Rowset_Abstract
+ * @throws Zend_Db_Table_Row_Exception If an invalid method is called.
+ */
+ public function __call($method, array $args)
+ {
+ $matches = array();
+
+ if (count($args) && $args[0] instanceof Zend_Db_Table_Select) {
+ $select = $args[0];
+ } else {
+ $select = null;
+ }
+
+ /**
+ * Recognize methods for Has-Many cases:
+ * findParent()
+ * findParentBy()
+ * Use the non-greedy pattern repeat modifier e.g. \w+?
+ */
+ if (preg_match('/^findParent(\w+?)(?:By(\w+))?$/', $method, $matches)) {
+ $class = $matches[1];
+ $ruleKey1 = isset($matches[2]) ? $matches[2] : null;
+ return $this->findParentRow($class, $ruleKey1, $select);
+ }
+
+ /**
+ * Recognize methods for Many-to-Many cases:
+ * findVia()
+ * findViaBy()
+ * findViaByAnd()
+ * Use the non-greedy pattern repeat modifier e.g. \w+?
+ */
+ if (preg_match('/^find(\w+?)Via(\w+?)(?:By(\w+?)(?:And(\w+))?)?$/', $method, $matches)) {
+ $class = $matches[1];
+ $viaClass = $matches[2];
+ $ruleKey1 = isset($matches[3]) ? $matches[3] : null;
+ $ruleKey2 = isset($matches[4]) ? $matches[4] : null;
+ return $this->findManyToManyRowset($class, $viaClass, $ruleKey1, $ruleKey2, $select);
+ }
+
+ /**
+ * Recognize methods for Belongs-To cases:
+ * find()
+ * findBy()
+ * Use the non-greedy pattern repeat modifier e.g. \w+?
+ */
+ if (preg_match('/^find(\w+?)(?:By(\w+))?$/', $method, $matches)) {
+ $class = $matches[1];
+ $ruleKey1 = isset($matches[2]) ? $matches[2] : null;
+ return $this->findDependentRowset($class, $ruleKey1, $select);
+ }
+
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception("Unrecognized method '$method()'");
+ }
+
+
+ /**
+ * _getTableFromString
+ *
+ * @param string $tableName
+ * @return Zend_Db_Table_Abstract
+ */
+ protected function _getTableFromString($tableName)
+ {
+
+ if ($this->_table instanceof Zend_Db_Table_Abstract) {
+ $tableDefinition = $this->_table->getDefinition();
+
+ if ($tableDefinition !== null && $tableDefinition->hasTableConfig($tableName)) {
+ return new Zend_Db_Table($tableName, $tableDefinition);
+ }
+ }
+
+ // assume the tableName is the class name
+ if (!class_exists($tableName)) {
+ try {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($tableName);
+ } catch (Zend_Exception $e) {
+ require_once 'Zend/Db/Table/Row/Exception.php';
+ throw new Zend_Db_Table_Row_Exception($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ $options = array();
+
+ if (($table = $this->_getTable())) {
+ $options['db'] = $table->getAdapter();
+ }
+
+ if (isset($tableDefinition) && $tableDefinition !== null) {
+ $options[Zend_Db_Table_Abstract::DEFINITION] = $tableDefinition;
+ }
+
+ return new $tableName($options);
+ }
+
+}
diff --git a/library/Zend/Db/Table/Row/Exception.php b/library/Zend/Db/Table/Row/Exception.php
new file mode 100644
index 0000000..df82c08
--- /dev/null
+++ b/library/Zend/Db/Table/Row/Exception.php
@@ -0,0 +1,38 @@
+_table = $config['table'];
+ $this->_tableClass = get_class($this->_table);
+ }
+ if (isset($config['rowClass'])) {
+ $this->_rowClass = $config['rowClass'];
+ }
+ if (!class_exists($this->_rowClass)) {
+ require_once 'Zend/Loader.php';
+ Zend_Loader::loadClass($this->_rowClass);
+ }
+ if (isset($config['data'])) {
+ $this->_data = $config['data'];
+ }
+ if (isset($config['readOnly'])) {
+ $this->_readOnly = $config['readOnly'];
+ }
+ if (isset($config['stored'])) {
+ $this->_stored = $config['stored'];
+ }
+
+ // set the count of rows
+ $this->_count = count($this->_data);
+
+ $this->init();
+ }
+
+ /**
+ * Store data, class names, and state in serialized object
+ *
+ * @return array
+ */
+ public function __sleep()
+ {
+ return array('_data', '_tableClass', '_rowClass', '_pointer', '_count', '_rows', '_stored',
+ '_readOnly');
+ }
+
+ /**
+ * Setup to do on wakeup.
+ * A de-serialized Rowset should not be assumed to have access to a live
+ * database connection, so set _connected = false.
+ *
+ * @return void
+ */
+ public function __wakeup()
+ {
+ $this->_connected = false;
+ }
+
+ /**
+ * Initialize object
+ *
+ * Called from {@link __construct()} as final step of object instantiation.
+ *
+ * @return void
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * Return the connected state of the rowset.
+ *
+ * @return boolean
+ */
+ public function isConnected()
+ {
+ return $this->_connected;
+ }
+
+ /**
+ * Returns the table object, or null if this is disconnected rowset
+ *
+ * @return Zend_Db_Table_Abstract
+ */
+ public function getTable()
+ {
+ return $this->_table;
+ }
+
+ /**
+ * Set the table object, to re-establish a live connection
+ * to the database for a Rowset that has been de-serialized.
+ *
+ * @param Zend_Db_Table_Abstract $table
+ * @return boolean
+ * @throws Zend_Db_Table_Row_Exception
+ */
+ public function setTable(Zend_Db_Table_Abstract $table)
+ {
+ $this->_table = $table;
+ $this->_connected = false;
+ // @todo This works only if we have iterated through
+ // the result set once to instantiate the rows.
+ foreach ($this as $row) {
+ $connected = $row->setTable($table);
+ if ($connected == true) {
+ $this->_connected = true;
+ }
+ }
+ return $this->_connected;
+ }
+
+ /**
+ * Query the class name of the Table object for which this
+ * Rowset was created.
+ *
+ * @return string
+ */
+ public function getTableClass()
+ {
+ return $this->_tableClass;
+ }
+
+ /**
+ * Rewind the Iterator to the first element.
+ * Similar to the reset() function for arrays in PHP.
+ * Required by interface Iterator.
+ *
+ * @return Zend_Db_Table_Rowset_Abstract Fluent interface.
+ */
+ public function rewind()
+ {
+ $this->_pointer = 0;
+ return $this;
+ }
+
+ /**
+ * Return the current element.
+ * Similar to the current() function for arrays in PHP
+ * Required by interface Iterator.
+ *
+ * @return Zend_Db_Table_Row_Abstract current element from the collection
+ */
+ public function current()
+ {
+ if ($this->valid() === false) {
+ return null;
+ }
+
+ // return the row object
+ return $this->_loadAndReturnRow($this->_pointer);
+ }
+
+ /**
+ * Return the identifying key of the current element.
+ * Similar to the key() function for arrays in PHP.
+ * Required by interface Iterator.
+ *
+ * @return int
+ */
+ public function key()
+ {
+ return $this->_pointer;
+ }
+
+ /**
+ * Move forward to next element.
+ * Similar to the next() function for arrays in PHP.
+ * Required by interface Iterator.
+ *
+ * @return void
+ */
+ public function next()
+ {
+ ++$this->_pointer;
+ }
+
+ /**
+ * Check if there is a current element after calls to rewind() or next().
+ * Used to check if we've iterated to the end of the collection.
+ * Required by interface Iterator.
+ *
+ * @return bool False if there's nothing more to iterate over
+ */
+ public function valid()
+ {
+ return $this->_pointer >= 0 && $this->_pointer < $this->_count;
+ }
+
+ /**
+ * Returns the number of elements in the collection.
+ *
+ * Implements Countable::count()
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->_count;
+ }
+
+ /**
+ * Take the Iterator to position $position
+ * Required by interface SeekableIterator.
+ *
+ * @param int $position the position to seek to
+ * @return Zend_Db_Table_Rowset_Abstract
+ * @throws Zend_Db_Table_Rowset_Exception
+ */
+ public function seek($position)
+ {
+ $position = (int) $position;
+ if ($position < 0 || $position >= $this->_count) {
+ require_once 'Zend/Db/Table/Rowset/Exception.php';
+ throw new Zend_Db_Table_Rowset_Exception("Illegal index $position");
+ }
+ $this->_pointer = $position;
+ return $this;
+ }
+
+ /**
+ * Check if an offset exists
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ * @return boolean
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->_data[(int) $offset]);
+ }
+
+ /**
+ * Get the row for the given offset
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ * @return Zend_Db_Table_Row_Abstract
+ */
+ public function offsetGet($offset)
+ {
+ $offset = (int) $offset;
+ if ($offset < 0 || $offset >= $this->_count) {
+ require_once 'Zend/Db/Table/Rowset/Exception.php';
+ throw new Zend_Db_Table_Rowset_Exception("Illegal index $offset");
+ }
+ $this->_pointer = $offset;
+
+ return $this->current();
+ }
+
+ /**
+ * Does nothing
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ * @param mixed $value
+ */
+ public function offsetSet($offset, $value)
+ {
+ }
+
+ /**
+ * Does nothing
+ * Required by the ArrayAccess implementation
+ *
+ * @param string $offset
+ */
+ public function offsetUnset($offset)
+ {
+ }
+
+ /**
+ * Returns a Zend_Db_Table_Row from a known position into the Iterator
+ *
+ * @param int $position the position of the row expected
+ * @param bool $seek wether or not seek the iterator to that position after
+ * @return Zend_Db_Table_Row
+ * @throws Zend_Db_Table_Rowset_Exception
+ */
+ public function getRow($position, $seek = false)
+ {
+ try {
+ $row = $this->_loadAndReturnRow($position);
+ } catch (Zend_Db_Table_Rowset_Exception $e) {
+ require_once 'Zend/Db/Table/Rowset/Exception.php';
+ throw new Zend_Db_Table_Rowset_Exception('No row could be found at position ' . (int) $position, 0, $e);
+ }
+
+ if ($seek == true) {
+ $this->seek($position);
+ }
+
+ return $row;
+ }
+
+ /**
+ * Returns all data as an array.
+ *
+ * Updates the $_data property with current row object values.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ // @todo This works only if we have iterated through
+ // the result set once to instantiate the rows.
+ foreach ($this->_rows as $i => $row) {
+ $this->_data[$i] = $row->toArray();
+ }
+ return $this->_data;
+ }
+
+ protected function _loadAndReturnRow($position)
+ {
+ if (!isset($this->_data[$position])) {
+ require_once 'Zend/Db/Table/Rowset/Exception.php';
+ throw new Zend_Db_Table_Rowset_Exception("Data for provided position does not exist");
+ }
+
+ // do we already have a row object for this position?
+ if (empty($this->_rows[$position])) {
+ $this->_rows[$position] = new $this->_rowClass(
+ array(
+ 'table' => $this->_table,
+ 'data' => $this->_data[$position],
+ 'stored' => $this->_stored,
+ 'readOnly' => $this->_readOnly
+ )
+ );
+ }
+
+ // return the row object
+ return $this->_rows[$position];
+ }
+
+}
diff --git a/library/Zend/Db/Table/Rowset/Exception.php b/library/Zend/Db/Table/Rowset/Exception.php
new file mode 100644
index 0000000..d775a40
--- /dev/null
+++ b/library/Zend/Db/Table/Rowset/Exception.php
@@ -0,0 +1,37 @@
+getAdapter());
+
+ $this->setTable($table);
+ }
+
+ /**
+ * Return the table that created this select object
+ *
+ * @return Zend_Db_Table_Abstract
+ */
+ public function getTable()
+ {
+ return $this->_table;
+ }
+
+ /**
+ * Sets the primary table name and retrieves the table schema.
+ *
+ * @param Zend_Db_Table_Abstract $adapter
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function setTable(Zend_Db_Table_Abstract $table)
+ {
+ $this->_adapter = $table->getAdapter();
+ $this->_info = $table->info();
+ $this->_table = $table;
+
+ return $this;
+ }
+
+ /**
+ * Sets the integrity check flag.
+ *
+ * Setting this flag to false skips the checks for table joins, allowing
+ * 'hybrid' table rows to be created.
+ *
+ * @param Zend_Db_Table_Abstract $adapter
+ * @return Zend_Db_Select This Zend_Db_Select object.
+ */
+ public function setIntegrityCheck($flag = true)
+ {
+ $this->_integrityCheck = $flag;
+ return $this;
+ }
+
+ /**
+ * Tests query to determine if expressions or aliases columns exist.
+ *
+ * @return boolean
+ */
+ public function isReadOnly()
+ {
+ $readOnly = false;
+ $fields = $this->getPart(Zend_Db_Table_Select::COLUMNS);
+ $cols = $this->_info[Zend_Db_Table_Abstract::COLS];
+
+ if (!count($fields)) {
+ return $readOnly;
+ }
+
+ foreach ($fields as $columnEntry) {
+ $column = $columnEntry[1];
+ $alias = $columnEntry[2];
+
+ if ($alias !== null) {
+ $column = $alias;
+ }
+
+ switch (true) {
+ case ($column == self::SQL_WILDCARD):
+ break;
+
+ case ($column instanceof Zend_Db_Expr):
+ case (!in_array($column, $cols)):
+ $readOnly = true;
+ break 2;
+ }
+ }
+
+ return $readOnly;
+ }
+
+ /**
+ * Adds a FROM table and optional columns to the query.
+ *
+ * The table name can be expressed
+ *
+ * @param array|string|Zend_Db_Expr|Zend_Db_Table_Abstract $name The table name or an
+ associative array relating
+ table name to correlation
+ name.
+ * @param array|string|Zend_Db_Expr $cols The columns to select from this table.
+ * @param string $schema The schema name to specify, if any.
+ * @return Zend_Db_Table_Select This Zend_Db_Table_Select object.
+ */
+ public function from($name, $cols = self::SQL_WILDCARD, $schema = null)
+ {
+ if ($name instanceof Zend_Db_Table_Abstract) {
+ $info = $name->info();
+ $name = $info[Zend_Db_Table_Abstract::NAME];
+ if (isset($info[Zend_Db_Table_Abstract::SCHEMA])) {
+ $schema = $info[Zend_Db_Table_Abstract::SCHEMA];
+ }
+ }
+
+ return $this->joinInner($name, null, $cols, $schema);
+ }
+
+ /**
+ * Performs a validation on the select query before passing back to the parent class.
+ * Ensures that only columns from the primary Zend_Db_Table are returned in the result.
+ *
+ * @return string|null This object as a SELECT string (or null if a string cannot be produced)
+ */
+ public function assemble()
+ {
+ $fields = $this->getPart(Zend_Db_Table_Select::COLUMNS);
+ $primary = $this->_info[Zend_Db_Table_Abstract::NAME];
+ $schema = $this->_info[Zend_Db_Table_Abstract::SCHEMA];
+
+
+ if (count($this->_parts[self::UNION]) == 0) {
+
+ // If no fields are specified we assume all fields from primary table
+ if (!count($fields)) {
+ $this->from($primary, self::SQL_WILDCARD, $schema);
+ $fields = $this->getPart(Zend_Db_Table_Select::COLUMNS);
+ }
+
+ $from = $this->getPart(Zend_Db_Table_Select::FROM);
+
+ if ($this->_integrityCheck !== false) {
+ foreach ($fields as $columnEntry) {
+ list($table, $column) = $columnEntry;
+
+ // Check each column to ensure it only references the primary table
+ if ($column) {
+ if (!isset($from[$table]) || $from[$table]['tableName'] != $primary) {
+ require_once 'Zend/Db/Table/Select/Exception.php';
+ throw new Zend_Db_Table_Select_Exception('Select query cannot join with another table');
+ }
+ }
+ }
+ }
+ }
+
+ return parent::assemble();
+ }
+}
\ No newline at end of file
diff --git a/library/Zend/Db/Table/Select/Exception.php b/library/Zend/Db/Table/Select/Exception.php
new file mode 100644
index 0000000..4a0bab9
--- /dev/null
+++ b/library/Zend/Db/Table/Select/Exception.php
@@ -0,0 +1,39 @@
+ tags, cleans up newlines and indents, and runs
+ * htmlentities() before output.
+ *
+ * @param mixed $var The variable to dump.
+ * @param string $label OPTIONAL Label to prepend to output.
+ * @param bool $echo OPTIONAL Echo output if true.
+ * @return string
+ */
+ public static function dump($var, $label=null, $echo=true)
+ {
+ // format the label
+ $label = ($label===null) ? '' : rtrim($label) . ' ';
+
+ // var_dump the variable into a buffer and keep the output
+ ob_start();
+ var_dump($var);
+ $output = ob_get_clean();
+
+ // neaten the newlines and indents
+ $output = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $output);
+ if (self::getSapi() == 'cli') {
+ $output = PHP_EOL . $label
+ . PHP_EOL . $output
+ . PHP_EOL;
+ } else {
+ if(!extension_loaded('xdebug')) {
+ $output = htmlspecialchars($output, ENT_QUOTES);
+ }
+
+ $output = ''
+ . $label
+ . $output
+ . '
';
+ }
+
+ if ($echo) {
+ echo($output);
+ }
+ return $output;
+ }
+
+}
diff --git a/library/Zend/Dojo.php b/library/Zend/Dojo.php
new file mode 100644
index 0000000..3ec3b59
--- /dev/null
+++ b/library/Zend/Dojo.php
@@ -0,0 +1,87 @@
+addPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator', 'decorator')
+ ->addPrefixPath('Zend_Dojo_Form_Element', 'Zend/Dojo/Form/Element', 'element')
+ ->addElementPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator', 'decorator')
+ ->addDisplayGroupPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator')
+ ->setDefaultDisplayGroupClass('Zend_Dojo_Form_DisplayGroup');
+
+ foreach ($form->getSubForms() as $subForm) {
+ self::enableForm($subForm);
+ }
+
+ if (null !== ($view = $form->getView())) {
+ self::enableView($view);
+ }
+ }
+
+ /**
+ * Dojo-enable a view instance
+ *
+ * @param Zend_View_Interface $view
+ * @return void
+ */
+ public static function enableView(Zend_View_Interface $view)
+ {
+ if (false === $view->getPluginLoader('helper')->getPaths('Zend_Dojo_View_Helper')) {
+ $view->addHelperPath('Zend/Dojo/View/Helper', 'Zend_Dojo_View_Helper');
+ }
+ }
+}
+
diff --git a/library/Zend/Dojo/BuildLayer.php b/library/Zend/Dojo/BuildLayer.php
new file mode 100644
index 0000000..2943e07
--- /dev/null
+++ b/library/Zend/Dojo/BuildLayer.php
@@ -0,0 +1,536 @@
+ 'release',
+ 'optimize' => 'shrinksafe',
+ 'layerOptimize' => 'shrinksafe',
+ 'copyTests' => false,
+ 'loader' => 'default',
+ 'cssOptimize' => 'comments',
+ );
+
+ /**
+ * Associative array of module/path pairs for the build profile
+ * @var array
+ */
+ protected $_profilePrefixes = array();
+
+ /**
+ * Zend_View reference
+ * @var Zend_View_Interface
+ */
+ protected $_view;
+
+ /**
+ * Constructor
+ *
+ * @param array|Zend_Config $options
+ * @return void
+ * @throws Zend_Dojo_Exception for invalid option argument
+ */
+ public function __construct($options = null)
+ {
+ if (null !== $options) {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ } elseif (!is_array($options)) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Invalid options provided to constructor');
+ }
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Set options
+ *
+ * Proxies to any setter that matches an option key.
+ *
+ * @param array $options
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setOptions(array $options)
+ {
+ $methods = get_class_methods($this);
+ foreach ($options as $key => $value) {
+ $method = 'set' . ucfirst($key);
+ if (in_array($method, $methods)) {
+ $this->$method($value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Set View object
+ *
+ * @param Zend_View_Interface $view
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setView(Zend_View_Interface $view)
+ {
+ $this->_view = $view;
+ return $this;
+ }
+
+ /**
+ * Retrieve view object
+ *
+ * @return Zend_View_Interface|null
+ */
+ public function getView()
+ {
+ return $this->_view;
+ }
+
+ /**
+ * Set dojo() view helper instance
+ *
+ * @param Zend_Dojo_View_Helper_Dojo_Container $helper
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setDojoHelper(Zend_Dojo_View_Helper_Dojo_Container $helper)
+ {
+ $this->_dojo = $helper;
+ return $this;
+ }
+
+ /**
+ * Retrieve dojo() view helper instance
+ *
+ * Will retrieve it from the view object if not registered.
+ *
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ * @throws Zend_Dojo_Exception if not registered and no view object found
+ */
+ public function getDojoHelper()
+ {
+ if (null === $this->_dojo) {
+ if (null === ($view = $this->getView())) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('View object not registered; cannot retrieve dojo helper');
+ }
+ $helper = $view->getHelper('dojo');
+ $this->setDojoHelper($view->dojo());
+ }
+ return $this->_dojo;
+ }
+
+ /**
+ * Set custom layer name; e.g. "custom.main"
+ *
+ * @param string $name
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setLayerName($name)
+ {
+ if (!preg_match('/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/i', $name)) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Invalid layer name provided; must be of form[a-z][a-z0-9_](\.[a-z][a-z0-9_])+');
+ }
+ $this->_layerName = $name;
+ return $this;
+ }
+
+ /**
+ * Retrieve custom layer name
+ *
+ * @return string|null
+ */
+ public function getLayerName()
+ {
+ return $this->_layerName;
+ }
+
+ /**
+ * Set the path to the custom layer script
+ *
+ * Should be a path relative to dojo.js
+ *
+ * @param string $path
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setLayerScriptPath($path)
+ {
+ $this->_layerScriptPath = (string) $path;
+ return $this;
+ }
+
+ /**
+ * Get custom layer script path
+ *
+ * @return string|null
+ */
+ public function getLayerScriptPath()
+ {
+ return $this->_layerScriptPath;
+ }
+
+ /**
+ * Set flag indicating whether or not to consume JS aggregated in dojo()
+ * view helper
+ *
+ * @param bool $flag
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setConsumeJavascript($flag)
+ {
+ $this->_consumeJavascript = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Get flag indicating whether or not to consume JS aggregated in dojo()
+ * view helper
+ *
+ * @return bool
+ */
+ public function consumeJavascript()
+ {
+ return $this->_consumeJavascript;
+ }
+
+ /**
+ * Set flag indicating whether or not to consume dojo.addOnLoad events
+ * aggregated in dojo() view helper
+ *
+ * @param bool $flag
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setConsumeOnLoad($flag)
+ {
+ $this->_consumeOnLoad = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Get flag indicating whether or not to consume dojo.addOnLoad events aggregated in dojo() view helper
+ *
+ * @return bool
+ */
+ public function consumeOnLoad()
+ {
+ return $this->_consumeOnLoad;
+ }
+
+ /**
+ * Set many build profile options at once
+ *
+ * @param array $options
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setProfileOptions(array $options)
+ {
+ $this->_profileOptions += $options;
+ return $this;
+ }
+
+ /**
+ * Add many build profile options at once
+ *
+ * @param array $options
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function addProfileOptions(array $options)
+ {
+ $this->_profileOptions = $this->_profileOptions + $options;
+ return $this;
+ }
+
+ /**
+ * Add a single build profile option
+ *
+ * @param string $key
+ * @param value $value
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function addProfileOption($key, $value)
+ {
+ $this->_profileOptions[(string) $key] = $value;
+ return $this;
+ }
+
+ /**
+ * Is a given build profile option set?
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function hasProfileOption($key)
+ {
+ return array_key_exists((string) $key, $this->_profileOptions);
+ }
+
+ /**
+ * Retrieve a single build profile option
+ *
+ * Returns null if profile option does not exist.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function getProfileOption($key)
+ {
+ if ($this->hasProfileOption($key)) {
+ return $this->_profileOptions[(string) $key];
+ }
+ return null;
+ }
+
+ /**
+ * Get all build profile options
+ *
+ * @return array
+ */
+ public function getProfileOptions()
+ {
+ return $this->_profileOptions;
+ }
+
+ /**
+ * Remove a build profile option
+ *
+ * @param string $name
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function removeProfileOption($name)
+ {
+ if ($this->hasProfileOption($name)) {
+ unset($this->_profileOptions[(string) $name]);
+ }
+ return $this;
+ }
+
+ /**
+ * Remove all build profile options
+ *
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function clearProfileOptions()
+ {
+ $this->_profileOptions = array();
+ return $this;
+ }
+
+ /**
+ * Add a build profile dependency prefix
+ *
+ * If just the prefix is passed, sets path to "../$prefix".
+ *
+ * @param string $prefix
+ * @param null|string $path
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function addProfilePrefix($prefix, $path = null)
+ {
+ if (null === $path) {
+ $path = '../' . $prefix;
+ }
+ $this->_profilePrefixes[$prefix] = array($prefix, $path);
+ return $this;
+ }
+
+ /**
+ * Set multiple dependency prefixes for bulid profile
+ *
+ * @param array $prefixes
+ * @return Zend_Dojo_BuildLayer
+ */
+ public function setProfilePrefixes(array $prefixes)
+ {
+ foreach ($prefixes as $prefix => $path) {
+ $this->addProfilePrefix($prefix, $path);
+ }
+ return $this;
+ }
+
+ /**
+ * Get build profile dependency prefixes
+ *
+ * @return array
+ */
+ public function getProfilePrefixes()
+ {
+ $layerName = $this->getLayerName();
+ if (null !== $layerName) {
+ $prefix = $this->_getPrefix($layerName);
+ if (!array_key_exists($prefix, $this->_profilePrefixes)) {
+ $this->addProfilePrefix($prefix);
+ }
+ }
+ $view = $this->getView();
+ if (!empty($view)) {
+ $helper = $this->getDojoHelper();
+ if ($helper) {
+ $modules = $helper->getModules();
+ foreach ($modules as $module) {
+ $prefix = $this->_getPrefix($module);
+ if (!array_key_exists($prefix, $this->_profilePrefixes)) {
+ $this->addProfilePrefix($prefix);
+ }
+ }
+ }
+ }
+ return $this->_profilePrefixes;
+ }
+
+ /**
+ * Generate module layer script
+ *
+ * @return string
+ */
+ public function generateLayerScript()
+ {
+ $helper = $this->getDojoHelper();
+ $layerName = $this->getLayerName();
+ $modulePaths = $helper->getModulePaths();
+ $modules = $helper->getModules();
+ $onLoadActions = $helper->getOnLoadActions();
+ $javascript = $helper->getJavascript();
+
+ $content = 'dojo.provide("' . $layerName . '");' . "\n\n(function(){\n";
+
+ foreach ($modulePaths as $module => $path) {
+ $content .= sprintf("dojo.registerModulePath(\"%s\", \"%s\");\n", $module, $path);
+ }
+ foreach ($modules as $module) {
+ $content .= sprintf("dojo.require(\"%s\");\n", $module);
+ }
+
+ if ($this->consumeOnLoad()) {
+ foreach ($helper->getOnLoadActions() as $callback) {
+ $content .= sprintf("dojo.addOnLoad(%s);\n", $callback);
+ }
+ }
+ if ($this->consumeJavascript()) {
+ $javascript = implode("\n", $helper->getJavascript());
+ if (!empty($javascript)) {
+ $content .= "\n" . $javascript . "\n";
+ }
+ }
+
+ $content .= "})();";
+
+ return $content;
+ }
+
+ /**
+ * Generate build profile
+ *
+ * @return string
+ */
+ public function generateBuildProfile()
+ {
+ $profileOptions = $this->getProfileOptions();
+ $layerName = $this->getLayerName();
+ $layerScriptPath = $this->getLayerScriptPath();
+ $profilePrefixes = $this->getProfilePrefixes();
+
+ if (!array_key_exists('releaseName', $profileOptions)) {
+ $profileOptions['releaseName'] = substr($layerName, 0, strpos($layerName, '.'));
+ }
+
+ $profile = $profileOptions;
+ $profile['layers'] = array(array(
+ 'name' => $layerScriptPath,
+ 'layerDependencies' => array(),
+ 'dependencies' => array($layerName),
+ ));
+ $profile['prefixes'] = array_values($profilePrefixes);
+
+ return 'dependencies = ' . $this->_filterJsonProfileToJavascript($profile) . ';';
+ }
+
+ /**
+ * Retrieve module prefix
+ *
+ * @param string $module
+ * @return void
+ */
+ protected function _getPrefix($module)
+ {
+ $segments = explode('.', $module, 2);
+ return $segments[0];
+ }
+
+ /**
+ * Filter a JSON build profile to JavaScript
+ *
+ * @param string $profile
+ * @return string
+ */
+ protected function _filterJsonProfileToJavascript($profile)
+ {
+ require_once 'Zend/Json.php';
+ $profile = Zend_Json::encode($profile);
+ $profile = trim($profile, '"');
+ $profile = preg_replace('/' . preg_quote('\\') . '/', '', $profile);
+ return $profile;
+ }
+}
diff --git a/library/Zend/Dojo/Data.php b/library/Zend/Dojo/Data.php
new file mode 100644
index 0000000..6c90899
--- /dev/null
+++ b/library/Zend/Dojo/Data.php
@@ -0,0 +1,563 @@
+setIdentifier($identifier);
+ }
+ if (null !== $items) {
+ $this->setItems($items);
+ }
+ if (null !== $label) {
+ $this->setLabel($label);
+ }
+ }
+
+ /**
+ * Set the items to collect
+ *
+ * @param array|Traversable $items
+ * @return Zend_Dojo_Data
+ */
+ public function setItems($items)
+ {
+ $this->clearItems();
+ return $this->addItems($items);
+ }
+
+ /**
+ * Set an individual item, optionally by identifier (overwrites)
+ *
+ * @param array|object $item
+ * @param string|null $identifier
+ * @return Zend_Dojo_Data
+ */
+ public function setItem($item, $id = null)
+ {
+ $item = $this->_normalizeItem($item, $id);
+ $this->_items[$item['id']] = $item['data'];
+ return $this;
+ }
+
+ /**
+ * Add an individual item, optionally by identifier
+ *
+ * @param array|object $item
+ * @param string|null $id
+ * @return Zend_Dojo_Data
+ */
+ public function addItem($item, $id = null)
+ {
+ $item = $this->_normalizeItem($item, $id);
+
+ if ($this->hasItem($item['id'])) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Overwriting items using addItem() is not allowed');
+ }
+
+ $this->_items[$item['id']] = $item['data'];
+
+ return $this;
+ }
+
+ /**
+ * Add multiple items at once
+ *
+ * @param array|Traversable $items
+ * @return Zend_Dojo_Data
+ */
+ public function addItems($items)
+ {
+ if (!is_array($items) && (!is_object($items) || !($items instanceof Traversable))) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Only arrays and Traversable objects may be added to ' . __CLASS__);
+ }
+
+ foreach ($items as $item) {
+ $this->addItem($item);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get all items as an array
+ *
+ * Serializes items to arrays.
+ *
+ * @return array
+ */
+ public function getItems()
+ {
+ return $this->_items;
+ }
+
+ /**
+ * Does an item with the given identifier exist?
+ *
+ * @param string|int $id
+ * @return bool
+ */
+ public function hasItem($id)
+ {
+ return array_key_exists($id, $this->_items);
+ }
+
+ /**
+ * Retrieve an item by identifier
+ *
+ * Item retrieved will be flattened to an array.
+ *
+ * @param string $id
+ * @return array
+ */
+ public function getItem($id)
+ {
+ if (!$this->hasItem($id)) {
+ return null;
+ }
+
+ return $this->_items[$id];
+ }
+
+ /**
+ * Remove item by identifier
+ *
+ * @param string $id
+ * @return Zend_Dojo_Data
+ */
+ public function removeItem($id)
+ {
+ if ($this->hasItem($id)) {
+ unset($this->_items[$id]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Remove all items at once
+ *
+ * @return Zend_Dojo_Data
+ */
+ public function clearItems()
+ {
+ $this->_items = array();
+ return $this;
+ }
+
+
+ /**
+ * Set identifier for item lookups
+ *
+ * @param string|int|null $identifier
+ * @return Zend_Dojo_Data
+ */
+ public function setIdentifier($identifier)
+ {
+ if (null === $identifier) {
+ $this->_identifier = null;
+ } elseif (is_string($identifier)) {
+ $this->_identifier = $identifier;
+ } elseif (is_numeric($identifier)) {
+ $this->_identifier = (int) $identifier;
+ } else {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Invalid identifier; please use a string or integer');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve current item identifier
+ *
+ * @return string|int|null
+ */
+ public function getIdentifier()
+ {
+ return $this->_identifier;
+ }
+
+
+ /**
+ * Set label to use for displaying item associations
+ *
+ * @param string|null $label
+ * @return Zend_Dojo_Data
+ */
+ public function setLabel($label)
+ {
+ if (null === $label) {
+ $this->_label = null;
+ } else {
+ $this->_label = (string) $label;
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve item association label
+ *
+ * @return string|null
+ */
+ public function getLabel()
+ {
+ return $this->_label;
+ }
+
+ /**
+ * Set metadata by key or en masse
+ *
+ * @param string|array $spec
+ * @param mixed $value
+ * @return Zend_Dojo_Data
+ */
+ public function setMetadata($spec, $value = null)
+ {
+ if (is_string($spec) && (null !== $value)) {
+ $this->_metadata[$spec] = $value;
+ } elseif (is_array($spec)) {
+ foreach ($spec as $key => $value) {
+ $this->setMetadata($key, $value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Get metadata item or all metadata
+ *
+ * @param null|string $key Metadata key when pulling single metadata item
+ * @return mixed
+ */
+ public function getMetadata($key = null)
+ {
+ if (null === $key) {
+ return $this->_metadata;
+ }
+
+ if (array_key_exists($key, $this->_metadata)) {
+ return $this->_metadata[$key];
+ }
+
+ return null;
+ }
+
+ /**
+ * Clear individual or all metadata item(s)
+ *
+ * @param null|string $key
+ * @return Zend_Dojo_Data
+ */
+ public function clearMetadata($key = null)
+ {
+ if (null === $key) {
+ $this->_metadata = array();
+ } elseif (array_key_exists($key, $this->_metadata)) {
+ unset($this->_metadata[$key]);
+ }
+ return $this;
+ }
+
+ /**
+ * Load object from array
+ *
+ * @param array $data
+ * @return Zend_Dojo_Data
+ */
+ public function fromArray(array $data)
+ {
+ if (array_key_exists('identifier', $data)) {
+ $this->setIdentifier($data['identifier']);
+ }
+ if (array_key_exists('label', $data)) {
+ $this->setLabel($data['label']);
+ }
+ if (array_key_exists('items', $data) && is_array($data['items'])) {
+ $this->setItems($data['items']);
+ } else {
+ $this->clearItems();
+ }
+ return $this;
+ }
+
+ /**
+ * Load object from JSON
+ *
+ * @param string $json
+ * @return Zend_Dojo_Data
+ */
+ public function fromJson($json)
+ {
+ if (!is_string($json)) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('fromJson() expects JSON input');
+ }
+ require_once 'Zend/Json.php';
+ $data = Zend_Json::decode($json);
+ return $this->fromArray($data);
+ }
+
+ /**
+ * Seralize entire data structure, including identifier and label, to array
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ if (null === ($identifier = $this->getIdentifier())) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Serialization requires that an identifier be present in the object; first call setIdentifier()');
+ }
+
+ $array = array(
+ 'identifier' => $identifier,
+ 'items' => array_values($this->getItems()),
+ );
+
+ $metadata = $this->getMetadata();
+ if (!empty($metadata)) {
+ foreach ($metadata as $key => $value) {
+ $array[$key] = $value;
+ }
+ }
+
+ if (null !== ($label = $this->getLabel())) {
+ $array['label'] = $label;
+ }
+
+ return $array;
+ }
+
+ /**
+ * Serialize to JSON (dojo.data format)
+ *
+ * @return string
+ */
+ public function toJson()
+ {
+ require_once 'Zend/Json.php';
+ return Zend_Json::encode($this->toArray());
+ }
+
+ /**
+ * Serialize to string (proxy to {@link toJson()})
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * ArrayAccess: does offset exist?
+ *
+ * @param string|int $offset
+ * @return bool
+ */
+ public function offsetExists($offset)
+ {
+ return (null !== $this->getItem($offset));
+ }
+
+ /**
+ * ArrayAccess: retrieve by offset
+ *
+ * @param string|int $offset
+ * @return array
+ */
+ public function offsetGet($offset)
+ {
+ return $this->getItem($offset);
+ }
+
+ /**
+ * ArrayAccess: set value by offset
+ *
+ * @param string $offset
+ * @param array|object|null $value
+ * @return void
+ */
+ public function offsetSet($offset, $value)
+ {
+ $this->setItem($value, $offset);
+ }
+
+ /**
+ * ArrayAccess: unset value by offset
+ *
+ * @param string $offset
+ * @return void
+ */
+ public function offsetUnset($offset)
+ {
+ $this->removeItem($offset);
+ }
+
+ /**
+ * Iterator: get current value
+ *
+ * @return array
+ */
+ public function current()
+ {
+ return current($this->_items);
+ }
+
+ /**
+ * Iterator: get current key
+ *
+ * @return string|int
+ */
+ public function key()
+ {
+ return key($this->_items);
+ }
+
+ /**
+ * Iterator: get next item
+ *
+ * @return void
+ */
+ public function next()
+ {
+ return next($this->_items);
+ }
+
+ /**
+ * Iterator: rewind to first value in collection
+ *
+ * @return void
+ */
+ public function rewind()
+ {
+ return reset($this->_items);
+ }
+
+ /**
+ * Iterator: is item valid?
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return (bool) $this->current();
+ }
+
+ /**
+ * Countable: how many items are present
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->_items);
+ }
+
+ /**
+ * Normalize an item to attach to the collection
+ *
+ * @param array|object $item
+ * @param string|int|null $id
+ * @return array
+ */
+ protected function _normalizeItem($item, $id)
+ {
+ if (null === ($identifier = $this->getIdentifier())) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('You must set an identifier prior to adding items');
+ }
+
+ if (!is_object($item) && !is_array($item)) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Only arrays and objects may be attached');
+ }
+
+ if (is_object($item)) {
+ if (method_exists($item, 'toArray')) {
+ $item = $item->toArray();
+ } else {
+ $item = get_object_vars($item);
+ }
+ }
+
+ if ((null === $id) && !array_key_exists($identifier, $item)) {
+ require_once 'Zend/Dojo/Exception.php';
+ throw new Zend_Dojo_Exception('Item must contain a column matching the currently set identifier');
+ } elseif (null === $id) {
+ $id = $item[$identifier];
+ } else {
+ $item[$identifier] = $id;
+ }
+
+ return array(
+ 'id' => $id,
+ 'data' => $item,
+ );
+ }
+}
diff --git a/library/Zend/Dojo/Exception.php b/library/Zend/Dojo/Exception.php
new file mode 100644
index 0000000..2aab015
--- /dev/null
+++ b/library/Zend/Dojo/Exception.php
@@ -0,0 +1,35 @@
+addPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator', 'decorator')
+ ->addPrefixPath('Zend_Dojo_Form_Element', 'Zend/Dojo/Form/Element', 'element')
+ ->addElementPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator', 'decorator')
+ ->addDisplayGroupPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator')
+ ->setDefaultDisplayGroupClass('Zend_Dojo_Form_DisplayGroup');
+ parent::__construct($options);
+ }
+
+ /**
+ * Load the default decorators
+ *
+ * @return void
+ */
+ public function loadDefaultDecorators()
+ {
+ if ($this->loadDefaultDecoratorsIsDisabled()) {
+ return;
+ }
+
+ $decorators = $this->getDecorators();
+ if (empty($decorators)) {
+ $this->addDecorator('FormElements')
+ ->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form_dojo'))
+ ->addDecorator('DijitForm');
+ }
+ }
+
+ /**
+ * Set the view object
+ *
+ * Ensures that the view object has the dojo view helper path set.
+ *
+ * @param Zend_View_Interface $view
+ * @return Zend_Dojo_Form_Element_Dijit
+ */
+ public function setView(Zend_View_Interface $view = null)
+ {
+ if (null !== $view) {
+ if (false === $view->getPluginLoader('helper')->getPaths('Zend_Dojo_View_Helper')) {
+ $view->addHelperPath('Zend/Dojo/View/Helper', 'Zend_Dojo_View_Helper');
+ }
+ }
+ return parent::setView($view);
+ }
+}
diff --git a/library/Zend/Dojo/Form/Decorator/AccordionContainer.php b/library/Zend/Dojo/Form/Decorator/AccordionContainer.php
new file mode 100644
index 0000000..8feb0dd
--- /dev/null
+++ b/library/Zend/Dojo/Form/Decorator/AccordionContainer.php
@@ -0,0 +1,43 @@
+_helper) {
+ require_once 'Zend/Form/Decorator/Exception.php';
+ throw new Zend_Form_Decorator_Exception('No view helper specified fo DijitContainer decorator');
+ }
+ return $this->_helper;
+ }
+
+ /**
+ * Get element attributes
+ *
+ * @return array
+ */
+ public function getAttribs()
+ {
+ if (null === $this->_attribs) {
+ $attribs = $this->getElement()->getAttribs();
+ if (array_key_exists('dijitParams', $attribs)) {
+ unset($attribs['dijitParams']);
+ }
+ $this->_attribs = $attribs;
+ }
+ return $this->_attribs;
+ }
+
+ /**
+ * Get dijit option parameters
+ *
+ * @return array
+ */
+ public function getDijitParams()
+ {
+ if (null === $this->_dijitParams) {
+ $attribs = $this->getElement()->getAttribs();
+ if (array_key_exists('dijitParams', $attribs)) {
+ $this->_dijitParams = $attribs['dijitParams'];
+ } else {
+ $this->_dijitParams = array();
+ }
+
+ $options = $this->getOptions();
+ if (array_key_exists('dijitParams', $options)) {
+ $this->_dijitParams = array_merge($this->_dijitParams, $options['dijitParams']);
+ $this->removeOption('dijitParams');
+ }
+ }
+
+ // Ensure we have a title param
+ if (!array_key_exists('title', $this->_dijitParams)) {
+ $this->_dijitParams['title'] = $this->getTitle();
+ }
+
+ return $this->_dijitParams;
+ }
+
+ /**
+ * Get title
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ if (null === $this->_title) {
+ $title = null;
+ if (null !== ($element = $this->getElement())) {
+ if (method_exists($element, 'getLegend')) {
+ $title = $element->getLegend();
+ }
+ }
+ if (empty($title) && (null !== ($title = $this->getOption('legend')))) {
+ $this->removeOption('legend');
+ }
+ if (empty($title) && (null !== ($title = $this->getOption('title')))) {
+ $this->removeOption('title');
+ }
+
+ if (!empty($title)) {
+ if (null !== ($translator = $element->getTranslator())) {
+ $title = $translator->translate($title);
+ }
+ $this->_title = $title;
+ }
+ }
+
+ return (empty($this->_title) ? '' : $this->_title);
+ }
+
+ /**
+ * Render a dijit layout container
+ *
+ * Replaces $content entirely from currently set element.
+ *
+ * @param string $content
+ * @return string
+ */
+ public function render($content)
+ {
+ $element = $this->getElement();
+ $view = $element->getView();
+ if (null === $view) {
+ return $content;
+ }
+
+ $dijitParams = $this->getDijitParams();
+ $attribs = array_merge($this->getAttribs(), $this->getOptions());
+
+ if (array_key_exists('legend', $attribs)) {
+ if (!array_key_exists('title', $dijitParams) || empty($dijitParams['title'])) {
+ $dijitParams['title'] = $attribs['legend'];
+ }
+ unset($attribs['legend']);
+ }
+
+ $helper = $this->getHelper();
+ $id = $element->getId() . '-' . $helper;
+
+ if ($view->dojo()->hasDijit($id)) {
+ trigger_error(sprintf('Duplicate dijit ID detected for id "%s; temporarily generating uniqid"', $id), E_USER_WARNING);
+ $base = $id;
+ do {
+ $id = $base . '-' . uniqid();
+ } while ($view->dojo()->hasDijit($id));
+ }
+
+ return $view->$helper($id, $content, $dijitParams, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/Form/Decorator/DijitElement.php b/library/Zend/Dojo/Form/Decorator/DijitElement.php
new file mode 100644
index 0000000..d601bd3
--- /dev/null
+++ b/library/Zend/Dojo/Form/Decorator/DijitElement.php
@@ -0,0 +1,193 @@
+_attribs) {
+ $this->_attribs = parent::getElementAttribs();
+ if (array_key_exists('dijitParams', $this->_attribs)) {
+ $this->setDijitParams($this->_attribs['dijitParams']);
+ unset($this->_attribs['dijitParams']);
+ }
+ }
+
+ return $this->_attribs;
+ }
+
+ /**
+ * Set a single dijit option parameter
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return Zend_Dojo_Form_Decorator_DijitContainer
+ */
+ public function setDijitParam($key, $value)
+ {
+ $this->_dijitParams[(string) $key] = $value;
+ return $this;
+ }
+
+ /**
+ * Set dijit option parameters
+ *
+ * @param array $params
+ * @return Zend_Dojo_Form_Decorator_DijitContainer
+ */
+ public function setDijitParams(array $params)
+ {
+ $this->_dijitParams = array_merge($this->_dijitParams, $params);
+ return $this;
+ }
+
+ /**
+ * Retrieve a single dijit option parameter
+ *
+ * @param string $key
+ * @return mixed|null
+ */
+ public function getDijitParam($key)
+ {
+ $this->getElementAttribs();
+ $key = (string) $key;
+ if (array_key_exists($key, $this->_dijitParams)) {
+ return $this->_dijitParams[$key];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get dijit option parameters
+ *
+ * @return array
+ */
+ public function getDijitParams()
+ {
+ $this->getElementAttribs();
+ return $this->_dijitParams;
+ }
+
+ /**
+ * Render an element using a view helper
+ *
+ * Determine view helper from 'helper' option, or, if none set, from
+ * the element type. Then call as
+ * helper($element->getName(), $element->getValue(), $element->getAttribs())
+ *
+ * @param string $content
+ * @return string
+ * @throws Zend_Form_Decorator_Exception if element or view are not registered
+ */
+ public function render($content)
+ {
+ $element = $this->getElement();
+ $view = $element->getView();
+ if (null === $view) {
+ require_once 'Zend/Form/Decorator/Exception.php';
+ throw new Zend_Form_Decorator_Exception('DijitElement decorator cannot render without a registered view object');
+ }
+
+ $options = null;
+ $helper = $this->getHelper();
+ $separator = $this->getSeparator();
+ $value = $this->getValue($element);
+ $attribs = $this->getElementAttribs();
+ $name = $element->getFullyQualifiedName();
+
+ $dijitParams = $this->getDijitParams();
+ $dijitParams['required'] = $element->isRequired();
+
+ $id = $element->getId();
+ if ($view->dojo()->hasDijit($id)) {
+ trigger_error(sprintf('Duplicate dijit ID detected for id "%s; temporarily generating uniqid"', $id), E_USER_NOTICE);
+ $base = $id;
+ do {
+ $id = $base . '-' . uniqid();
+ } while ($view->dojo()->hasDijit($id));
+ }
+ $attribs['id'] = $id;
+
+ if (array_key_exists('options', $attribs)) {
+ $options = $attribs['options'];
+ }
+
+ $elementContent = $view->$helper($name, $value, $dijitParams, $attribs, $options);
+ switch ($this->getPlacement()) {
+ case self::APPEND:
+ return $content . $separator . $elementContent;
+ case self::PREPEND:
+ return $elementContent . $separator . $content;
+ default:
+ return $elementContent;
+ }
+ }
+}
diff --git a/library/Zend/Dojo/Form/Decorator/DijitForm.php b/library/Zend/Dojo/Form/Decorator/DijitForm.php
new file mode 100644
index 0000000..1ac9d62
--- /dev/null
+++ b/library/Zend/Dojo/Form/Decorator/DijitForm.php
@@ -0,0 +1,61 @@
+getElement();
+ $view = $element->getView();
+ if (null === $view) {
+ return $content;
+ }
+
+ $dijitParams = $this->getDijitParams();
+ $attribs = array_merge($this->getAttribs(), $this->getOptions());
+
+ return $view->form($element->getName(), $attribs, $content);
+ }
+}
diff --git a/library/Zend/Dojo/Form/Decorator/SplitContainer.php b/library/Zend/Dojo/Form/Decorator/SplitContainer.php
new file mode 100644
index 0000000..34a1414
--- /dev/null
+++ b/library/Zend/Dojo/Form/Decorator/SplitContainer.php
@@ -0,0 +1,43 @@
+addPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator');
+ }
+
+ /**
+ * Set the view object
+ *
+ * Ensures that the view object has the dojo view helper path set.
+ *
+ * @param Zend_View_Interface $view
+ * @return Zend_Dojo_Form_Element_Dijit
+ */
+ public function setView(Zend_View_Interface $view = null)
+ {
+ if (null !== $view) {
+ if (false === $view->getPluginLoader('helper')->getPaths('Zend_Dojo_View_Helper')) {
+ $view->addHelperPath('Zend/Dojo/View/Helper', 'Zend_Dojo_View_Helper');
+ }
+ }
+ return parent::setView($view);
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/Button.php b/library/Zend/Dojo/Form/Element/Button.php
new file mode 100644
index 0000000..2abbec9
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/Button.php
@@ -0,0 +1,121 @@
+ $options);
+ }
+
+ parent::__construct($spec, $options);
+ }
+
+ /**
+ * Return label
+ *
+ * If no label is present, returns the currently set name.
+ *
+ * If a translator is present, returns the translated label.
+ *
+ * @return string
+ */
+ public function getLabel()
+ {
+ $value = parent::getLabel();
+
+ if (null === $value) {
+ $value = $this->getName();
+ }
+
+ if (null !== ($translator = $this->getTranslator())) {
+ return $translator->translate($value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Has this submit button been selected?
+ *
+ * @return bool
+ */
+ public function isChecked()
+ {
+ $value = $this->getValue();
+
+ if (empty($value)) {
+ return false;
+ }
+ if ($value != $this->getLabel()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Default decorators
+ *
+ * Uses only 'DijitElement' and 'DtDdWrapper' decorators by default.
+ *
+ * @return void
+ */
+ public function loadDefaultDecorators()
+ {
+ if ($this->loadDefaultDecoratorsIsDisabled()) {
+ return;
+ }
+
+ $decorators = $this->getDecorators();
+ if (empty($decorators)) {
+ $this->addDecorator('DijitElement')
+ ->addDecorator('DtDdWrapper');
+ }
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/CheckBox.php b/library/Zend/Dojo/Form/Element/CheckBox.php
new file mode 100644
index 0000000..8e091c1
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/CheckBox.php
@@ -0,0 +1,206 @@
+ '1',
+ 'uncheckedValue' => '0',
+ );
+
+ /**
+ * Value when checked
+ * @var string
+ */
+ protected $_checkedValue = '1';
+
+ /**
+ * Value when not checked
+ * @var string
+ */
+ protected $_uncheckedValue = '0';
+
+ /**
+ * Current value
+ * @var string 0 or 1
+ */
+ protected $_value = '0';
+
+ /**
+ * Set options
+ *
+ * Intercept checked and unchecked values and set them early; test stored
+ * value against checked and unchecked values after configuration.
+ *
+ * @param array $options
+ * @return Zend_Form_Element_Checkbox
+ */
+ public function setOptions(array $options)
+ {
+ if (array_key_exists('checkedValue', $options)) {
+ $this->setCheckedValue($options['checkedValue']);
+ unset($options['checkedValue']);
+ }
+ if (array_key_exists('uncheckedValue', $options)) {
+ $this->setUncheckedValue($options['uncheckedValue']);
+ unset($options['uncheckedValue']);
+ }
+ parent::setOptions($options);
+
+ $curValue = $this->getValue();
+ $test = array($this->getCheckedValue(), $this->getUncheckedValue());
+ if (!in_array($curValue, $test)) {
+ $this->setValue($curValue);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set value
+ *
+ * If value matches checked value, sets to that value, and sets the checked
+ * flag to true.
+ *
+ * Any other value causes the unchecked value to be set as the current
+ * value, and the checked flag to be set as false.
+ *
+ *
+ * @param mixed $value
+ * @return Zend_Form_Element_Checkbox
+ */
+ public function setValue($value)
+ {
+ if ($value == $this->getCheckedValue()) {
+ parent::setValue($value);
+ $this->checked = true;
+ } else {
+ parent::setValue($this->getUncheckedValue());
+ $this->checked = false;
+ }
+ return $this;
+ }
+
+ /**
+ * Set checked value
+ *
+ * @param string $value
+ * @return Zend_Form_Element_Checkbox
+ */
+ public function setCheckedValue($value)
+ {
+ $this->_checkedValue = (string) $value;
+ $this->options['checkedValue'] = $value;
+ return $this;
+ }
+
+ /**
+ * Get value when checked
+ *
+ * @return string
+ */
+ public function getCheckedValue()
+ {
+ return $this->_checkedValue;
+ }
+
+ /**
+ * Set unchecked value
+ *
+ * @param string $value
+ * @return Zend_Form_Element_Checkbox
+ */
+ public function setUncheckedValue($value)
+ {
+ $this->_uncheckedValue = (string) $value;
+ $this->options['uncheckedValue'] = $value;
+ return $this;
+ }
+
+ /**
+ * Get value when not checked
+ *
+ * @return string
+ */
+ public function getUncheckedValue()
+ {
+ return $this->_uncheckedValue;
+ }
+
+ /**
+ * Set checked flag
+ *
+ * @param bool $flag
+ * @return Zend_Form_Element_Checkbox
+ */
+ public function setChecked($flag)
+ {
+ $this->checked = (bool) $flag;
+ if ($this->checked) {
+ $this->setValue($this->getCheckedValue());
+ } else {
+ $this->setValue($this->getUncheckedValue());
+ }
+ return $this;
+ }
+
+ /**
+ * Get checked flag
+ *
+ * @return bool
+ */
+ public function isChecked()
+ {
+ return $this->checked;
+ }
+}
+
diff --git a/library/Zend/Dojo/Form/Element/ComboBox.php b/library/Zend/Dojo/Form/Element/ComboBox.php
new file mode 100644
index 0000000..1d3e9b3
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/ComboBox.php
@@ -0,0 +1,186 @@
+hasDijitParam('store')) {
+ $this->dijitParams['store'] = array();
+ }
+ return $this->dijitParams['store'];
+ }
+
+ /**
+ * Set datastore identifier
+ *
+ * @param string $identifier
+ * @return Zend_Dojo_Form_Element_ComboBox
+ */
+ public function setStoreId($identifier)
+ {
+ $store = $this->getStoreInfo();
+ $store['store'] = (string) $identifier;
+ $this->setDijitParam('store', $store);
+ return $this;
+ }
+
+ /**
+ * Get datastore identifier
+ *
+ * @return string|null
+ */
+ public function getStoreId()
+ {
+ $store = $this->getStoreInfo();
+ if (array_key_exists('store', $store)) {
+ return $store['store'];
+ }
+ return null;
+ }
+
+ /**
+ * Set datastore dijit type
+ *
+ * @param string $dojoType
+ * @return Zend_Dojo_Form_Element_ComboBox
+ */
+ public function setStoreType($dojoType)
+ {
+ $store = $this->getStoreInfo();
+ $store['type'] = (string) $dojoType;
+ $this->setDijitParam('store', $store);
+ return $this;
+ }
+
+ /**
+ * Get datastore dijit type
+ *
+ * @return string|null
+ */
+ public function getStoreType()
+ {
+ $store = $this->getStoreInfo();
+ if (array_key_exists('type', $store)) {
+ return $store['type'];
+ }
+ return null;
+ }
+
+ /**
+ * Set datastore parameters
+ *
+ * @param array $params
+ * @return Zend_Dojo_Form_Element_ComboBox
+ */
+ public function setStoreParams(array $params)
+ {
+ $store = $this->getStoreInfo();
+ $store['params'] = $params;
+ $this->setDijitParam('store', $store);
+ return $this;
+ }
+
+ /**
+ * Get datastore params
+ *
+ * @return array
+ */
+ public function getStoreParams()
+ {
+ $store = $this->getStoreInfo();
+ if (array_key_exists('params', $store)) {
+ return $store['params'];
+ }
+ return array();
+ }
+
+ /**
+ * Set autocomplete flag
+ *
+ * @param bool $flag
+ * @return Zend_Dojo_Form_Element_ComboBox
+ */
+ public function setAutocomplete($flag)
+ {
+ $this->setDijitParam('autocomplete', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Get autocomplete flag
+ *
+ * @return bool
+ */
+ public function getAutocomplete()
+ {
+ if (!$this->hasDijitParam('autocomplete')) {
+ return false;
+ }
+ return $this->getDijitParam('autocomplete');
+ }
+
+ /**
+ * Is the value valid?
+ *
+ * @param string $value
+ * @param mixed $context
+ * @return bool
+ */
+ public function isValid($value, $context = null)
+ {
+ $storeInfo = $this->getStoreInfo();
+ if (!empty($storeInfo)) {
+ $this->setRegisterInArrayValidator(false);
+ }
+ return parent::isValid($value, $context);
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/CurrencyTextBox.php b/library/Zend/Dojo/Form/Element/CurrencyTextBox.php
new file mode 100644
index 0000000..f8bc65c
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/CurrencyTextBox.php
@@ -0,0 +1,120 @@
+setDijitParam('currency', (string) $currency);
+ return $this;
+ }
+
+ /**
+ * Retrieve currency
+ *
+ * @return string|null
+ */
+ public function getCurrency()
+ {
+ return $this->getDijitParam('currency');
+ }
+
+ /**
+ * Set currency symbol
+ *
+ * Casts to string, uppercases, and trims to three characters.
+ *
+ * @param string $symbol
+ * @return Zend_Dojo_Form_Element_CurrencyTextBox
+ */
+ public function setSymbol($symbol)
+ {
+ $symbol = strtoupper((string) $symbol);
+ $length = strlen($symbol);
+ if (3 > $length) {
+ require_once 'Zend/Form/Element/Exception.php';
+ throw new Zend_Form_Element_Exception('Invalid symbol provided; please provide ISO 4217 alphabetic currency code');
+ }
+ if (3 < $length) {
+ $symbol = substr($symbol, 0, 3);
+ }
+
+ $this->setConstraint('symbol', $symbol);
+ return $this;
+ }
+
+ /**
+ * Retrieve symbol
+ *
+ * @return string|null
+ */
+ public function getSymbol()
+ {
+ return $this->getConstraint('symbol');
+ }
+
+ /**
+ * Set whether currency is fractional
+ *
+ * @param bool $flag
+ * @return Zend_Dojo_Form_Element_CurrencyTextBox
+ */
+ public function setFractional($flag)
+ {
+ $this->setConstraint('fractional', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Get whether or not to present fractional values
+ *
+ * @return bool
+ */
+ public function getFractional()
+ {
+ return ('true' == $this->getConstraint('fractional'));
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/DateTextBox.php b/library/Zend/Dojo/Form/Element/DateTextBox.php
new file mode 100644
index 0000000..3678317
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/DateTextBox.php
@@ -0,0 +1,214 @@
+setConstraint('am,pm', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve am,pm flag
+ *
+ * @return bool
+ */
+ public function getAmPm()
+ {
+ if (!$this->hasConstraint('am,pm')) {
+ return false;
+ }
+ return ('true' ==$this->getConstraint('am,pm'));
+ }
+
+ /**
+ * Set strict flag
+ *
+ * @param bool $strict
+ * @return Zend_Dojo_Form_Element_DateTextBox
+ */
+ public function setStrict($flag)
+ {
+ $this->setConstraint('strict', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve strict flag
+ *
+ * @return bool
+ */
+ public function getStrict()
+ {
+ if (!$this->hasConstraint('strict')) {
+ return false;
+ }
+ return ('true' == $this->getConstraint('strict'));
+ }
+
+ /**
+ * Set locale
+ *
+ * @param string $locale
+ * @return Zend_Dojo_Form_Element_DateTextBox
+ */
+ public function setLocale($locale)
+ {
+ $this->setConstraint('locale', (string) $locale);
+ return $this;
+ }
+
+ /**
+ * Retrieve locale
+ *
+ * @return string|null
+ */
+ public function getLocale()
+ {
+ return $this->getConstraint('locale');
+ }
+
+ /**
+ * Set date format pattern
+ *
+ * @param string $pattern
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setDatePattern($pattern)
+ {
+ $this->setConstraint('datePattern', (string) $pattern);
+ return $this;
+ }
+
+ /**
+ * Retrieve date format pattern
+ *
+ * @return string|null
+ */
+ public function getDatePattern()
+ {
+ return $this->getConstraint('datePattern');
+ }
+
+ /**
+ * Set numeric format formatLength
+ *
+ * @see $_allowedFormatTypes
+ * @param string $formatLength
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setFormatLength($formatLength)
+ {
+ $formatLength = strtolower($formatLength);
+ if (!in_array($formatLength, $this->_allowedFormatTypes)) {
+ require_once 'Zend/Form/Element/Exception.php';
+ throw new Zend_Form_Element_Exception(sprintf('Invalid formatLength "%s" specified', $formatLength));
+ }
+
+ $this->setConstraint('formatLength', $formatLength);
+ return $this;
+ }
+
+ /**
+ * Retrieve formatLength
+ *
+ * @return string|null
+ */
+ public function getFormatLength()
+ {
+ return $this->getConstraint('formatLength');
+ }
+
+ /**
+ * Set numeric format Selector
+ *
+ * @see $_allowedSelectorTypes
+ * @param string $selector
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setSelector($selector)
+ {
+ $selector = strtolower($selector);
+ if (!in_array($selector, $this->_allowedSelectorTypes)) {
+ require_once 'Zend/Form/Element/Exception.php';
+ throw new Zend_Form_Element_Exception(sprintf('Invalid Selector "%s" specified', $selector));
+ }
+
+ $this->setConstraint('selector', $selector);
+ return $this;
+ }
+
+ /**
+ * Retrieve selector
+ *
+ * @return string|null
+ */
+ public function getSelector()
+ {
+ return $this->getConstraint('selector');
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/Dijit.php b/library/Zend/Dojo/Form/Element/Dijit.php
new file mode 100644
index 0000000..5506bc9
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/Dijit.php
@@ -0,0 +1,189 @@
+addPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator', 'decorator');
+ parent::__construct($spec, $options);
+ }
+
+ /**
+ * Set a dijit parameter
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return Zend_Dojo_Form_Element_Dijit
+ */
+ public function setDijitParam($key, $value)
+ {
+ $key = (string) $key;
+ $this->dijitParams[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * Set multiple dijit params at once
+ *
+ * @param array $params
+ * @return Zend_Dojo_Form_Element_Dijit
+ */
+ public function setDijitParams(array $params)
+ {
+ $this->dijitParams = array_merge($this->dijitParams, $params);
+ return $this;
+ }
+
+ /**
+ * Does the given dijit parameter exist?
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function hasDijitParam($key)
+ {
+ return array_key_exists($key, $this->dijitParams);
+ }
+
+ /**
+ * Get a single dijit parameter
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function getDijitParam($key)
+ {
+ $key = (string) $key;
+ if ($this->hasDijitParam($key)) {
+ return $this->dijitParams[$key];
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve all dijit parameters
+ *
+ * @return array
+ */
+ public function getDijitParams()
+ {
+ return $this->dijitParams;
+ }
+
+ /**
+ * Remove a single dijit parameter
+ *
+ * @param string $key
+ * @return Zend_Dojo_Form_Element_Dijit
+ */
+ public function removeDijitParam($key)
+ {
+ $key = (string) $key;
+ if (array_key_exists($key, $this->dijitParams)) {
+ unset($this->dijitParams[$key]);
+ }
+ return $this;
+ }
+
+ /**
+ * Clear all dijit parameters
+ *
+ * @return Zend_Dojo_Form_Element_Dijit
+ */
+ public function clearDijitParams()
+ {
+ $this->dijitParams = array();
+ return $this;
+ }
+
+ /**
+ * Load default decorators
+ *
+ * @return void
+ */
+ public function loadDefaultDecorators()
+ {
+ if ($this->loadDefaultDecoratorsIsDisabled()) {
+ return;
+ }
+
+ $decorators = $this->getDecorators();
+ if (empty($decorators)) {
+ $this->addDecorator('DijitElement')
+ ->addDecorator('Errors')
+ ->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
+ ->addDecorator('HtmlTag', array('tag' => 'dd'))
+ ->addDecorator('Label', array('tag' => 'dt'));
+ }
+ }
+
+ /**
+ * Set the view object
+ *
+ * Ensures that the view object has the dojo view helper path set.
+ *
+ * @param Zend_View_Interface $view
+ * @return Zend_Dojo_Form_Element_Dijit
+ */
+ public function setView(Zend_View_Interface $view = null)
+ {
+ if (null !== $view) {
+ if (false === $view->getPluginLoader('helper')->getPaths('Zend_Dojo_View_Helper')) {
+ $view->addHelperPath('Zend/Dojo/View/Helper', 'Zend_Dojo_View_Helper');
+ }
+ }
+ return parent::setView($view);
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/DijitMulti.php b/library/Zend/Dojo/Form/Element/DijitMulti.php
new file mode 100644
index 0000000..57eb019
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/DijitMulti.php
@@ -0,0 +1,303 @@
+'.
+ * @var string
+ */
+ protected $_separator = '
';
+
+ /**
+ * Which values are translated already?
+ * @var array
+ */
+ protected $_translated = array();
+
+ /**
+ * Retrieve separator
+ *
+ * @return mixed
+ */
+ public function getSeparator()
+ {
+ return $this->_separator;
+ }
+
+ /**
+ * Set separator
+ *
+ * @param mixed $separator
+ * @return self
+ */
+ public function setSeparator($separator)
+ {
+ $this->_separator = $separator;
+ return $this;
+ }
+
+ /**
+ * Retrieve options array
+ *
+ * @return array
+ */
+ protected function _getMultiOptions()
+ {
+ if (null === $this->options || !is_array($this->options)) {
+ $this->options = array();
+ }
+
+ return $this->options;
+ }
+
+ /**
+ * Add an option
+ *
+ * @param string $option
+ * @param string $value
+ * @return Zend_Form_Element_Multi
+ */
+ public function addMultiOption($option, $value = '')
+ {
+ $option = (string) $option;
+ $this->_getMultiOptions();
+ if (!$this->_translateOption($option, $value)) {
+ $this->options[$option] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add many options at once
+ *
+ * @param array $options
+ * @return Zend_Form_Element_Multi
+ */
+ public function addMultiOptions(array $options)
+ {
+ foreach ($options as $option => $value) {
+ if (is_array($value)
+ && array_key_exists('key', $value)
+ && array_key_exists('value', $value)
+ ) {
+ $this->addMultiOption($value['key'], $value['value']);
+ } else {
+ $this->addMultiOption($option, $value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Set all options at once (overwrites)
+ *
+ * @param array $options
+ * @return Zend_Form_Element_Multi
+ */
+ public function setMultiOptions(array $options)
+ {
+ $this->clearMultiOptions();
+ return $this->addMultiOptions($options);
+ }
+
+ /**
+ * Retrieve single multi option
+ *
+ * @param string $option
+ * @return mixed
+ */
+ public function getMultiOption($option)
+ {
+ $option = (string) $option;
+ $this->_getMultiOptions();
+ if (isset($this->options[$option])) {
+ $this->_translateOption($option, $this->options[$option]);
+ return $this->options[$option];
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve options
+ *
+ * @return array
+ */
+ public function getMultiOptions()
+ {
+ $this->_getMultiOptions();
+ foreach ($this->options as $option => $value) {
+ $this->_translateOption($option, $value);
+ }
+ return $this->options;
+ }
+
+ /**
+ * Remove a single multi option
+ *
+ * @param string $option
+ * @return bool
+ */
+ public function removeMultiOption($option)
+ {
+ $option = (string) $option;
+ $this->_getMultiOptions();
+ if (isset($this->options[$option])) {
+ unset($this->options[$option]);
+ if (isset($this->_translated[$option])) {
+ unset($this->_translated[$option]);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Clear all options
+ *
+ * @return Zend_Form_Element_Multi
+ */
+ public function clearMultiOptions()
+ {
+ $this->options = array();
+ $this->_translated = array();
+ return $this;
+ }
+
+ /**
+ * Set flag indicating whether or not to auto-register inArray validator
+ *
+ * @param bool $flag
+ * @return Zend_Form_Element_Multi
+ */
+ public function setRegisterInArrayValidator($flag)
+ {
+ $this->_registerInArrayValidator = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Get status of auto-register inArray validator flag
+ *
+ * @return bool
+ */
+ public function registerInArrayValidator()
+ {
+ return $this->_registerInArrayValidator;
+ }
+
+ /**
+ * Is the value provided valid?
+ *
+ * Autoregisters InArray validator if necessary.
+ *
+ * @param string $value
+ * @param mixed $context
+ * @return bool
+ */
+ public function isValid($value, $context = null)
+ {
+ if ($this->registerInArrayValidator()) {
+ if (!$this->getValidator('InArray')) {
+ $options = $this->getMultiOptions();
+ $this->addValidator(
+ 'InArray',
+ true,
+ array(array_keys($options))
+ );
+ }
+ }
+ return parent::isValid($value, $context);
+ }
+
+ /**
+ * Translate an option
+ *
+ * @param string $option
+ * @param string $value
+ * @return bool
+ */
+ protected function _translateOption($option, $value)
+ {
+ if (!isset($this->_translated[$option])) {
+ $this->options[$option] = $this->_translateValue($value);
+ if ($this->options[$option] === $value) {
+ return false;
+ }
+ $this->_translated[$option] = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Translate a value
+ *
+ * @param array|string $value
+ * @return array|string
+ */
+ protected function _translateValue($value)
+ {
+ if (is_array($value)) {
+ foreach ($value as $key => $val) {
+ $value[$key] = $this->_translateValue($val);
+ }
+ return $value;
+ } else {
+ if (null !== ($translator = $this->getTranslator())) {
+ return $translator->translate($value);
+ }
+
+ return $value;
+ }
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/Editor.php b/library/Zend/Dojo/Form/Element/Editor.php
new file mode 100644
index 0000000..86bd99a
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/Editor.php
@@ -0,0 +1,599 @@
+getCaptureEvents();
+ if (in_array($event, $captureEvents)) {
+ return $this;
+ }
+
+ $captureEvents[] = (string) $event;
+ $this->setDijitParam('captureEvents', $captureEvents);
+ return $this;
+ }
+
+ /**
+ * Add multiple capture events
+ *
+ * @param array $events
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function addCaptureEvents(array $events)
+ {
+ foreach ($events as $event) {
+ $this->addCaptureEvent($event);
+ }
+ return $this;
+ }
+
+ /**
+ * Overwrite many capture events at once
+ *
+ * @param array $events
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setCaptureEvents(array $events)
+ {
+ $this->clearCaptureEvents();
+ $this->addCaptureEvents($events);
+ return $this;
+ }
+
+ /**
+ * Get all capture events
+ *
+ * @return array
+ */
+ public function getCaptureEvents()
+ {
+ if (!$this->hasDijitParam('captureEvents')) {
+ return array();
+ }
+ return $this->getDijitParam('captureEvents');
+ }
+
+ /**
+ * Is a given capture event registered?
+ *
+ * @param string $event
+ * @return bool
+ */
+ public function hasCaptureEvent($event)
+ {
+ $captureEvents = $this->getCaptureEvents();
+ return in_array((string) $event, $captureEvents);
+ }
+
+ /**
+ * Remove a given capture event
+ *
+ * @param string $event
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function removeCaptureEvent($event)
+ {
+ $event = (string) $event;
+ $captureEvents = $this->getCaptureEvents();
+ if (false === ($index = array_search($event, $captureEvents))) {
+ return $this;
+ }
+ unset($captureEvents[$index]);
+ $this->setDijitParam('captureEvents', $captureEvents);
+ return $this;
+ }
+
+ /**
+ * Clear all capture events
+ *
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function clearCaptureEvents()
+ {
+ return $this->removeDijitParam('captureEvents');
+ }
+
+ /**
+ * Add a single event to the dijit
+ *
+ * @param string $event
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function addEvent($event)
+ {
+ $event = (string) $event;
+ $events = $this->getEvents();
+ if (in_array($event, $events)) {
+ return $this;
+ }
+
+ $events[] = (string) $event;
+ $this->setDijitParam('events', $events);
+ return $this;
+ }
+
+ /**
+ * Add multiple events
+ *
+ * @param array $events
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function addEvents(array $events)
+ {
+ foreach ($events as $event) {
+ $this->addEvent($event);
+ }
+ return $this;
+ }
+
+ /**
+ * Overwrite many events at once
+ *
+ * @param array $events
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setEvents(array $events)
+ {
+ $this->clearEvents();
+ $this->addEvents($events);
+ return $this;
+ }
+
+ /**
+ * Get all events
+ *
+ * @return array
+ */
+ public function getEvents()
+ {
+ if (!$this->hasDijitParam('events')) {
+ return array();
+ }
+ return $this->getDijitParam('events');
+ }
+
+ /**
+ * Is a given event registered?
+ *
+ * @param string $event
+ * @return bool
+ */
+ public function hasEvent($event)
+ {
+ $events = $this->getEvents();
+ return in_array((string) $event, $events);
+ }
+
+ /**
+ * Remove a given event
+ *
+ * @param string $event
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function removeEvent($event)
+ {
+ $events = $this->getEvents();
+ if (false === ($index = array_search($event, $events))) {
+ return $this;
+ }
+ unset($events[$index]);
+ $this->setDijitParam('events', $events);
+ return $this;
+ }
+
+ /**
+ * Clear all events
+ *
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function clearEvents()
+ {
+ return $this->removeDijitParam('events');
+ }
+
+ /**
+ * Add a single editor plugin
+ *
+ * @param string $plugin
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function addPlugin($plugin)
+ {
+ $plugin = (string) $plugin;
+ $plugins = $this->getPlugins();
+ if (in_array($plugin, $plugins) && $plugin !== '|') {
+ return $this;
+ }
+
+ $plugins[] = (string) $plugin;
+ $this->setDijitParam('plugins', $plugins);
+ return $this;
+ }
+
+ /**
+ * Add multiple plugins
+ *
+ * @param array $plugins
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function addPlugins(array $plugins)
+ {
+ foreach ($plugins as $plugin) {
+ $this->addPlugin($plugin);
+ }
+ return $this;
+ }
+
+ /**
+ * Overwrite many plugins at once
+ *
+ * @param array $plugins
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setPlugins(array $plugins)
+ {
+ $this->clearPlugins();
+ $this->addPlugins($plugins);
+ return $this;
+ }
+
+ /**
+ * Get all plugins
+ *
+ * @return array
+ */
+ public function getPlugins()
+ {
+ if (!$this->hasDijitParam('plugins')) {
+ return array();
+ }
+ return $this->getDijitParam('plugins');
+ }
+
+ /**
+ * Is a given plugin registered?
+ *
+ * @param string $plugin
+ * @return bool
+ */
+ public function hasPlugin($plugin)
+ {
+ $plugins = $this->getPlugins();
+ return in_array((string) $plugin, $plugins);
+ }
+
+ /**
+ * Remove a given plugin
+ *
+ * @param string $plugin
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function removePlugin($plugin)
+ {
+ $plugins = $this->getPlugins();
+ if (false === ($index = array_search($plugin, $plugins))) {
+ return $this;
+ }
+ unset($plugins[$index]);
+ $this->setDijitParam('plugins', $plugins);
+ return $this;
+ }
+
+ /**
+ * Clear all plugins
+ *
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function clearPlugins()
+ {
+ return $this->removeDijitParam('plugins');
+ }
+
+ /**
+ * Set edit action interval
+ *
+ * @param int $interval
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setEditActionInterval($interval)
+ {
+ return $this->setDijitParam('editActionInterval', (int) $interval);
+ }
+
+ /**
+ * Get edit action interval; defaults to 3
+ *
+ * @return int
+ */
+ public function getEditActionInterval()
+ {
+ if (!$this->hasDijitParam('editActionInterval')) {
+ $this->setEditActionInterval(3);
+ }
+ return $this->getDijitParam('editActionInterval');
+ }
+
+ /**
+ * Set focus on load flag
+ *
+ * @param bool $flag
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setFocusOnLoad($flag)
+ {
+ return $this->setDijitParam('focusOnLoad', (bool) $flag);
+ }
+
+ /**
+ * Retrieve focus on load flag
+ *
+ * @return bool
+ */
+ public function getFocusOnLoad()
+ {
+ if (!$this->hasDijitParam('focusOnLoad')) {
+ return false;
+ }
+ return $this->getDijitParam('focusOnLoad');
+ }
+
+ /**
+ * Set editor height
+ *
+ * @param string|int $height
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setHeight($height)
+ {
+ if (!preg_match('/^\d+(em|px|%)?$/i', $height)) {
+ require_once 'Zend/Form/Element/Exception.php';
+ throw new Zend_Form_Element_Exception('Invalid height provided; must be integer or CSS measurement');
+ }
+ if (!preg_match('/(em|px|%)$/', $height)) {
+ $height .= 'px';
+ }
+ return $this->setDijitParam('height', $height);
+ }
+
+ /**
+ * Retrieve height
+ *
+ * @return string
+ */
+ public function getHeight()
+ {
+ if (!$this->hasDijitParam('height')) {
+ return '300px';
+ }
+ return $this->getDijitParam('height');
+ }
+
+ /**
+ * Set whether or not to inherit parent's width
+ *
+ * @param bool $flag
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setInheritWidth($flag)
+ {
+ return $this->setDijitParam('inheritWidth', (bool) $flag);
+ }
+
+ /**
+ * Whether or not to inherit the parent's width
+ *
+ * @return bool
+ */
+ public function getInheritWidth()
+ {
+ if (!$this->hasDijitParam('inheritWidth')) {
+ return false;
+ }
+ return $this->getDijitParam('inheritWidth');
+ }
+
+ /**
+ * Set minimum height of editor
+ *
+ * @param string|int $minHeight
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setMinHeight($minHeight)
+ {
+ if (!preg_match('/^\d+(em|px|%)?$/i', $minHeight)) {
+ require_once 'Zend/Form/Element/Exception.php';
+ throw new Zend_Form_Element_Exception('Invalid minHeight provided; must be integer or CSS measurement');
+ }
+ if (!preg_match('/(em|px|%)$/', $minHeight)) {
+ $minHeight .= 'em';
+ }
+ return $this->setDijitParam('minHeight', $minHeight);
+ }
+
+ /**
+ * Get minimum height of editor
+ *
+ * @return string
+ */
+ public function getMinHeight()
+ {
+ if (!$this->hasDijitParam('minHeight')) {
+ return '1em';
+ }
+ return $this->getDijitParam('minHeight');
+ }
+
+ /**
+ * Add a custom stylesheet
+ *
+ * @param string $styleSheet
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function addStyleSheet($styleSheet)
+ {
+ $stylesheets = $this->getStyleSheets();
+ if (strstr($stylesheets, ';')) {
+ $stylesheets = explode(';', $stylesheets);
+ } elseif (!empty($stylesheets)) {
+ $stylesheets = (array) $stylesheets;
+ } else {
+ $stylesheets = array();
+ }
+ if (!in_array($styleSheet, $stylesheets)) {
+ $stylesheets[] = (string) $styleSheet;
+ }
+ return $this->setDijitParam('styleSheets', implode(';', $stylesheets));
+ }
+
+ /**
+ * Add multiple custom stylesheets
+ *
+ * @param array $styleSheets
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function addStyleSheets(array $styleSheets)
+ {
+ foreach ($styleSheets as $styleSheet) {
+ $this->addStyleSheet($styleSheet);
+ }
+ return $this;
+ }
+
+ /**
+ * Overwrite all stylesheets
+ *
+ * @param array $styleSheets
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setStyleSheets(array $styleSheets)
+ {
+ $this->clearStyleSheets();
+ return $this->addStyleSheets($styleSheets);
+ }
+
+ /**
+ * Get all stylesheets
+ *
+ * @return string
+ */
+ public function getStyleSheets()
+ {
+ if (!$this->hasDijitParam('styleSheets')) {
+ return '';
+ }
+ return $this->getDijitParam('styleSheets');
+ }
+
+ /**
+ * Is a given stylesheet registered?
+ *
+ * @param string $styleSheet
+ * @return bool
+ */
+ public function hasStyleSheet($styleSheet)
+ {
+ $styleSheets = $this->getStyleSheets();
+ $styleSheets = explode(';', $styleSheets);
+ return in_array($styleSheet, $styleSheets);
+ }
+
+ /**
+ * Remove a single stylesheet
+ *
+ * @param string $styleSheet
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function removeStyleSheet($styleSheet)
+ {
+ $styleSheets = $this->getStyleSheets();
+ $styleSheets = explode(';', $styleSheets);
+ if (false !== ($index = array_search($styleSheet, $styleSheets))) {
+ unset($styleSheets[$index]);
+ $this->setDijitParam('styleSheets', implode(';', $styleSheets));
+ }
+ return $this;
+ }
+
+ /**
+ * Clear all stylesheets
+ *
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function clearStyleSheets()
+ {
+ if ($this->hasDijitParam('styleSheets')) {
+ $this->removeDijitParam('styleSheets');
+ }
+ return $this;
+ }
+
+ /**
+ * Set update interval
+ *
+ * @param int $interval
+ * @return Zend_Dojo_Form_Element_Editor
+ */
+ public function setUpdateInterval($interval)
+ {
+ return $this->setDijitParam('updateInterval', (int) $interval);
+ }
+
+ /**
+ * Get update interval
+ *
+ * @return int
+ */
+ public function getUpdateInterval()
+ {
+ if (!$this->hasDijitParam('updateInterval')) {
+ return 200;
+ }
+ return $this->getDijitParam('updateInterval');
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/FilteringSelect.php b/library/Zend/Dojo/Form/Element/FilteringSelect.php
new file mode 100644
index 0000000..a9eda22
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/FilteringSelect.php
@@ -0,0 +1,48 @@
+hasDijitParam('topDecoration')) {
+ return $this->getDijitParam('topDecoration');
+ }
+ return array();
+ }
+
+ /**
+ * Set dijit to use with top decoration
+ *
+ * @param mixed $dijit
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setTopDecorationDijit($dijit)
+ {
+ $decoration = $this->getTopDecoration();
+ $decoration['dijit'] = (string) $dijit;
+ $this->setDijitParam('topDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set container to use with top decoration
+ *
+ * @param mixed $container
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setTopDecorationContainer($container)
+ {
+ $decoration = $this->getTopDecoration();
+ $decoration['container'] = (string) $container;
+ $this->setDijitParam('topDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set labels to use with top decoration
+ *
+ * @param array $labels
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setTopDecorationLabels(array $labels)
+ {
+ $decoration = $this->getTopDecoration();
+ $decoration['labels'] = array_values($labels);
+ $this->setDijitParam('topDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set params to use with top decoration
+ *
+ * @param array $params
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setTopDecorationParams(array $params)
+ {
+ $decoration = $this->getTopDecoration();
+ $decoration['params'] = $params;
+ $this->setDijitParam('topDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set attribs to use with top decoration
+ *
+ * @param array $attribs
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setTopDecorationAttribs(array $attribs)
+ {
+ $decoration = $this->getTopDecoration();
+ $decoration['attribs'] = $attribs;
+ $this->setDijitParam('topDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Get bottom decoration data
+ *
+ * @return array
+ */
+ public function getBottomDecoration()
+ {
+ if ($this->hasDijitParam('bottomDecoration')) {
+ return $this->getDijitParam('bottomDecoration');
+ }
+ return array();
+ }
+
+ /**
+ * Set dijit to use with bottom decoration
+ *
+ * @param mixed $dijit
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setBottomDecorationDijit($dijit)
+ {
+ $decoration = $this->getBottomDecoration();
+ $decoration['dijit'] = (string) $dijit;
+ $this->setDijitParam('bottomDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set container to use with bottom decoration
+ *
+ * @param mixed $container
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setBottomDecorationContainer($container)
+ {
+ $decoration = $this->getBottomDecoration();
+ $decoration['container'] = (string) $container;
+ $this->setDijitParam('bottomDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set labels to use with bottom decoration
+ *
+ * @param array $labels
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setBottomDecorationLabels(array $labels)
+ {
+ $decoration = $this->getBottomDecoration();
+ $decoration['labels'] = array_values($labels);
+ $this->setDijitParam('bottomDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set params to use with bottom decoration
+ *
+ * @param array $params
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setBottomDecorationParams(array $params)
+ {
+ $decoration = $this->getBottomDecoration();
+ $decoration['params'] = $params;
+ $this->setDijitParam('bottomDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set attribs to use with bottom decoration
+ *
+ * @param array $attribs
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setBottomDecorationAttribs(array $attribs)
+ {
+ $decoration = $this->getBottomDecoration();
+ $decoration['attribs'] = $attribs;
+ $this->setDijitParam('bottomDecoration', $decoration);
+ return $this;
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/NumberSpinner.php b/library/Zend/Dojo/Form/Element/NumberSpinner.php
new file mode 100644
index 0000000..099a4e2
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/NumberSpinner.php
@@ -0,0 +1,245 @@
+setDijitParam('defaultTimeout', (int) $timeout);
+ return $this;
+ }
+
+ /**
+ * Retrieve defaultTimeout
+ *
+ * @return int|null
+ */
+ public function getDefaultTimeout()
+ {
+ return $this->getDijitParam('defaultTimeout');
+ }
+
+ /**
+ * Set timeoutChangeRate
+ *
+ * @param int $rate
+ * @return Zend_Dojo_Form_Element_NumberSpinner
+ */
+ public function setTimeoutChangeRate($rate)
+ {
+ $this->setDijitParam('timeoutChangeRate', (int) $rate);
+ return $this;
+ }
+
+ /**
+ * Retrieve timeoutChangeRate
+ *
+ * @return int|null
+ */
+ public function getTimeoutChangeRate()
+ {
+ return $this->getDijitParam('timeoutChangeRate');
+ }
+
+ /**
+ * Set largeDelta
+ *
+ * @param int $delta
+ * @return Zend_Dojo_Form_Element_NumberSpinner
+ */
+ public function setLargeDelta($delta)
+ {
+ $this->setDijitParam('largeDelta', (float) $delta);
+ return $this;
+ }
+
+ /**
+ * Retrieve largeDelta
+ *
+ * @return int|null
+ */
+ public function getLargeDelta()
+ {
+ return $this->getDijitParam('largeDelta');
+ }
+
+ /**
+ * Set smallDelta
+ *
+ * @param int $delta
+ * @return Zend_Dojo_Form_Element_NumberSpinner
+ */
+ public function setSmallDelta($delta)
+ {
+ $this->setDijitParam('smallDelta', (float) $delta);
+ return $this;
+ }
+
+ /**
+ * Retrieve smallDelta
+ *
+ * @return int|null
+ */
+ public function getSmallDelta()
+ {
+ return $this->getDijitParam('smallDelta');
+ }
+
+ /**
+ * Set intermediateChanges flag
+ *
+ * @param bool $flag
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setIntermediateChanges($flag)
+ {
+ $this->setDijitParam('intermediateChanges', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve intermediateChanges flag
+ *
+ * @return bool
+ */
+ public function getIntermediateChanges()
+ {
+ if (!$this->hasDijitParam('intermediateChanges')) {
+ return false;
+ }
+ return $this->getDijitParam('intermediateChanges');
+ }
+
+ /**
+ * Set rangeMessage
+ *
+ * @param string $message
+ * @return Zend_Dojo_Form_Element_NumberSpinner
+ */
+ public function setRangeMessage($message)
+ {
+ $this->setDijitParam('rangeMessage', (string) $message);
+ return $this;
+ }
+
+ /**
+ * Retrieve rangeMessage
+ *
+ * @return string|null
+ */
+ public function getRangeMessage()
+ {
+ return $this->getDijitParam('rangeMessage');
+ }
+
+ /**
+ * Set minimum value
+ *
+ * @param int $value
+ * @return Zend_Dojo_Form_Element_NumberSpinner
+ */
+ public function setMin($value)
+ {
+ $constraints = array();
+ if ($this->hasDijitParam('constraints')) {
+ $constraints = $this->getDijitParam('constraints');
+ }
+ $constraints['min'] = (float) $value;
+ $this->setDijitParam('constraints', $constraints);
+ return $this;
+ }
+
+ /**
+ * Get minimum value
+ *
+ * @return null|int
+ */
+ public function getMin()
+ {
+ if (!$this->hasDijitParam('constraints')) {
+ return null;
+ }
+ $constraints = $this->getDijitParam('constraints');
+ if (!array_key_exists('min', $constraints)) {
+ return null;
+ }
+ return $constraints['min'];
+ }
+
+ /**
+ * Set maximum value
+ *
+ * @param int $value
+ * @return Zend_Dojo_Form_Element_NumberSpinner
+ */
+ public function setMax($value)
+ {
+ $constraints = array();
+ if ($this->hasDijitParam('constraints')) {
+ $constraints = $this->getDijitParam('constraints');
+ }
+ $constraints['max'] = (float) $value;
+ $this->setDijitParam('constraints', $constraints);
+ return $this;
+ }
+
+ /**
+ * Get maximum value
+ *
+ * @return null|int
+ */
+ public function getMax()
+ {
+ if (!$this->hasDijitParam('constraints')) {
+ return null;
+ }
+ $constraints = $this->getDijitParam('constraints');
+ if (!array_key_exists('max', $constraints)) {
+ return null;
+ }
+ return $constraints['max'];
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/NumberTextBox.php b/library/Zend/Dojo/Form/Element/NumberTextBox.php
new file mode 100644
index 0000000..023c4de
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/NumberTextBox.php
@@ -0,0 +1,173 @@
+setConstraint('locale', (string) $locale);
+ return $this;
+ }
+
+ /**
+ * Retrieve locale
+ *
+ * @return string|null
+ */
+ public function getLocale()
+ {
+ return $this->getConstraint('locale');
+ }
+
+ /**
+ * Set numeric format pattern
+ *
+ * @param string $pattern
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setPattern($pattern)
+ {
+ $this->setConstraint('pattern', (string) $pattern);
+ return $this;
+ }
+
+ /**
+ * Retrieve numeric format pattern
+ *
+ * @return string|null
+ */
+ public function getPattern()
+ {
+ return $this->getConstraint('pattern');
+ }
+
+ /**
+ * Set numeric format type
+ *
+ * @see $_allowedTypes
+ * @param string $type
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setType($type)
+ {
+ $type = strtolower($type);
+ if (!in_array($type, $this->_allowedTypes)) {
+ require_once 'Zend/Form/Element/Exception.php';
+ throw new Zend_Form_Element_Exception(sprintf('Invalid numeric type "%s" specified', $type));
+ }
+
+ $this->setConstraint('type', $type);
+ return $this;
+ }
+
+ /**
+ * Retrieve type
+ *
+ * @return string|null
+ */
+ public function getType()
+ {
+ return $this->getConstraint('type');
+ }
+
+ /**
+ * Set decimal places
+ *
+ * @param int $places
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setPlaces($places)
+ {
+ $this->setConstraint('places', (int) $places);
+ return $this;
+ }
+
+ /**
+ * Retrieve decimal places
+ *
+ * @return int|null
+ */
+ public function getPlaces()
+ {
+ return $this->getConstraint('places');
+ }
+
+ /**
+ * Set strict flag
+ *
+ * @param bool $strict
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setStrict($flag)
+ {
+ $this->setConstraint('strict', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve strict flag
+ *
+ * @return bool
+ */
+ public function getStrict()
+ {
+ if (!$this->hasConstraint('strict')) {
+ return false;
+ }
+ return ('true' == $this->getConstraint('strict'));
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/PasswordTextBox.php b/library/Zend/Dojo/Form/Element/PasswordTextBox.php
new file mode 100644
index 0000000..1034c09
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/PasswordTextBox.php
@@ -0,0 +1,42 @@
+setDijitParam('clickSelect', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve clickSelect flag
+ *
+ * @return bool
+ */
+ public function getClickSelect()
+ {
+ if (!$this->hasDijitParam('clickSelect')) {
+ return false;
+ }
+ return $this->getDijitParam('clickSelect');
+ }
+
+ /**
+ * Set intermediateChanges flag
+ *
+ * @param bool $intermediateChanges
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setIntermediateChanges($flag)
+ {
+ $this->setDijitParam('intermediateChanges', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve intermediateChanges flag
+ *
+ * @return bool
+ */
+ public function getIntermediateChanges()
+ {
+ if (!$this->hasDijitParam('intermediateChanges')) {
+ return false;
+ }
+ return $this->getDijitParam('intermediateChanges');
+ }
+
+ /**
+ * Set showButtons flag
+ *
+ * @param bool $showButtons
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setShowButtons($flag)
+ {
+ $this->setDijitParam('showButtons', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve showButtons flag
+ *
+ * @return bool
+ */
+ public function getShowButtons()
+ {
+ if (!$this->hasDijitParam('showButtons')) {
+ return false;
+ }
+ return $this->getDijitParam('showButtons');
+ }
+
+ /**
+ * Set discreteValues
+ *
+ * @param int $value
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setDiscreteValues($value)
+ {
+ $this->setDijitParam('discreteValues', (int) $value);
+ return $this;
+ }
+
+ /**
+ * Retrieve discreteValues
+ *
+ * @return int|null
+ */
+ public function getDiscreteValues()
+ {
+ return $this->getDijitParam('discreteValues');
+ }
+
+ /**
+ * Set maximum
+ *
+ * @param int $value
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setMaximum($value)
+ {
+ $this->setDijitParam('maximum', (int) $value);
+ return $this;
+ }
+
+ /**
+ * Retrieve maximum
+ *
+ * @return int|null
+ */
+ public function getMaximum()
+ {
+ return $this->getDijitParam('maximum');
+ }
+
+ /**
+ * Set minimum
+ *
+ * @param int $value
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setMinimum($value)
+ {
+ $this->setDijitParam('minimum', (int) $value);
+ return $this;
+ }
+
+ /**
+ * Retrieve minimum
+ *
+ * @return int|null
+ */
+ public function getMinimum()
+ {
+ return $this->getDijitParam('minimum');
+ }
+
+ /**
+ * Set pageIncrement
+ *
+ * @param int $value
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setPageIncrement($value)
+ {
+ $this->setDijitParam('pageIncrement', (int) $value);
+ return $this;
+ }
+
+ /**
+ * Retrieve pageIncrement
+ *
+ * @return int|null
+ */
+ public function getPageIncrement()
+ {
+ return $this->getDijitParam('pageIncrement');
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/SubmitButton.php b/library/Zend/Dojo/Form/Element/SubmitButton.php
new file mode 100644
index 0000000..92d4748
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/SubmitButton.php
@@ -0,0 +1,42 @@
+setDijitParam('lowercase', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve lowercase flag
+ *
+ * @return bool
+ */
+ public function getLowercase()
+ {
+ if (!$this->hasDijitParam('lowercase')) {
+ return false;
+ }
+ return $this->getDijitParam('lowercase');
+ }
+
+ /**
+ * Set propercase flag
+ *
+ * @param bool $propercase
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setPropercase($flag)
+ {
+ $this->setDijitParam('propercase', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve propercase flag
+ *
+ * @return bool
+ */
+ public function getPropercase()
+ {
+ if (!$this->hasDijitParam('propercase')) {
+ return false;
+ }
+ return $this->getDijitParam('propercase');
+ }
+
+ /**
+ * Set uppercase flag
+ *
+ * @param bool $uppercase
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setUppercase($flag)
+ {
+ $this->setDijitParam('uppercase', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve uppercase flag
+ *
+ * @return bool
+ */
+ public function getUppercase()
+ {
+ if (!$this->hasDijitParam('uppercase')) {
+ return false;
+ }
+ return $this->getDijitParam('uppercase');
+ }
+
+ /**
+ * Set trim flag
+ *
+ * @param bool $trim
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setTrim($flag)
+ {
+ $this->setDijitParam('trim', (bool) $flag);
+ return $this;
+ }
+
+ /**
+ * Retrieve trim flag
+ *
+ * @return bool
+ */
+ public function getTrim()
+ {
+ if (!$this->hasDijitParam('trim')) {
+ return false;
+ }
+ return $this->getDijitParam('trim');
+ }
+
+ /**
+ * Set maxLength
+ *
+ * @param int $length
+ * @return Zend_Dojo_Form_Element_TextBox
+ */
+ public function setMaxLength($length)
+ {
+ $this->setDijitParam('maxLength', (int) $length);
+ return $this;
+ }
+
+ /**
+ * Retrieve maxLength
+ *
+ * @return int|null
+ */
+ public function getMaxLength()
+ {
+ return $this->getDijitParam('maxLength');
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/Textarea.php b/library/Zend/Dojo/Form/Element/Textarea.php
new file mode 100644
index 0000000..9881113
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/Textarea.php
@@ -0,0 +1,42 @@
+setConstraint('timePattern', (string) $pattern);
+ return $this;
+ }
+
+ /**
+ * Retrieve time format pattern
+ *
+ * @return string|null
+ */
+ public function getTimePattern()
+ {
+ return $this->getConstraint('timePattern');
+ }
+
+ /**
+ * Set clickableIncrement
+ *
+ * @param string $format
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setClickableIncrement($format)
+ {
+ $format = (string) $format;
+ $this->_validateIso8601($format);
+ $this->setConstraint('clickableIncrement', $format);
+ return $this;
+ }
+
+ /**
+ * Retrieve clickableIncrement
+ *
+ * @return string|null
+ */
+ public function getClickableIncrement()
+ {
+ return $this->getConstraint('clickableIncrement');
+ }
+
+ /**
+ * Set visibleIncrement
+ *
+ * @param string $format
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setVisibleIncrement($format)
+ {
+ $format = (string) $format;
+ $this->_validateIso8601($format);
+ $this->setConstraint('visibleIncrement', $format);
+ return $this;
+ }
+
+ /**
+ * Retrieve visibleIncrement
+ *
+ * @return string|null
+ */
+ public function getVisibleIncrement()
+ {
+ return $this->getConstraint('visibleIncrement');
+ }
+
+ /**
+ * Set visibleRange
+ *
+ * @param string $format
+ * @return Zend_Dojo_Form_Element_NumberTextBox
+ */
+ public function setVisibleRange($format)
+ {
+ $format = (string) $format;
+ $this->_validateIso8601($format);
+ $this->setConstraint('visibleRange', $format);
+ return $this;
+ }
+
+ /**
+ * Retrieve visibleRange
+ *
+ * @return string|null
+ */
+ public function getVisibleRange()
+ {
+ return $this->getConstraint('visibleRange');
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/ValidationTextBox.php b/library/Zend/Dojo/Form/Element/ValidationTextBox.php
new file mode 100644
index 0000000..6822004
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/ValidationTextBox.php
@@ -0,0 +1,220 @@
+setDijitParam('invalidMessage', (string) $message);
+ return $this;
+ }
+
+ /**
+ * Retrieve invalidMessage
+ *
+ * @return string|null
+ */
+ public function getInvalidMessage()
+ {
+ return $this->getDijitParam('invalidMessage');
+ }
+
+ /**
+ * Set promptMessage
+ *
+ * @param string $message
+ * @return Zend_Dojo_Form_Element_ValidationTextBox
+ */
+ public function setPromptMessage($message)
+ {
+ $this->setDijitParam('promptMessage', (string) $message);
+ return $this;
+ }
+
+ /**
+ * Retrieve promptMessage
+ *
+ * @return string|null
+ */
+ public function getPromptMessage()
+ {
+ return $this->getDijitParam('promptMessage');
+ }
+
+ /**
+ * Set regExp
+ *
+ * @param string $regexp
+ * @return Zend_Dojo_Form_Element_ValidationTextBox
+ */
+ public function setRegExp($regexp)
+ {
+ $this->setDijitParam('regExp', (string) $regexp);
+ return $this;
+ }
+
+ /**
+ * Retrieve regExp
+ *
+ * @return string|null
+ */
+ public function getRegExp()
+ {
+ return $this->getDijitParam('regExp');
+ }
+
+ /**
+ * Set an individual constraint
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return Zend_Dojo_Form_Element_ValidationTextBox
+ */
+ public function setConstraint($key, $value)
+ {
+ $constraints = $this->getConstraints();
+ $constraints[(string) $key] = $value;
+ $this->setConstraints($constraints);
+ return $this;
+ }
+
+ /**
+ * Set validation constraints
+ *
+ * Refer to Dojo dijit.form.ValidationTextBox documentation for valid
+ * structure.
+ *
+ * @param array $constraints
+ * @return Zend_Dojo_Form_Element_ValidationTextBox
+ */
+ public function setConstraints(array $constraints)
+ {
+ $tmp = $this->getConstraints();
+ $constraints = array_merge($tmp, $constraints);
+ array_walk_recursive($constraints, array($this, '_castBoolToString'));
+ $this->setDijitParam('constraints', $constraints);
+ return $this;
+ }
+
+ /**
+ * Is the given constraint set?
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function hasConstraint($key)
+ {
+ $constraints = $this->getConstraints();
+ return array_key_exists((string)$key, $constraints);
+ }
+
+ /**
+ * Get an individual constraint
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function getConstraint($key)
+ {
+ $key = (string) $key;
+ if (!$this->hasConstraint($key)) {
+ return null;
+ }
+ return $this->dijitParams['constraints'][$key];
+ }
+
+ /**
+ * Get constraints
+ *
+ * @return array
+ */
+ public function getConstraints()
+ {
+ if ($this->hasDijitParam('constraints')) {
+ return $this->getDijitParam('constraints');
+ }
+ return array();
+ }
+
+ /**
+ * Remove a single constraint
+ *
+ * @param string $key
+ * @return Zend_Dojo_Form_Element_ValidationTextBox
+ */
+ public function removeConstraint($key)
+ {
+ $key = (string) $key;
+ if ($this->hasConstraint($key)) {
+ unset($this->dijitParams['constraints'][$key]);
+ }
+ return $this;
+ }
+
+ /**
+ * Clear all constraints
+ *
+ * @return Zend_Dojo_Form_Element_ValidationTextBox
+ */
+ public function clearConstraints()
+ {
+ return $this->removeDijitParam('constraints');
+ }
+
+ /**
+ * Cast a boolean value to a string
+ *
+ * @param mixed $item
+ * @param string $key
+ * @return void
+ */
+ protected function _castBoolToString(&$item, $key)
+ {
+ if (is_bool($item)) {
+ $item = ($item) ? 'true' : 'false';
+ }
+ }
+}
diff --git a/library/Zend/Dojo/Form/Element/VerticalSlider.php b/library/Zend/Dojo/Form/Element/VerticalSlider.php
new file mode 100644
index 0000000..a1b4b17
--- /dev/null
+++ b/library/Zend/Dojo/Form/Element/VerticalSlider.php
@@ -0,0 +1,208 @@
+hasDijitParam('leftDecoration')) {
+ return $this->getDijitParam('leftDecoration');
+ }
+ return array();
+ }
+
+ /**
+ * Set dijit to use with left decoration
+ *
+ * @param mixed $dijit
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setLeftDecorationDijit($dijit)
+ {
+ $decoration = $this->getLeftDecoration();
+ $decoration['dijit'] = (string) $dijit;
+ $this->setDijitParam('leftDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set container to use with left decoration
+ *
+ * @param mixed $container
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setLeftDecorationContainer($container)
+ {
+ $decoration = $this->getLeftDecoration();
+ $decoration['container'] = (string) $container;
+ $this->setDijitParam('leftDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set labels to use with left decoration
+ *
+ * @param array $labels
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setLeftDecorationLabels(array $labels)
+ {
+ $decoration = $this->getLeftDecoration();
+ $decoration['labels'] = array_values($labels);
+ $this->setDijitParam('leftDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set params to use with left decoration
+ *
+ * @param array $params
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setLeftDecorationParams(array $params)
+ {
+ $decoration = $this->getLeftDecoration();
+ $decoration['params'] = $params;
+ $this->setDijitParam('leftDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set attribs to use with left decoration
+ *
+ * @param array $attribs
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setLeftDecorationAttribs(array $attribs)
+ {
+ $decoration = $this->getLeftDecoration();
+ $decoration['attribs'] = $attribs;
+ $this->setDijitParam('leftDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Get right decoration data
+ *
+ * @return array
+ */
+ public function getRightDecoration()
+ {
+ if ($this->hasDijitParam('rightDecoration')) {
+ return $this->getDijitParam('rightDecoration');
+ }
+ return array();
+ }
+
+ /**
+ * Set dijit to use with right decoration
+ *
+ * @param mixed $dijit
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setRightDecorationDijit($dijit)
+ {
+ $decoration = $this->getRightDecoration();
+ $decoration['dijit'] = (string) $dijit;
+ $this->setDijitParam('rightDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set container to use with right decoration
+ *
+ * @param mixed $container
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setRightDecorationContainer($container)
+ {
+ $decoration = $this->getRightDecoration();
+ $decoration['container'] = (string) $container;
+ $this->setDijitParam('rightDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set labels to use with right decoration
+ *
+ * @param array $labels
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setRightDecorationLabels(array $labels)
+ {
+ $decoration = $this->getRightDecoration();
+ $decoration['labels'] = array_values($labels);
+ $this->setDijitParam('rightDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set params to use with right decoration
+ *
+ * @param array $params
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setRightDecorationParams(array $params)
+ {
+ $decoration = $this->getRightDecoration();
+ $decoration['params'] = $params;
+ $this->setDijitParam('rightDecoration', $decoration);
+ return $this;
+ }
+
+ /**
+ * Set attribs to use with right decoration
+ *
+ * @param array $attribs
+ * @return Zend_Dojo_Form_Element_HorizontalSlider
+ */
+ public function setRightDecorationAttribs(array $attribs)
+ {
+ $decoration = $this->getRightDecoration();
+ $decoration['attribs'] = $attribs;
+ $this->setDijitParam('rightDecoration', $decoration);
+ return $this;
+ }
+}
diff --git a/library/Zend/Dojo/Form/SubForm.php b/library/Zend/Dojo/Form/SubForm.php
new file mode 100644
index 0000000..fbba437
--- /dev/null
+++ b/library/Zend/Dojo/Form/SubForm.php
@@ -0,0 +1,94 @@
+addPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator', 'decorator')
+ ->addPrefixPath('Zend_Dojo_Form_Element', 'Zend/Dojo/Form/Element', 'element')
+ ->addElementPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator', 'decorator')
+ ->addDisplayGroupPrefixPath('Zend_Dojo_Form_Decorator', 'Zend/Dojo/Form/Decorator')
+ ->setDefaultDisplayGroupClass('Zend_Dojo_Form_DisplayGroup');
+ parent::__construct($options);
+ }
+
+ /**
+ * Load the default decorators
+ *
+ * @return void
+ */
+ public function loadDefaultDecorators()
+ {
+ if ($this->loadDefaultDecoratorsIsDisabled()) {
+ return;
+ }
+
+ $decorators = $this->getDecorators();
+ if (empty($decorators)) {
+ $this->addDecorator('FormElements')
+ ->addDecorator('HtmlTag', array('tag' => 'dl'))
+ ->addDecorator('ContentPane');
+ }
+ }
+
+ /**
+ * Get view
+ *
+ * @return Zend_View_Interface
+ */
+ public function getView()
+ {
+ $view = parent::getView();
+ if (!$this->_dojoViewPathRegistered) {
+ if (false === $view->getPluginLoader('helper')->getPaths('Zend_Dojo_View_Helper')) {
+ $view->addHelperPath('Zend/Dojo/View/Helper', 'Zend_Dojo_View_Helper');
+ }
+ $this->_dojoViewPathRegistered = true;
+ }
+ return $view;
+ }
+}
diff --git a/library/Zend/Dojo/View/Exception.php b/library/Zend/Dojo/View/Exception.php
new file mode 100644
index 0000000..0bf71f3
--- /dev/null
+++ b/library/Zend/Dojo/View/Exception.php
@@ -0,0 +1,37 @@
+_createLayoutContainer($id, $content, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/AccordionPane.php b/library/Zend/Dojo/View/Helper/AccordionPane.php
new file mode 100644
index 0000000..15a115a
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/AccordionPane.php
@@ -0,0 +1,66 @@
+_createLayoutContainer($id, $content, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/BorderContainer.php b/library/Zend/Dojo/View/Helper/BorderContainer.php
new file mode 100644
index 0000000..f0e6a62
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/BorderContainer.php
@@ -0,0 +1,79 @@
+_styleIsRegistered) {
+ $this->view->headStyle()->appendStyle('html, body { height: 100%; width: 100%; margin: 0; padding: 0; }');
+ $this->_styleIsRegistered = true;
+ }
+
+ // and now we create it:
+ return $this->_createLayoutContainer($id, $content, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/Button.php b/library/Zend/Dojo/View/Helper/Button.php
new file mode 100644
index 0000000..0001872
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/Button.php
@@ -0,0 +1,68 @@
+_prepareDijit($attribs, $params, 'element');
+
+ return $this->view->formButton($id, $value, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/CheckBox.php b/library/Zend/Dojo/View/Helper/CheckBox.php
new file mode 100644
index 0000000..821163b
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/CheckBox.php
@@ -0,0 +1,100 @@
+_prepareDijit($attribs, $params, 'element');
+
+ // strip options so they don't show up in markup
+ if (array_key_exists('options', $attribs)) {
+ unset($attribs['options']);
+ }
+
+ // and now we create it:
+ $html = '';
+ if (!strstr($id, '[]')) {
+ // hidden element for unchecked value
+ $html .= $this->_renderHiddenElement($id, $checkboxInfo['uncheckedValue']);
+ }
+
+ // and final element
+ $html .= $this->_createFormElement($id, $checkboxInfo['checkedValue'], $params, $attribs);
+
+ return $html;
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/ComboBox.php b/library/Zend/Dojo/View/Helper/ComboBox.php
new file mode 100644
index 0000000..077f1ac
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/ComboBox.php
@@ -0,0 +1,155 @@
+_renderStore($params['store'], $id))) {
+ $params['store'] = $params['store']['store'];
+ if (is_string($store)) {
+ $html .= $store;
+ }
+ $html .= $this->_createFormElement($id, $value, $params, $attribs);
+ return $html;
+ }
+ unset($params['store']);
+ } elseif (array_key_exists('store', $params)) {
+ if (array_key_exists('storeType', $params)) {
+ $storeParams = array(
+ 'store' => $params['store'],
+ 'type' => $params['storeType'],
+ );
+ unset($params['storeType']);
+ if (array_key_exists('storeParams', $params)) {
+ $storeParams['params'] = $params['storeParams'];
+ unset($params['storeParams']);
+ }
+ if (false !== ($store = $this->_renderStore($storeParams, $id))) {
+ if (is_string($store)) {
+ $html .= $store;
+ }
+ }
+ }
+ $html .= $this->_createFormElement($id, $value, $params, $attribs);
+ return $html;
+ }
+
+ // required for correct type casting in declerative mode
+ if (isset($params['autocomplete'])) {
+ $params['autocomplete'] = ($params['autocomplete']) ? 'true' : 'false';
+ }
+ // do as normal select
+ $attribs = $this->_prepareDijit($attribs, $params, 'element');
+ return $this->view->formSelect($id, $value, $attribs, $options);
+ }
+
+ /**
+ * Render data store element
+ *
+ * Renders to dojo view helper
+ *
+ * @param array $params
+ * @return string|false
+ */
+ protected function _renderStore(array $params, $id)
+ {
+ if (!array_key_exists('store', $params) || !array_key_exists('type', $params)) {
+ return false;
+ }
+
+ $this->dojo->requireModule($params['type']);
+
+ $extraParams = array();
+ $storeParams = array(
+ 'dojoType' => $params['type'],
+ 'jsId' => $params['store'],
+ );
+
+ if (array_key_exists('params', $params)) {
+ $storeParams = array_merge($storeParams, $params['params']);
+ $extraParams = $params['params'];
+ }
+
+ if ($this->_useProgrammatic()) {
+ if (!$this->_useProgrammaticNoScript()) {
+ require_once 'Zend/Json.php';
+ $this->dojo->addJavascript('var ' . $storeParams['jsId'] . ";\n");
+ $js = $storeParams['jsId'] . ' = '
+ . 'new ' . $storeParams['dojoType'] . '('
+ . Zend_Json::encode($extraParams)
+ . ");\n";
+ $js = "function() {\n$js\n}";
+ $this->dojo->_addZendLoad($js);
+ }
+ return true;
+ }
+
+ return '_htmlAttribs($storeParams) . '>
';
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/ContentPane.php b/library/Zend/Dojo/View/Helper/ContentPane.php
new file mode 100644
index 0000000..6e15d17
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/ContentPane.php
@@ -0,0 +1,66 @@
+_createLayoutContainer($id, $content, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/CurrencyTextBox.php b/library/Zend/Dojo/View/Helper/CurrencyTextBox.php
new file mode 100644
index 0000000..79eb4c3
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/CurrencyTextBox.php
@@ -0,0 +1,68 @@
+_createFormElement($id, $value, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/CustomDijit.php b/library/Zend/Dojo/View/Helper/CustomDijit.php
new file mode 100644
index 0000000..d0c26d2
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/CustomDijit.php
@@ -0,0 +1,112 @@
+_defaultDojoType)
+ ) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception('No dojoType specified; cannot create dijit');
+ } elseif (array_key_exists('dojoType', $params)) {
+ $this->_dijit = $params['dojoType'];
+ $this->_module = $params['dojoType'];
+ unset($params['dojoType']);
+ } else {
+ $this->_dijit = $this->_defaultDojoType;
+ $this->_module = $this->_defaultDojoType;
+ }
+
+ if (array_key_exists('rootNode', $params)) {
+ $this->setRootNode($params['rootNode']);
+ unset($params['rootNode']);
+ }
+
+ return $this->_createLayoutContainer($id, $value, $params, $attribs);
+ }
+
+ /**
+ * Begin capturing content.
+ *
+ * Requires that either the {@link $_defaultDojotype} property is set, or
+ * that you pass a value to the "dojoType" key of the $params argument.
+ *
+ * @param string $id
+ * @param array $params
+ * @param array $attribs
+ * @return void
+ */
+ public function captureStart($id, array $params = array(), array $attribs = array())
+ {
+ if (!array_key_exists('dojoType', $params)
+ && (null === $this->_defaultDojoType)
+ ) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception('No dojoType specified; cannot create dijit');
+ } elseif (array_key_exists('dojoType', $params)) {
+ $this->_dijit = $params['dojoType'];
+ $this->_module = $params['dojoType'];
+ unset($params['dojoType']);
+ } else {
+ $this->_dijit = $this->_defaultDojoType;
+ $this->_module = $this->_defaultDojoType;
+ }
+
+ return parent::captureStart($id, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/DateTextBox.php b/library/Zend/Dojo/View/Helper/DateTextBox.php
new file mode 100644
index 0000000..2b383f9
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/DateTextBox.php
@@ -0,0 +1,68 @@
+_createFormElement($id, $value, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/Dijit.php b/library/Zend/Dojo/View/Helper/Dijit.php
new file mode 100644
index 0000000..bb89824
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/Dijit.php
@@ -0,0 +1,344 @@
+dojo = $this->view->dojo();
+ $this->dojo->enable();
+ return $this;
+ }
+
+
+ /**
+ * Get root node type
+ *
+ * @return string
+ */
+ public function getRootNode()
+ {
+ return $this->_rootNode;
+ }
+
+ /**
+ * Set root node type
+ *
+ * @param string $value
+ * @return Zend_Dojo_View_Helper_Dijit
+ */
+ public function setRootNode($value)
+ {
+ $this->_rootNode = $value;
+ return $this;
+ }
+
+ /**
+ * Whether or not to use declarative dijit creation
+ *
+ * @return bool
+ */
+ protected function _useDeclarative()
+ {
+ return Zend_Dojo_View_Helper_Dojo::useDeclarative();
+ }
+
+ /**
+ * Whether or not to use programmatic dijit creation
+ *
+ * @return bool
+ */
+ protected function _useProgrammatic()
+ {
+ return Zend_Dojo_View_Helper_Dojo::useProgrammatic();
+ }
+
+ /**
+ * Whether or not to use programmatic dijit creation w/o script creation
+ *
+ * @return bool
+ */
+ protected function _useProgrammaticNoScript()
+ {
+ return Zend_Dojo_View_Helper_Dojo::useProgrammaticNoScript();
+ }
+
+ /**
+ * Create a layout container
+ *
+ * @param int $id
+ * @param string $content
+ * @param array $params
+ * @param array $attribs
+ * @param string|null $dijit
+ * @return string
+ */
+ protected function _createLayoutContainer($id, $content, array $params, array $attribs, $dijit = null)
+ {
+ $attribs['id'] = $id;
+ $attribs = $this->_prepareDijit($attribs, $params, 'layout', $dijit);
+
+ $nodeType = $this->getRootNode();
+ $html = '<' . $nodeType . $this->_htmlAttribs($attribs) . '>'
+ . $content
+ . "$nodeType>\n";
+
+ return $html;
+ }
+
+ /**
+ * Create HTML representation of a dijit form element
+ *
+ * @param string $id
+ * @param string $value
+ * @param array $params
+ * @param array $attribs
+ * @param string|null $dijit
+ * @return string
+ */
+ public function _createFormElement($id, $value, array $params, array $attribs, $dijit = null)
+ {
+ if (!array_key_exists('id', $attribs)) {
+ $attribs['id'] = $id;
+ }
+ $attribs['name'] = $id;
+ $attribs['value'] = (string) $value;
+ $attribs['type'] = $this->_elementType;
+
+ $attribs = $this->_prepareDijit($attribs, $params, 'element', $dijit);
+
+ $html = '_htmlAttribs($attribs)
+ . $this->getClosingBracket();
+ return $html;
+ }
+
+ /**
+ * Merge attributes and parameters
+ *
+ * Also sets up requires
+ *
+ * @param array $attribs
+ * @param array $params
+ * @param string $type
+ * @param string $dijit Dijit type to use (otherwise, pull from $_dijit)
+ * @return array
+ */
+ protected function _prepareDijit(array $attribs, array $params, $type, $dijit = null)
+ {
+ $this->dojo->requireModule($this->_module);
+
+ switch ($type) {
+ case 'layout':
+ $stripParams = array('id');
+ break;
+ case 'element':
+ $stripParams = array('id', 'name', 'value', 'type');
+ foreach (array('checked', 'disabled', 'readonly') as $attrib) {
+ if (array_key_exists($attrib, $attribs)) {
+ if ($attribs[$attrib]) {
+ $attribs[$attrib] = $attrib;
+ } else {
+ unset($attribs[$attrib]);
+ }
+ }
+ }
+ break;
+ case 'textarea':
+ $stripParams = array('id', 'name', 'type', 'degrade');
+ break;
+ default:
+ }
+
+ foreach ($stripParams as $param) {
+ if (array_key_exists($param, $params)) {
+ unset($params[$param]);
+ }
+ }
+
+ // Normalize constraints, if present
+ foreach ($this->_jsonParams as $param) {
+ if (array_key_exists($param, $params)) {
+ require_once 'Zend/Json.php';
+
+ if (is_array($params[$param])) {
+ $values = array();
+ foreach ($params[$param] as $key => $value) {
+ if (!is_scalar($value)) {
+ continue;
+ }
+ $values[$key] = $value;
+ }
+ } elseif (is_string($params[$param])) {
+ $values = (array) $params[$param];
+ } else {
+ $values = array();
+ }
+ $values = Zend_Json::encode($values);
+ if ($this->_useDeclarative()) {
+ $values = str_replace('"', "'", $values);
+ }
+ $params[$param] = $values;
+ }
+ }
+
+ $dijit = (null === $dijit) ? $this->_dijit : $dijit;
+ if ($this->_useDeclarative()) {
+ $attribs = array_merge($attribs, $params);
+ if (isset($attribs['required'])) {
+ $attribs['required'] = ($attribs['required']) ? 'true' : 'false';
+ }
+ $attribs['dojoType'] = $dijit;
+ } elseif (!$this->_useProgrammaticNoScript()) {
+ $this->_createDijit($dijit, $attribs['id'], $params);
+ }
+
+ return $attribs;
+ }
+
+ /**
+ * Create a dijit programmatically
+ *
+ * @param string $dijit
+ * @param string $id
+ * @param array $params
+ * @return void
+ */
+ protected function _createDijit($dijit, $id, array $params)
+ {
+ $params['dojoType'] = $dijit;
+
+ array_walk_recursive($params, array($this, '_castBoolToString'));
+
+ $this->dojo->setDijit($id, $params);
+ }
+
+ /**
+ * Cast a boolean to a string value
+ *
+ * @param mixed $item
+ * @param string $key
+ * @return void
+ */
+ protected function _castBoolToString(&$item, $key)
+ {
+ if (!is_bool($item)) {
+ return;
+ }
+ $item = ($item) ? "true" : "false";
+ }
+
+ /**
+ * Render a hidden element to hold a value
+ *
+ * @param string $id
+ * @param string|int|float $value
+ * @return string
+ */
+ protected function _renderHiddenElement($id, $value)
+ {
+ $hiddenAttribs = array(
+ 'name' => $id,
+ 'value' => (string) $value,
+ 'type' => 'hidden',
+ );
+ return '_htmlAttribs($hiddenAttribs) . $this->getClosingBracket();
+ }
+
+ /**
+ * Create JS function for retrieving parent form
+ *
+ * @return void
+ */
+ protected function _createGetParentFormFunction()
+ {
+ $function =<<dojo->addJavascript($function);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/DijitContainer.php b/library/Zend/Dojo/View/Helper/DijitContainer.php
new file mode 100644
index 0000000..412a07c
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/DijitContainer.php
@@ -0,0 +1,92 @@
+_captureLock)) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception(sprintf('Lock already exists for id "%s"', $id));
+ }
+
+ $this->_captureLock[$id] = true;
+ $this->_captureInfo[$id] = array(
+ 'params' => $params,
+ 'attribs' => $attribs,
+ );
+
+ ob_start();
+ return;
+ }
+
+ /**
+ * Finish capturing content for layout container
+ *
+ * @param string $id
+ * @return string
+ */
+ public function captureEnd($id)
+ {
+ if (!array_key_exists($id, $this->_captureLock)) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception(sprintf('No capture lock exists for id "%s"; nothing to capture', $id));
+ }
+
+ $content = ob_get_clean();
+ extract($this->_captureInfo[$id]);
+ unset($this->_captureLock[$id], $this->_captureInfo[$id]);
+ return $this->_createLayoutContainer($id, $content, $params, $attribs);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/Dojo.php b/library/Zend/Dojo/View/Helper/Dojo.php
new file mode 100644
index 0000000..7ef0e87
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/Dojo.php
@@ -0,0 +1,176 @@
+_container = $registry[__CLASS__];
+ }
+
+ /**
+ * Set view object
+ *
+ * @param Zend_Dojo_View_Interface $view
+ * @return void
+ */
+ public function setView(Zend_View_Interface $view)
+ {
+ $this->view = $view;
+ $this->_container->setView($view);
+ }
+
+ /**
+ * Return dojo container
+ *
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function dojo()
+ {
+ return $this->_container;
+ }
+
+ /**
+ * Proxy to container methods
+ *
+ * @param string $method
+ * @param array $args
+ * @return mixed
+ * @throws Zend_Dojo_View_Exception For invalid method calls
+ */
+ public function __call($method, $args)
+ {
+ if (!method_exists($this->_container, $method)) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception(sprintf('Invalid method "%s" called on dojo view helper', $method));
+ }
+
+ return call_user_func_array(array($this->_container, $method), $args);
+ }
+
+ /**
+ * Set whether or not dijits should be created declaratively
+ *
+ * @return void
+ */
+ public static function setUseDeclarative()
+ {
+ self::$_useProgrammatic = false;
+ }
+
+ /**
+ * Set whether or not dijits should be created programmatically
+ *
+ * Optionally, specifiy whether or not dijit helpers should generate the
+ * programmatic dojo.
+ *
+ * @param int $style
+ * @return void
+ */
+ public static function setUseProgrammatic($style = self::PROGRAMMATIC_SCRIPT)
+ {
+ if (!in_array($style, array(self::PROGRAMMATIC_SCRIPT, self::PROGRAMMATIC_NOSCRIPT))) {
+ $style = self::PROGRAMMATIC_SCRIPT;
+ }
+ self::$_useProgrammatic = $style;
+ }
+
+ /**
+ * Should dijits be created declaratively?
+ *
+ * @return bool
+ */
+ public static function useDeclarative()
+ {
+ return (false === self::$_useProgrammatic);
+ }
+
+ /**
+ * Should dijits be created programmatically?
+ *
+ * @return bool
+ */
+ public static function useProgrammatic()
+ {
+ return (false !== self::$_useProgrammatic);
+ }
+
+ /**
+ * Should dijits be created programmatically but without scripts?
+ *
+ * @return bool
+ */
+ public static function useProgrammaticNoScript()
+ {
+ return (self::PROGRAMMATIC_NOSCRIPT === self::$_useProgrammatic);
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/Dojo/Container.php b/library/Zend/Dojo/View/Helper/Dojo/Container.php
new file mode 100644
index 0000000..2e7cf7a
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/Dojo/Container.php
@@ -0,0 +1,1205 @@
+view = $view;
+ }
+
+ /**
+ * Enable dojo
+ *
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function enable()
+ {
+ $this->_enabled = true;
+ return $this;
+ }
+
+ /**
+ * Disable dojo
+ *
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function disable()
+ {
+ $this->_enabled = false;
+ return $this;
+ }
+
+ /**
+ * Is dojo enabled?
+ *
+ * @return bool
+ */
+ public function isEnabled()
+ {
+ return $this->_enabled;
+ }
+
+ /**
+ * Add options for the Dojo Container to use
+ *
+ * @param array|Zend_Config Array or Zend_Config object with options to use
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setOptions($options)
+ {
+ if($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ }
+
+ foreach($options as $key => $value) {
+ $key = strtolower($key);
+ switch($key) {
+ case 'requiremodules':
+ $this->requireModule($value);
+ break;
+ case 'modulepaths':
+ foreach($value as $module => $path) {
+ $this->registerModulePath($module, $path);
+ }
+ break;
+ case 'layers':
+ $value = (array) $value;
+ foreach($value as $layer) {
+ $this->addLayer($layer);
+ }
+ break;
+ case 'cdnbase':
+ $this->setCdnBase($value);
+ break;
+ case 'cdnversion':
+ $this->setCdnVersion($value);
+ break;
+ case 'cdndojopath':
+ $this->setCdnDojoPath($value);
+ break;
+ case 'localpath':
+ $this->setLocalPath($value);
+ break;
+ case 'djconfig':
+ $this->setDjConfig($value);
+ break;
+ case 'stylesheetmodules':
+ $value = (array) $value;
+ foreach($value as $module) {
+ $this->addStylesheetModule($module);
+ }
+ break;
+ case 'stylesheets':
+ $value = (array) $value;
+ foreach($value as $stylesheet) {
+ $this->addStylesheet($stylesheet);
+ }
+ break;
+ case 'registerdojostylesheet':
+ $this->registerDojoStylesheet($value);
+ break;
+ case 'enable':
+ if($value) {
+ $this->enable();
+ } else {
+ $this->disable();
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Specify one or multiple modules to require
+ *
+ * @param string|array $modules
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function requireModule($modules)
+ {
+ if (!is_string($modules) && !is_array($modules)) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception('Invalid module name specified; must be a string or an array of strings');
+ }
+
+ $modules = (array) $modules;
+
+ foreach ($modules as $mod) {
+ if (!preg_match('/^[a-z][a-z0-9._-]+$/i', $mod)) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception(sprintf('Module name specified, "%s", contains invalid characters', (string) $mod));
+ }
+
+ if (!in_array($mod, $this->_modules)) {
+ $this->_modules[] = $mod;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Retrieve list of modules to require
+ *
+ * @return array
+ */
+ public function getModules()
+ {
+ return $this->_modules;
+ }
+
+ /**
+ * Register a module path
+ *
+ * @param string $module The module to register a path for
+ * @param string $path The path to register for the module
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function registerModulePath($module, $path)
+ {
+ $path = (string) $path;
+ if (!in_array($module, $this->_modulePaths)) {
+ $this->_modulePaths[$module] = $path;
+ }
+
+ return $this;
+ }
+
+ /**
+ * List registered module paths
+ *
+ * @return array
+ */
+ public function getModulePaths()
+ {
+ return $this->_modulePaths;
+ }
+
+ /**
+ * Add layer (custom build) path
+ *
+ * @param string $path
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function addLayer($path)
+ {
+ $path = (string) $path;
+ if (!in_array($path, $this->_layers)) {
+ $this->_layers[] = $path;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get registered layers
+ *
+ * @return array
+ */
+ public function getLayers()
+ {
+ return $this->_layers;
+ }
+
+ /**
+ * Remove a registered layer
+ *
+ * @param string $path
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function removeLayer($path)
+ {
+ $path = (string) $path;
+ $layers = array_flip($this->_layers);
+ if (array_key_exists($path, $layers)) {
+ unset($layers[$path]);
+ $this->_layers = array_keys($layers);
+ }
+ return $this;
+ }
+
+ /**
+ * Clear all registered layers
+ *
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function clearLayers()
+ {
+ $this->_layers = array();
+ return $this;
+ }
+
+ /**
+ * Set CDN base path
+ *
+ * @param string $url
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setCdnBase($url)
+ {
+ $this->_cdnBase = (string) $url;
+ return $this;
+ }
+
+ /**
+ * Return CDN base URL
+ *
+ * @return string
+ */
+ public function getCdnBase()
+ {
+ return $this->_cdnBase;
+ }
+
+ /**
+ * Use CDN, using version specified
+ *
+ * @param string $version
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setCdnVersion($version = null)
+ {
+ $this->enable();
+ if (preg_match('/^[1-9]\.[0-9](\.[0-9])?$/', $version)) {
+ $this->_cdnVersion = $version;
+ }
+ return $this;
+ }
+
+ /**
+ * Get CDN version
+ *
+ * @return string
+ */
+ public function getCdnVersion()
+ {
+ return $this->_cdnVersion;
+ }
+
+ /**
+ * Set CDN path to dojo (relative to CDN base + version)
+ *
+ * @param string $path
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setCdnDojoPath($path)
+ {
+ $this->_cdnDojoPath = (string) $path;
+ return $this;
+ }
+
+ /**
+ * Get CDN path to dojo (relative to CDN base + version)
+ *
+ * @return string
+ */
+ public function getCdnDojoPath()
+ {
+ return $this->_cdnDojoPath;
+ }
+
+ /**
+ * Are we using the CDN?
+ *
+ * @return bool
+ */
+ public function useCdn()
+ {
+ return !$this->useLocalPath();
+ }
+
+ /**
+ * Set path to local dojo
+ *
+ * @param string $path
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setLocalPath($path)
+ {
+ $this->enable();
+ $this->_localPath = (string) $path;
+ return $this;
+ }
+
+ /**
+ * Get local path to dojo
+ *
+ * @return string
+ */
+ public function getLocalPath()
+ {
+ return $this->_localPath;
+ }
+
+ /**
+ * Are we using a local path?
+ *
+ * @return bool
+ */
+ public function useLocalPath()
+ {
+ return (null === $this->_localPath) ? false : true;
+ }
+
+ /**
+ * Set Dojo configuration
+ *
+ * @param string $option
+ * @param mixed $value
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setDjConfig(array $config)
+ {
+ $this->_djConfig = $config;
+ return $this;
+ }
+
+ /**
+ * Set Dojo configuration option
+ *
+ * @param string $option
+ * @param mixed $value
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setDjConfigOption($option, $value)
+ {
+ $option = (string) $option;
+ $this->_djConfig[$option] = $value;
+ return $this;
+ }
+
+ /**
+ * Retrieve dojo configuration values
+ *
+ * @return array
+ */
+ public function getDjConfig()
+ {
+ return $this->_djConfig;
+ }
+
+ /**
+ * Get dojo configuration value
+ *
+ * @param string $option
+ * @param mixed $default
+ * @return mixed
+ */
+ public function getDjConfigOption($option, $default = null)
+ {
+ $option = (string) $option;
+ if (array_key_exists($option, $this->_djConfig)) {
+ return $this->_djConfig[$option];
+ }
+ return $default;
+ }
+
+ /**
+ * Add a stylesheet by module name
+ *
+ * @param string $module
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function addStylesheetModule($module)
+ {
+ if (!preg_match('/^[a-z0-9]+\.[a-z0-9_-]+(\.[a-z0-9_-]+)*$/i', $module)) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception('Invalid stylesheet module specified');
+ }
+ if (!in_array($module, $this->_stylesheetModules)) {
+ $this->_stylesheetModules[] = $module;
+ }
+ return $this;
+ }
+
+ /**
+ * Get all stylesheet modules currently registered
+ *
+ * @return array
+ */
+ public function getStylesheetModules()
+ {
+ return $this->_stylesheetModules;
+ }
+
+ /**
+ * Add a stylesheet
+ *
+ * @param string $path
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function addStylesheet($path)
+ {
+ $path = (string) $path;
+ if (!in_array($path, $this->_stylesheets)) {
+ $this->_stylesheets[] = (string) $path;
+ }
+ return $this;
+ }
+
+ /**
+ * Register the dojo.css stylesheet?
+ *
+ * With no arguments, returns the status of the flag; with arguments, sets
+ * the flag and returns the object.
+ *
+ * @param null|bool $flag
+ * @return Zend_Dojo_View_Helper_Dojo_Container|bool
+ */
+ public function registerDojoStylesheet($flag = null)
+ {
+ if (null === $flag) {
+ return $this->_registerDojoStylesheet;
+ }
+
+ $this->_registerDojoStylesheet = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Retrieve registered stylesheets
+ *
+ * @return array
+ */
+ public function getStylesheets()
+ {
+ return $this->_stylesheets;
+ }
+
+ /**
+ * Add a script to execute onLoad
+ *
+ * dojo.addOnLoad accepts:
+ * - function name
+ * - lambda
+ *
+ * @param string $callback Lambda
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function addOnLoad($callback)
+ {
+ if (!in_array($callback, $this->_onLoadActions, true)) {
+ $this->_onLoadActions[] = $callback;
+ }
+ return $this;
+ }
+
+ /**
+ * Prepend an onLoad event to the list of onLoad actions
+ *
+ * @param string $callback Lambda
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function prependOnLoad($callback)
+ {
+ if (!in_array($callback, $this->_onLoadActions, true)) {
+ array_unshift($this->_onLoadActions, $callback);
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve all registered onLoad actions
+ *
+ * @return array
+ */
+ public function getOnLoadActions()
+ {
+ return $this->_onLoadActions;
+ }
+
+ /**
+ * Start capturing routines to run onLoad
+ *
+ * @return bool
+ */
+ public function onLoadCaptureStart()
+ {
+ if ($this->_captureLock) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception('Cannot nest onLoad captures');
+ }
+
+ $this->_captureLock = true;
+ ob_start();
+ return;
+ }
+
+ /**
+ * Stop capturing routines to run onLoad
+ *
+ * @return bool
+ */
+ public function onLoadCaptureEnd()
+ {
+ $data = ob_get_clean();
+ $this->_captureLock = false;
+
+ $this->addOnLoad($data);
+ return true;
+ }
+
+ /**
+ * Add a programmatic dijit
+ *
+ * @param string $id
+ * @param array $params
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function addDijit($id, array $params)
+ {
+ if (array_key_exists($id, $this->_dijits)) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception(sprintf('Duplicate dijit with id "%s" already registered', $id));
+ }
+
+ $this->_dijits[$id] = array(
+ 'id' => $id,
+ 'params' => $params,
+ );
+
+ return $this;
+ }
+
+ /**
+ * Set a programmatic dijit (overwrites)
+ *
+ * @param string $id
+ * @param array $params
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setDijit($id, array $params)
+ {
+ $this->removeDijit($id);
+ return $this->addDijit($id, $params);
+ }
+
+ /**
+ * Add multiple dijits at once
+ *
+ * Expects an array of id => array $params pairs
+ *
+ * @param array $dijits
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function addDijits(array $dijits)
+ {
+ foreach ($dijits as $id => $params) {
+ $this->addDijit($id, $params);
+ }
+ return $this;
+ }
+
+ /**
+ * Set multiple dijits at once (overwrites)
+ *
+ * Expects an array of id => array $params pairs
+ *
+ * @param array $dijits
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function setDijits(array $dijits)
+ {
+ $this->clearDijits();
+ return $this->addDijits($dijits);
+ }
+
+ /**
+ * Is the given programmatic dijit already registered?
+ *
+ * @param string $id
+ * @return bool
+ */
+ public function hasDijit($id)
+ {
+ return array_key_exists($id, $this->_dijits);
+ }
+
+ /**
+ * Retrieve a dijit by id
+ *
+ * @param string $id
+ * @return array|null
+ */
+ public function getDijit($id)
+ {
+ if ($this->hasDijit($id)) {
+ return $this->_dijits[$id]['params'];
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve all dijits
+ *
+ * Returns dijits as an array of assoc arrays
+ *
+ * @return array
+ */
+ public function getDijits()
+ {
+ return array_values($this->_dijits);
+ }
+
+ /**
+ * Remove a programmatic dijit if it exists
+ *
+ * @param string $id
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function removeDijit($id)
+ {
+ if (array_key_exists($id, $this->_dijits)) {
+ unset($this->_dijits[$id]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Clear all dijits
+ *
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function clearDijits()
+ {
+ $this->_dijits = array();
+ return $this;
+ }
+
+ /**
+ * Render dijits as JSON structure
+ *
+ * @return string
+ */
+ public function dijitsToJson()
+ {
+ require_once 'Zend/Json.php';
+ return Zend_Json::encode($this->getDijits(), false, array('enableJsonExprFinder' => true));
+ }
+
+ /**
+ * Create dijit loader functionality
+ *
+ * @return void
+ */
+ public function registerDijitLoader()
+ {
+ if (!$this->_dijitLoaderRegistered) {
+ $js =<<requireModule('dojo.parser');
+ $this->_addZendLoad($js);
+ $this->addJavascript('var zendDijits = ' . $this->dijitsToJson() . ';');
+ $this->_dijitLoaderRegistered = true;
+ }
+ }
+
+ /**
+ * Add arbitrary javascript to execute in dojo JS container
+ *
+ * @param string $js
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function addJavascript($js)
+ {
+ $js = trim($js);
+ if (!in_array(substr($js, -1), array(';', '}'))) {
+ $js .= ';';
+ }
+
+ if (in_array($js, $this->_javascriptStatements)) {
+ return $this;
+ }
+
+ $this->_javascriptStatements[] = $js;
+ return $this;
+ }
+
+ /**
+ * Return all registered javascript statements
+ *
+ * @return array
+ */
+ public function getJavascript()
+ {
+ return $this->_javascriptStatements;
+ }
+
+ /**
+ * Clear arbitrary javascript stack
+ *
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function clearJavascript()
+ {
+ $this->_javascriptStatements = array();
+ return $this;
+ }
+
+ /**
+ * Capture arbitrary javascript to include in dojo script
+ *
+ * @return void
+ */
+ public function javascriptCaptureStart()
+ {
+ if ($this->_captureLock) {
+ require_once 'Zend/Dojo/View/Exception.php';
+ throw new Zend_Dojo_View_Exception('Cannot nest captures');
+ }
+
+ $this->_captureLock = true;
+ ob_start();
+ return;
+ }
+
+ /**
+ * Finish capturing arbitrary javascript to include in dojo script
+ *
+ * @return true
+ */
+ public function javascriptCaptureEnd()
+ {
+ $data = ob_get_clean();
+ $this->_captureLock = false;
+
+ $this->addJavascript($data);
+ return true;
+ }
+
+ /**
+ * String representation of dojo environment
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ if (!$this->isEnabled()) {
+ return '';
+ }
+
+ $this->_isXhtml = $this->view->doctype()->isXhtml();
+
+ if (Zend_Dojo_View_Helper_Dojo::useDeclarative()) {
+ if (null === $this->getDjConfigOption('parseOnLoad')) {
+ $this->setDjConfigOption('parseOnLoad', true);
+ }
+ }
+
+ if (!empty($this->_dijits)) {
+ $this->registerDijitLoader();
+ }
+
+ $html = $this->_renderStylesheets() . PHP_EOL
+ . $this->_renderDjConfig() . PHP_EOL
+ . $this->_renderDojoScriptTag() . PHP_EOL
+ . $this->_renderLayers() . PHP_EOL
+ . $this->_renderExtras();
+ return $html;
+ }
+
+ /**
+ * Retrieve local path to dojo resources for building relative paths
+ *
+ * @return string
+ */
+ protected function _getLocalRelativePath()
+ {
+ if (null === $this->_localRelativePath) {
+ $localPath = $this->getLocalPath();
+ $localPath = preg_replace('|[/\\\\]dojo[/\\\\]dojo.js[^/\\\\]*$|i', '', $localPath);
+ $this->_localRelativePath = $localPath;
+ }
+ return $this->_localRelativePath;
+ }
+
+ /**
+ * Render dojo stylesheets
+ *
+ * @return string
+ */
+ protected function _renderStylesheets()
+ {
+ if ($this->useCdn()) {
+ $base = $this->getCdnBase()
+ . $this->getCdnVersion();
+ } else {
+ $base = $this->_getLocalRelativePath();
+ }
+
+ $registeredStylesheets = $this->getStylesheetModules();
+ foreach ($registeredStylesheets as $stylesheet) {
+ $themeName = substr($stylesheet, strrpos($stylesheet, '.') + 1);
+ $stylesheet = str_replace('.', '/', $stylesheet);
+ $stylesheets[] = $base . '/' . $stylesheet . '/' . $themeName . '.css';
+ }
+
+ foreach ($this->getStylesheets() as $stylesheet) {
+ $stylesheets[] = $stylesheet;
+ }
+
+ if ($this->_registerDojoStylesheet) {
+ $stylesheets[] = $base . '/dojo/resources/dojo.css';
+ }
+
+ if (empty($stylesheets)) {
+ return '';
+ }
+
+ array_reverse($stylesheets);
+ $style = '';
+
+ return $style;
+ }
+
+ /**
+ * Render DjConfig values
+ *
+ * @return string
+ */
+ protected function _renderDjConfig()
+ {
+ $djConfigValues = $this->getDjConfig();
+ if (empty($djConfigValues)) {
+ return '';
+ }
+
+ require_once 'Zend/Json.php';
+ $scriptTag = '';
+
+ return $scriptTag;
+ }
+
+ /**
+ * Render dojo script tag
+ *
+ * Renders Dojo script tag by utilizing either local path provided or the
+ * CDN. If any djConfig values were set, they will be serialized and passed
+ * with that attribute.
+ *
+ * @return string
+ */
+ protected function _renderDojoScriptTag()
+ {
+ if ($this->useCdn()) {
+ $source = $this->getCdnBase()
+ . $this->getCdnVersion()
+ . $this->getCdnDojoPath();
+ } else {
+ $source = $this->getLocalPath();
+ }
+
+ $scriptTag = '';
+ return $scriptTag;
+ }
+
+ /**
+ * Render layers (custom builds) as script tags
+ *
+ * @return string
+ */
+ protected function _renderLayers()
+ {
+ $layers = $this->getLayers();
+ if (empty($layers)) {
+ return '';
+ }
+
+ $enc = 'UTF-8';
+ if ($this->view instanceof Zend_View_Interface
+ && method_exists($this->view, 'getEncoding')
+ ) {
+ $enc = $this->view->getEncoding();
+ }
+
+ $html = array();
+ foreach ($layers as $path) {
+ $html[] = sprintf(
+ '',
+ htmlspecialchars($path, ENT_QUOTES, $enc)
+ );
+ }
+
+ return implode("\n", $html);
+ }
+
+ /**
+ * Render dojo module paths and requires
+ *
+ * @return string
+ */
+ protected function _renderExtras()
+ {
+ $js = array();
+ $modulePaths = $this->getModulePaths();
+ if (!empty($modulePaths)) {
+ foreach ($modulePaths as $module => $path) {
+ $js[] = 'dojo.registerModulePath("' . $this->view->escape($module) . '", "' . $this->view->escape($path) . '");';
+ }
+ }
+
+ $modules = $this->getModules();
+ if (!empty($modules)) {
+ foreach ($modules as $module) {
+ $js[] = 'dojo.require("' . $this->view->escape($module) . '");';
+ }
+ }
+
+ $onLoadActions = array();
+ // Get Zend specific onLoad actions; these will always be first to
+ // ensure that dijits are created in the correct order
+ foreach ($this->_getZendLoadActions() as $callback) {
+ $onLoadActions[] = 'dojo.addOnLoad(' . $callback . ');';
+ }
+
+ // Get all other onLoad actions
+ foreach ($this->getOnLoadActions() as $callback) {
+ $onLoadActions[] = 'dojo.addOnLoad(' . $callback . ');';
+ }
+
+ $javascript = implode("\n ", $this->getJavascript());
+
+ $content = '';
+ if (!empty($js)) {
+ $content .= implode("\n ", $js) . "\n";
+ }
+
+ if (!empty($onLoadActions)) {
+ $content .= implode("\n ", $onLoadActions) . "\n";
+ }
+
+ if (!empty($javascript)) {
+ $content .= $javascript . "\n";
+ }
+
+ if (preg_match('/^\s*$/s', $content)) {
+ return '';
+ }
+
+ $html = '';
+ return $html;
+ }
+
+ /**
+ * Add an onLoad action related to ZF dijit creation
+ *
+ * This method is public, but prefixed with an underscore to indicate that
+ * it should not normally be called by userland code. It is pertinent to
+ * ensuring that the correct order of operations occurs during dijit
+ * creation.
+ *
+ * @param string $callback
+ * @return Zend_Dojo_View_Helper_Dojo_Container
+ */
+ public function _addZendLoad($callback)
+ {
+ if (!in_array($callback, $this->_zendLoadActions, true)) {
+ $this->_zendLoadActions[] = $callback;
+ }
+ return $this;
+ }
+
+ /**
+ * Retrieve all ZF dijit callbacks
+ *
+ * @return array
+ */
+ public function _getZendLoadActions()
+ {
+ return $this->_zendLoadActions;
+ }
+}
diff --git a/library/Zend/Dojo/View/Helper/Editor.php b/library/Zend/Dojo/View/Helper/Editor.php
new file mode 100644
index 0000000..e40cf8d
--- /dev/null
+++ b/library/Zend/Dojo/View/Helper/Editor.php
@@ -0,0 +1,199 @@
+ 'LinkDialog',
+ 'insertImage' => 'LinkDialog',
+ 'fontName' => 'FontChoice',
+ 'fontSize' => 'FontChoice',
+ 'formatBlock' => 'FontChoice',
+ 'foreColor' => 'TextColor',
+ 'hiliteColor' => 'TextColor',
+ 'enterKeyHandling' => 'EnterKeyHandling',
+ 'fullScreen' => 'FullScreen',
+ 'newPage' => 'NewPage',
+ 'print' => 'Print',
+ 'tabIndent' => 'TabIndent',
+ 'toggleDir' => 'ToggleDir',
+ 'viewSource' => 'ViewSource'
+ );
+
+ /**
+ * JSON-encoded parameters
+ * @var array
+ */
+ protected $_jsonParams = array('captureEvents', 'events', 'plugins', 'extraPlugins');
+
+ /**
+ * dijit.Editor
+ *
+ * @param string $id
+ * @param string $value
+ * @param array $params
+ * @param array $attribs
+ * @return string
+ */
+ public function editor($id, $value = null, $params = array(), $attribs = array())
+ {
+ if (isset($params['plugins'])) {
+ foreach ($this->_getRequiredModules($params['plugins']) as $module) {
+ $this->dojo->requireModule($module);
+ }
+ }
+
+ // Previous versions allowed specifying "degrade" to allow using a
+ // textarea instead of a div -- but this is insecure. Removing the
+ // parameter if set to prevent its injection in the dijit.
+ if (isset($params['degrade'])) {
+ unset($params['degrade']);
+ }
+
+ $hiddenName = $id;
+ if (array_key_exists('id', $attribs)) {
+ $hiddenId = $attribs['id'];
+ } else {
+ $hiddenId = $hiddenName;
+ }
+ $hiddenId = $this->_normalizeId($hiddenId);
+
+ $textareaName = $this->_normalizeEditorName($hiddenName);
+ $textareaId = $hiddenId . '-Editor';
+
+ $hiddenAttribs = array(
+ 'id' => $hiddenId,
+ 'name' => $hiddenName,
+ 'value' => $value,
+ 'type' => 'hidden',
+ );
+ $attribs['id'] = $textareaId;
+
+ $this->_createGetParentFormFunction();
+ $this->_createEditorOnSubmit($hiddenId, $textareaId);
+
+ $attribs = $this->_prepareDijit($attribs, $params, 'textarea');
+
+ $html = '_htmlAttribs($attribs) . '>'
+ . $value
+ . "
\n";
+
+ // Embed a textarea in a