File indexing completed on 2024-06-16 05:29:59

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Db
0017  * @subpackage Table
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Db_Adapter_Abstract
0025  */
0026 // require_once 'Zend/Db/Adapter/Abstract.php';
0027 
0028 /**
0029  * @see Zend_Db_Adapter_Abstract
0030  */
0031 // require_once 'Zend/Db/Select.php';
0032 
0033 /**
0034  * @see Zend_Db
0035  */
0036 // require_once 'Zend/Db.php';
0037 
0038 /**
0039  * Class for SQL table interface.
0040  *
0041  * @category   Zend
0042  * @package    Zend_Db
0043  * @subpackage Table
0044  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0045  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0046  */
0047 abstract class Zend_Db_Table_Abstract
0048 {
0049 
0050     const ADAPTER          = 'db';
0051     const DEFINITION        = 'definition';
0052     const DEFINITION_CONFIG_NAME = 'definitionConfigName';
0053     const SCHEMA           = 'schema';
0054     const NAME             = 'name';
0055     const PRIMARY          = 'primary';
0056     const COLS             = 'cols';
0057     const METADATA         = 'metadata';
0058     const METADATA_CACHE   = 'metadataCache';
0059     const METADATA_CACHE_IN_CLASS = 'metadataCacheInClass';
0060     const ROW_CLASS        = 'rowClass';
0061     const ROWSET_CLASS     = 'rowsetClass';
0062     const REFERENCE_MAP    = 'referenceMap';
0063     const DEPENDENT_TABLES = 'dependentTables';
0064     const SEQUENCE         = 'sequence';
0065 
0066     const COLUMNS          = 'columns';
0067     const REF_TABLE_CLASS  = 'refTableClass';
0068     const REF_COLUMNS      = 'refColumns';
0069     const ON_DELETE        = 'onDelete';
0070     const ON_UPDATE        = 'onUpdate';
0071 
0072     const CASCADE          = 'cascade';
0073     const CASCADE_RECURSE  = 'cascadeRecurse';
0074     const RESTRICT         = 'restrict';
0075     const SET_NULL         = 'setNull';
0076 
0077     const DEFAULT_NONE     = 'defaultNone';
0078     const DEFAULT_CLASS    = 'defaultClass';
0079     const DEFAULT_DB       = 'defaultDb';
0080 
0081     const SELECT_WITH_FROM_PART    = true;
0082     const SELECT_WITHOUT_FROM_PART = false;
0083 
0084     /**
0085      * Default Zend_Db_Adapter_Abstract object.
0086      *
0087      * @var Zend_Db_Adapter_Abstract
0088      */
0089     protected static $_defaultDb;
0090 
0091     /**
0092      * Optional Zend_Db_Table_Definition object
0093      *
0094      * @var unknown_type
0095      */
0096     protected $_definition = null;
0097 
0098     /**
0099      * Optional definition config name used in concrete implementation
0100      *
0101      * @var string
0102      */
0103     protected $_definitionConfigName = null;
0104 
0105     /**
0106      * Default cache for information provided by the adapter's describeTable() method.
0107      *
0108      * @var Zend_Cache_Core
0109      */
0110     protected static $_defaultMetadataCache = null;
0111 
0112     /**
0113      * Zend_Db_Adapter_Abstract object.
0114      *
0115      * @var Zend_Db_Adapter_Abstract
0116      */
0117     protected $_db;
0118 
0119     /**
0120      * The schema name (default null means current schema)
0121      *
0122      * @var array
0123      */
0124     protected $_schema = null;
0125 
0126     /**
0127      * The table name.
0128      *
0129      * @var string
0130      */
0131     protected $_name = null;
0132 
0133     /**
0134      * The table column names derived from Zend_Db_Adapter_Abstract::describeTable().
0135      *
0136      * @var array
0137      */
0138     protected $_cols;
0139 
0140     /**
0141      * The primary key column or columns.
0142      * A compound key should be declared as an array.
0143      * You may declare a single-column primary key
0144      * as a string.
0145      *
0146      * @var mixed
0147      */
0148     protected $_primary = null;
0149 
0150     /**
0151      * If your primary key is a compound key, and one of the columns uses
0152      * an auto-increment or sequence-generated value, set _identity
0153      * to the ordinal index in the $_primary array for that column.
0154      * Note this index is the position of the column in the primary key,
0155      * not the position of the column in the table.  The primary key
0156      * array is 1-based.
0157      *
0158      * @var integer
0159      */
0160     protected $_identity = 1;
0161 
0162     /**
0163      * Define the logic for new values in the primary key.
0164      * May be a string, boolean true, or boolean false.
0165      *
0166      * @var mixed
0167      */
0168     protected $_sequence = true;
0169 
0170     /**
0171      * Information provided by the adapter's describeTable() method.
0172      *
0173      * @var array
0174      */
0175     protected $_metadata = array();
0176 
0177     /**
0178      * Cache for information provided by the adapter's describeTable() method.
0179      *
0180      * @var Zend_Cache_Core
0181      */
0182     protected $_metadataCache = null;
0183 
0184     /**
0185      * Flag: whether or not to cache metadata in the class
0186      * @var bool
0187      */
0188     protected $_metadataCacheInClass = true;
0189 
0190     /**
0191      * Classname for row
0192      *
0193      * @var string
0194      */
0195     protected $_rowClass = 'Zend_Db_Table_Row';
0196 
0197     /**
0198      * Classname for rowset
0199      *
0200      * @var string
0201      */
0202     protected $_rowsetClass = 'Zend_Db_Table_Rowset';
0203 
0204     /**
0205      * Associative array map of declarative referential integrity rules.
0206      * This array has one entry per foreign key in the current table.
0207      * Each key is a mnemonic name for one reference rule.
0208      *
0209      * Each value is also an associative array, with the following keys:
0210      * - columns       = array of names of column(s) in the child table.
0211      * - refTableClass = class name of the parent table.
0212      * - refColumns    = array of names of column(s) in the parent table,
0213      *                   in the same order as those in the 'columns' entry.
0214      * - onDelete      = "cascade" means that a delete in the parent table also
0215      *                   causes a delete of referencing rows in the child table.
0216      * - onUpdate      = "cascade" means that an update of primary key values in
0217      *                   the parent table also causes an update of referencing
0218      *                   rows in the child table.
0219      *
0220      * @var array
0221      */
0222     protected $_referenceMap = array();
0223 
0224     /**
0225      * Simple array of class names of tables that are "children" of the current
0226      * table, in other words tables that contain a foreign key to this one.
0227      * Array elements are not table names; they are class names of classes that
0228      * extend Zend_Db_Table_Abstract.
0229      *
0230      * @var array
0231      */
0232     protected $_dependentTables = array();
0233 
0234 
0235     protected $_defaultSource = self::DEFAULT_NONE;
0236     protected $_defaultValues = array();
0237 
0238     /**
0239      * Constructor.
0240      *
0241      * Supported params for $config are:
0242      * - db              = user-supplied instance of database connector,
0243      *                     or key name of registry instance.
0244      * - name            = table name.
0245      * - primary         = string or array of primary key(s).
0246      * - rowClass        = row class name.
0247      * - rowsetClass     = rowset class name.
0248      * - referenceMap    = array structure to declare relationship
0249      *                     to parent tables.
0250      * - dependentTables = array of child tables.
0251      * - metadataCache   = cache for information from adapter describeTable().
0252      *
0253      * @param  mixed $config Array of user-specified config options, or just the Db Adapter.
0254      * @return void
0255      */
0256     public function __construct($config = array())
0257     {
0258         /**
0259          * Allow a scalar argument to be the Adapter object or Registry key.
0260          */
0261         if (!is_array($config)) {
0262             $config = array(self::ADAPTER => $config);
0263         }
0264 
0265         if ($config) {
0266             $this->setOptions($config);
0267         }
0268 
0269         $this->_setup();
0270         $this->init();
0271     }
0272 
0273     /**
0274      * setOptions()
0275      *
0276      * @param array $options
0277      * @return Zend_Db_Table_Abstract
0278      */
0279     public function setOptions(Array $options)
0280     {
0281         foreach ($options as $key => $value) {
0282             switch ($key) {
0283                 case self::ADAPTER:
0284                     $this->_setAdapter($value);
0285                     break;
0286                 case self::DEFINITION:
0287                     $this->setDefinition($value);
0288                     break;
0289                 case self::DEFINITION_CONFIG_NAME:
0290                     $this->setDefinitionConfigName($value);
0291                     break;
0292                 case self::SCHEMA:
0293                     $this->_schema = (string) $value;
0294                     break;
0295                 case self::NAME:
0296                     $this->_name = (string) $value;
0297                     break;
0298                 case self::PRIMARY:
0299                     $this->_primary = (array) $value;
0300                     break;
0301                 case self::ROW_CLASS:
0302                     $this->setRowClass($value);
0303                     break;
0304                 case self::ROWSET_CLASS:
0305                     $this->setRowsetClass($value);
0306                     break;
0307                 case self::REFERENCE_MAP:
0308                     $this->setReferences($value);
0309                     break;
0310                 case self::DEPENDENT_TABLES:
0311                     $this->setDependentTables($value);
0312                     break;
0313                 case self::METADATA_CACHE:
0314                     $this->_setMetadataCache($value);
0315                     break;
0316                 case self::METADATA_CACHE_IN_CLASS:
0317                     $this->setMetadataCacheInClass($value);
0318                     break;
0319                 case self::SEQUENCE:
0320                     $this->_setSequence($value);
0321                     break;
0322                 default:
0323                     // ignore unrecognized configuration directive
0324                     break;
0325             }
0326         }
0327 
0328         return $this;
0329     }
0330 
0331     /**
0332      * setDefinition()
0333      *
0334      * @param Zend_Db_Table_Definition $definition
0335      * @return Zend_Db_Table_Abstract
0336      */
0337     public function setDefinition(Zend_Db_Table_Definition $definition)
0338     {
0339         $this->_definition = $definition;
0340         return $this;
0341     }
0342 
0343     /**
0344      * getDefinition()
0345      *
0346      * @return Zend_Db_Table_Definition|null
0347      */
0348     public function getDefinition()
0349     {
0350         return $this->_definition;
0351     }
0352 
0353     /**
0354      * setDefinitionConfigName()
0355      *
0356      * @param string $definition
0357      * @return Zend_Db_Table_Abstract
0358      */
0359     public function setDefinitionConfigName($definitionConfigName)
0360     {
0361         $this->_definitionConfigName = $definitionConfigName;
0362         return $this;
0363     }
0364 
0365     /**
0366      * getDefinitionConfigName()
0367      *
0368      * @return string
0369      */
0370     public function getDefinitionConfigName()
0371     {
0372         return $this->_definitionConfigName;
0373     }
0374 
0375     /**
0376      * @param  string $classname
0377      * @return Zend_Db_Table_Abstract Provides a fluent interface
0378      */
0379     public function setRowClass($classname)
0380     {
0381         $this->_rowClass = (string) $classname;
0382 
0383         return $this;
0384     }
0385 
0386     /**
0387      * @return string
0388      */
0389     public function getRowClass()
0390     {
0391         return $this->_rowClass;
0392     }
0393 
0394     /**
0395      * @param  string $classname
0396      * @return Zend_Db_Table_Abstract Provides a fluent interface
0397      */
0398     public function setRowsetClass($classname)
0399     {
0400         $this->_rowsetClass = (string) $classname;
0401 
0402         return $this;
0403     }
0404 
0405     /**
0406      * @return string
0407      */
0408     public function getRowsetClass()
0409     {
0410         return $this->_rowsetClass;
0411     }
0412 
0413     /**
0414      * Add a reference to the reference map
0415      *
0416      * @param string $ruleKey
0417      * @param string|array $columns
0418      * @param string $refTableClass
0419      * @param string|array $refColumns
0420      * @param string $onDelete
0421      * @param string $onUpdate
0422      * @return Zend_Db_Table_Abstract
0423      */
0424     public function addReference($ruleKey, $columns, $refTableClass, $refColumns,
0425                                  $onDelete = null, $onUpdate = null)
0426     {
0427         $reference = array(self::COLUMNS         => (array) $columns,
0428                            self::REF_TABLE_CLASS => $refTableClass,
0429                            self::REF_COLUMNS     => (array) $refColumns);
0430 
0431         if (!empty($onDelete)) {
0432             $reference[self::ON_DELETE] = $onDelete;
0433         }
0434 
0435         if (!empty($onUpdate)) {
0436             $reference[self::ON_UPDATE] = $onUpdate;
0437         }
0438 
0439         $this->_referenceMap[$ruleKey] = $reference;
0440 
0441         return $this;
0442     }
0443 
0444     /**
0445      * @param array $referenceMap
0446      * @return Zend_Db_Table_Abstract Provides a fluent interface
0447      */
0448     public function setReferences(array $referenceMap)
0449     {
0450         $this->_referenceMap = $referenceMap;
0451 
0452         return $this;
0453     }
0454 
0455     /**
0456      * @param string $tableClassname
0457      * @param string $ruleKey OPTIONAL
0458      * @return array
0459      * @throws Zend_Db_Table_Exception
0460      */
0461     public function getReference($tableClassname, $ruleKey = null)
0462     {
0463         $thisClass = get_class($this);
0464         if ($thisClass === 'Zend_Db_Table') {
0465             $thisClass = $this->_definitionConfigName;
0466         }
0467         $refMap = $this->_getReferenceMapNormalized();
0468         if ($ruleKey !== null) {
0469             if (!isset($refMap[$ruleKey])) {
0470                 // require_once "Zend/Db/Table/Exception.php";
0471                 throw new Zend_Db_Table_Exception("No reference rule \"$ruleKey\" from table $thisClass to table $tableClassname");
0472             }
0473             if ($refMap[$ruleKey][self::REF_TABLE_CLASS] != $tableClassname) {
0474                 // require_once "Zend/Db/Table/Exception.php";
0475                 throw new Zend_Db_Table_Exception("Reference rule \"$ruleKey\" does not reference table $tableClassname");
0476             }
0477             return $refMap[$ruleKey];
0478         }
0479         foreach ($refMap as $reference) {
0480             if ($reference[self::REF_TABLE_CLASS] == $tableClassname) {
0481                 return $reference;
0482             }
0483         }
0484         // require_once "Zend/Db/Table/Exception.php";
0485         throw new Zend_Db_Table_Exception("No reference from table $thisClass to table $tableClassname");
0486     }
0487 
0488     /**
0489      * @param  array $dependentTables
0490      * @return Zend_Db_Table_Abstract Provides a fluent interface
0491      */
0492     public function setDependentTables(array $dependentTables)
0493     {
0494         $this->_dependentTables = $dependentTables;
0495 
0496         return $this;
0497     }
0498 
0499     /**
0500      * @return array
0501      */
0502     public function getDependentTables()
0503     {
0504         return $this->_dependentTables;
0505     }
0506 
0507     /**
0508      * set the defaultSource property - this tells the table class where to find default values
0509      *
0510      * @param string $defaultSource
0511      * @return Zend_Db_Table_Abstract
0512      */
0513     public function setDefaultSource($defaultSource = self::DEFAULT_NONE)
0514     {
0515         if (!in_array($defaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE))) {
0516             $defaultSource = self::DEFAULT_NONE;
0517         }
0518 
0519         $this->_defaultSource = $defaultSource;
0520         return $this;
0521     }
0522 
0523     /**
0524      * returns the default source flag that determines where defaultSources come from
0525      *
0526      * @return unknown
0527      */
0528     public function getDefaultSource()
0529     {
0530         return $this->_defaultSource;
0531     }
0532 
0533     /**
0534      * set the default values for the table class
0535      *
0536      * @param array $defaultValues
0537      * @return Zend_Db_Table_Abstract
0538      */
0539     public function setDefaultValues(Array $defaultValues)
0540     {
0541         foreach ($defaultValues as $defaultName => $defaultValue) {
0542             if (array_key_exists($defaultName, $this->_metadata)) {
0543                 $this->_defaultValues[$defaultName] = $defaultValue;
0544             }
0545         }
0546         return $this;
0547     }
0548 
0549     public function getDefaultValues()
0550     {
0551         return $this->_defaultValues;
0552     }
0553 
0554 
0555     /**
0556      * Sets the default Zend_Db_Adapter_Abstract for all Zend_Db_Table objects.
0557      *
0558      * @param  mixed $db Either an Adapter object, or a string naming a Registry key
0559      * @return void
0560      */
0561     public static function setDefaultAdapter($db = null)
0562     {
0563         self::$_defaultDb = self::_setupAdapter($db);
0564     }
0565 
0566     /**
0567      * Gets the default Zend_Db_Adapter_Abstract for all Zend_Db_Table objects.
0568      *
0569      * @return Zend_Db_Adapter_Abstract or null
0570      */
0571     public static function getDefaultAdapter()
0572     {
0573         return self::$_defaultDb;
0574     }
0575 
0576     /**
0577      * @param  mixed $db Either an Adapter object, or a string naming a Registry key
0578      * @return Zend_Db_Table_Abstract Provides a fluent interface
0579      */
0580     protected function _setAdapter($db)
0581     {
0582         $this->_db = self::_setupAdapter($db);
0583         return $this;
0584     }
0585 
0586     /**
0587      * Gets the Zend_Db_Adapter_Abstract for this particular Zend_Db_Table object.
0588      *
0589      * @return Zend_Db_Adapter_Abstract
0590      */
0591     public function getAdapter()
0592     {
0593         return $this->_db;
0594     }
0595 
0596     /**
0597      * @param  mixed $db Either an Adapter object, or a string naming a Registry key
0598      * @return Zend_Db_Adapter_Abstract
0599      * @throws Zend_Db_Table_Exception
0600      */
0601     protected static function _setupAdapter($db)
0602     {
0603         if ($db === null) {
0604             return null;
0605         }
0606         if (is_string($db)) {
0607             // require_once 'Zend/Registry.php';
0608             $db = Zend_Registry::get($db);
0609         }
0610         if (!$db instanceof Zend_Db_Adapter_Abstract) {
0611             // require_once 'Zend/Db/Table/Exception.php';
0612             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');
0613         }
0614         return $db;
0615     }
0616 
0617     /**
0618      * Sets the default metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
0619      *
0620      * If $defaultMetadataCache is null, then no metadata cache is used by default.
0621      *
0622      * @param  mixed $metadataCache Either a Cache object, or a string naming a Registry key
0623      * @return void
0624      */
0625     public static function setDefaultMetadataCache($metadataCache = null)
0626     {
0627         self::$_defaultMetadataCache = self::_setupMetadataCache($metadataCache);
0628     }
0629 
0630     /**
0631      * Gets the default metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
0632      *
0633      * @return Zend_Cache_Core or null
0634      */
0635     public static function getDefaultMetadataCache()
0636     {
0637         return self::$_defaultMetadataCache;
0638     }
0639 
0640     /**
0641      * Sets the metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
0642      *
0643      * If $metadataCache is null, then no metadata cache is used. Since there is no opportunity to reload metadata
0644      * after instantiation, this method need not be public, particularly because that it would have no effect
0645      * results in unnecessary API complexity. To configure the metadata cache, use the metadataCache configuration
0646      * option for the class constructor upon instantiation.
0647      *
0648      * @param  mixed $metadataCache Either a Cache object, or a string naming a Registry key
0649      * @return Zend_Db_Table_Abstract Provides a fluent interface
0650      */
0651     protected function _setMetadataCache($metadataCache)
0652     {
0653         $this->_metadataCache = self::_setupMetadataCache($metadataCache);
0654         return $this;
0655     }
0656 
0657     /**
0658      * Gets the metadata cache for information returned by Zend_Db_Adapter_Abstract::describeTable().
0659      *
0660      * @return Zend_Cache_Core or null
0661      */
0662     public function getMetadataCache()
0663     {
0664         return $this->_metadataCache;
0665     }
0666 
0667     /**
0668      * Indicate whether metadata should be cached in the class for the duration
0669      * of the instance
0670      *
0671      * @param  bool $flag
0672      * @return Zend_Db_Table_Abstract
0673      */
0674     public function setMetadataCacheInClass($flag)
0675     {
0676         $this->_metadataCacheInClass = (bool) $flag;
0677         return $this;
0678     }
0679 
0680     /**
0681      * Retrieve flag indicating if metadata should be cached for duration of
0682      * instance
0683      *
0684      * @return bool
0685      */
0686     public function metadataCacheInClass()
0687     {
0688         return $this->_metadataCacheInClass;
0689     }
0690 
0691     /**
0692      * @param mixed $metadataCache Either a Cache object, or a string naming a Registry key
0693      * @return Zend_Cache_Core
0694      * @throws Zend_Db_Table_Exception
0695      */
0696     protected static function _setupMetadataCache($metadataCache)
0697     {
0698         if ($metadataCache === null) {
0699             return null;
0700         }
0701         if (is_string($metadataCache)) {
0702             // require_once 'Zend/Registry.php';
0703             $metadataCache = Zend_Registry::get($metadataCache);
0704         }
0705         if (!$metadataCache instanceof Zend_Cache_Core) {
0706             // require_once 'Zend/Db/Table/Exception.php';
0707             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');
0708         }
0709         return $metadataCache;
0710     }
0711 
0712     /**
0713      * Sets the sequence member, which defines the behavior for generating
0714      * primary key values in new rows.
0715      * - If this is a string, then the string names the sequence object.
0716      * - If this is boolean true, then the key uses an auto-incrementing
0717      *   or identity mechanism.
0718      * - If this is boolean false, then the key is user-defined.
0719      *   Use this for natural keys, for example.
0720      *
0721      * @param mixed $sequence
0722      * @return Zend_Db_Table_Adapter_Abstract Provides a fluent interface
0723      */
0724     protected function _setSequence($sequence)
0725     {
0726         $this->_sequence = $sequence;
0727 
0728         return $this;
0729     }
0730 
0731     /**
0732      * Turnkey for initialization of a table object.
0733      * Calls other protected methods for individual tasks, to make it easier
0734      * for a subclass to override part of the setup logic.
0735      *
0736      * @return void
0737      */
0738     protected function _setup()
0739     {
0740         $this->_setupDatabaseAdapter();
0741         $this->_setupTableName();
0742     }
0743 
0744     /**
0745      * Initialize database adapter.
0746      *
0747      * @return void
0748      * @throws Zend_Db_Table_Exception
0749      */
0750     protected function _setupDatabaseAdapter()
0751     {
0752         if (! $this->_db) {
0753             $this->_db = self::getDefaultAdapter();
0754             if (!$this->_db instanceof Zend_Db_Adapter_Abstract) {
0755                 // require_once 'Zend/Db/Table/Exception.php';
0756                 throw new Zend_Db_Table_Exception('No adapter found for ' . get_class($this));
0757             }
0758         }
0759     }
0760 
0761     /**
0762      * Initialize table and schema names.
0763      *
0764      * If the table name is not set in the class definition,
0765      * use the class name itself as the table name.
0766      *
0767      * A schema name provided with the table name (e.g., "schema.table") overrides
0768      * any existing value for $this->_schema.
0769      *
0770      * @return void
0771      */
0772     protected function _setupTableName()
0773     {
0774         if (! $this->_name) {
0775             $this->_name = get_class($this);
0776         } else if (strpos($this->_name, '.')) {
0777             list($this->_schema, $this->_name) = explode('.', $this->_name);
0778         }
0779     }
0780 
0781     /**
0782      * Initializes metadata.
0783      *
0784      * If metadata cannot be loaded from cache, adapter's describeTable() method is called to discover metadata
0785      * information. Returns true if and only if the metadata are loaded from cache.
0786      *
0787      * @return boolean
0788      * @throws Zend_Db_Table_Exception
0789      */
0790     protected function _setupMetadata()
0791     {
0792         if ($this->metadataCacheInClass() && (count($this->_metadata) > 0)) {
0793             return true;
0794         }
0795 
0796         // Assume that metadata will be loaded from cache
0797         $isMetadataFromCache = true;
0798 
0799         // If $this has no metadata cache but the class has a default metadata cache
0800         if (null === $this->_metadataCache && null !== self::$_defaultMetadataCache) {
0801             // Make $this use the default metadata cache of the class
0802             $this->_setMetadataCache(self::$_defaultMetadataCache);
0803         }
0804 
0805         // If $this has a metadata cache
0806         if (null !== $this->_metadataCache) {
0807             // Define the cache identifier where the metadata are saved
0808 
0809             //get db configuration
0810             $dbConfig = $this->_db->getConfig();
0811 
0812             $port = isset($dbConfig['options']['port'])
0813                   ? ':'.$dbConfig['options']['port']
0814                   : (isset($dbConfig['port'])
0815                   ? ':'.$dbConfig['port']
0816                   : null);
0817 
0818             $host = isset($dbConfig['options']['host'])
0819                   ? ':'.$dbConfig['options']['host']
0820                   : (isset($dbConfig['host'])
0821                   ? ':'.$dbConfig['host']
0822                   : null);
0823 
0824             // Define the cache identifier where the metadata are saved
0825             $cacheId = md5( // port:host/dbname:schema.table (based on availabilty)
0826                     $port . $host . '/'. $dbConfig['dbname'] . ':'
0827                   . $this->_schema. '.' . $this->_name
0828             );
0829         }
0830 
0831         // If $this has no metadata cache or metadata cache misses
0832         if (null === $this->_metadataCache || !($metadata = $this->_metadataCache->load($cacheId))) {
0833             // Metadata are not loaded from cache
0834             $isMetadataFromCache = false;
0835             // Fetch metadata from the adapter's describeTable() method
0836             $metadata = $this->_db->describeTable($this->_name, $this->_schema);
0837             // If $this has a metadata cache, then cache the metadata
0838             if (null !== $this->_metadataCache && !$this->_metadataCache->save($metadata, $cacheId)) {
0839                 trigger_error('Failed saving metadata to metadataCache', E_USER_NOTICE);
0840             }
0841         }
0842 
0843         // Assign the metadata to $this
0844         $this->_metadata = $metadata;
0845 
0846         // Return whether the metadata were loaded from cache
0847         return $isMetadataFromCache;
0848     }
0849 
0850     /**
0851      * Retrieve table columns
0852      *
0853      * @return array
0854      */
0855     protected function _getCols()
0856     {
0857         if (null === $this->_cols) {
0858             $this->_setupMetadata();
0859             $this->_cols = array_keys($this->_metadata);
0860         }
0861         return $this->_cols;
0862     }
0863 
0864     /**
0865      * Initialize primary key from metadata.
0866      * If $_primary is not defined, discover primary keys
0867      * from the information returned by describeTable().
0868      *
0869      * @return void
0870      * @throws Zend_Db_Table_Exception
0871      */
0872     protected function _setupPrimaryKey()
0873     {
0874         if (!$this->_primary) {
0875             $this->_setupMetadata();
0876             $this->_primary = array();
0877             foreach ($this->_metadata as $col) {
0878                 if ($col['PRIMARY']) {
0879                     $this->_primary[ $col['PRIMARY_POSITION'] ] = $col['COLUMN_NAME'];
0880                     if ($col['IDENTITY']) {
0881                         $this->_identity = $col['PRIMARY_POSITION'];
0882                     }
0883                 }
0884             }
0885             // if no primary key was specified and none was found in the metadata
0886             // then throw an exception.
0887             if (empty($this->_primary)) {
0888                 // require_once 'Zend/Db/Table/Exception.php';
0889                 throw new Zend_Db_Table_Exception("A table must have a primary key, but none was found for table '{$this->_name}'");
0890             }
0891         } else if (!is_array($this->_primary)) {
0892             $this->_primary = array(1 => $this->_primary);
0893         } else if (isset($this->_primary[0])) {
0894             array_unshift($this->_primary, null);
0895             unset($this->_primary[0]);
0896         }
0897 
0898         $cols = $this->_getCols();
0899         if (! array_intersect((array) $this->_primary, $cols) == (array) $this->_primary) {
0900             // require_once 'Zend/Db/Table/Exception.php';
0901             throw new Zend_Db_Table_Exception("Primary key column(s) ("
0902                 . implode(',', (array) $this->_primary)
0903                 . ") are not columns in this table ("
0904                 . implode(',', $cols)
0905                 . ")");
0906         }
0907 
0908         $primary    = (array) $this->_primary;
0909         $pkIdentity = $primary[(int) $this->_identity];
0910 
0911         /**
0912          * Special case for PostgreSQL: a SERIAL key implicitly uses a sequence
0913          * object whose name is "<table>_<column>_seq".
0914          */
0915         if ($this->_sequence === true && $this->_db instanceof Zend_Db_Adapter_Pdo_Pgsql) {
0916             $this->_sequence = $this->_db->quoteIdentifier("{$this->_name}_{$pkIdentity}_seq");
0917             if ($this->_schema) {
0918                 $this->_sequence = $this->_db->quoteIdentifier($this->_schema) . '.' . $this->_sequence;
0919             }
0920         }
0921     }
0922 
0923     /**
0924      * Returns a normalized version of the reference map
0925      *
0926      * @return array
0927      */
0928     protected function _getReferenceMapNormalized()
0929     {
0930         $referenceMapNormalized = array();
0931 
0932         foreach ($this->_referenceMap as $rule => $map) {
0933 
0934             $referenceMapNormalized[$rule] = array();
0935 
0936             foreach ($map as $key => $value) {
0937                 switch ($key) {
0938 
0939                     // normalize COLUMNS and REF_COLUMNS to arrays
0940                     case self::COLUMNS:
0941                     case self::REF_COLUMNS:
0942                         if (!is_array($value)) {
0943                             $referenceMapNormalized[$rule][$key] = array($value);
0944                         } else {
0945                             $referenceMapNormalized[$rule][$key] = $value;
0946                         }
0947                         break;
0948 
0949                     // other values are copied as-is
0950                     default:
0951                         $referenceMapNormalized[$rule][$key] = $value;
0952                         break;
0953                 }
0954             }
0955         }
0956 
0957         return $referenceMapNormalized;
0958     }
0959 
0960     /**
0961      * Initialize object
0962      *
0963      * Called from {@link __construct()} as final step of object instantiation.
0964      *
0965      * @return void
0966      */
0967     public function init()
0968     {
0969     }
0970 
0971     /**
0972      * Returns table information.
0973      *
0974      * You can elect to return only a part of this information by supplying its key name,
0975      * otherwise all information is returned as an array.
0976      *
0977      * @param  string $key The specific info part to return OPTIONAL
0978      * @return mixed
0979      * @throws Zend_Db_Table_Exception
0980      */
0981     public function info($key = null)
0982     {
0983         $this->_setupPrimaryKey();
0984 
0985         $info = array(
0986             self::SCHEMA           => $this->_schema,
0987             self::NAME             => $this->_name,
0988             self::COLS             => $this->_getCols(),
0989             self::PRIMARY          => (array) $this->_primary,
0990             self::METADATA         => $this->_metadata,
0991             self::ROW_CLASS        => $this->getRowClass(),
0992             self::ROWSET_CLASS     => $this->getRowsetClass(),
0993             self::REFERENCE_MAP    => $this->_referenceMap,
0994             self::DEPENDENT_TABLES => $this->_dependentTables,
0995             self::SEQUENCE         => $this->_sequence
0996         );
0997 
0998         if ($key === null) {
0999             return $info;
1000         }
1001 
1002         if (!array_key_exists($key, $info)) {
1003             // require_once 'Zend/Db/Table/Exception.php';
1004             throw new Zend_Db_Table_Exception('There is no table information for the key "' . $key . '"');
1005         }
1006 
1007         return $info[$key];
1008     }
1009 
1010     /**
1011      * Returns an instance of a Zend_Db_Table_Select object.
1012      *
1013      * @param bool $withFromPart Whether or not to include the from part of the select based on the table
1014      * @return Zend_Db_Table_Select
1015      */
1016     public function select($withFromPart = self::SELECT_WITHOUT_FROM_PART)
1017     {
1018         // require_once 'Zend/Db/Table/Select.php';
1019         $select = new Zend_Db_Table_Select($this);
1020         if ($withFromPart == self::SELECT_WITH_FROM_PART) {
1021             $select->from($this->info(self::NAME), Zend_Db_Table_Select::SQL_WILDCARD, $this->info(self::SCHEMA));
1022         }
1023         return $select;
1024     }
1025 
1026     /**
1027      * Inserts a new row.
1028      *
1029      * @param  array  $data  Column-value pairs.
1030      * @return mixed         The primary key of the row inserted.
1031      */
1032     public function insert(array $data)
1033     {
1034         $this->_setupPrimaryKey();
1035 
1036         /**
1037          * Zend_Db_Table assumes that if you have a compound primary key
1038          * and one of the columns in the key uses a sequence,
1039          * it's the _first_ column in the compound key.
1040          */
1041         $primary = (array) $this->_primary;
1042         $pkIdentity = $primary[(int)$this->_identity];
1043 
1044 
1045         /**
1046          * If the primary key can be generated automatically, and no value was
1047          * specified in the user-supplied data, then omit it from the tuple.
1048          *
1049          * Note: this checks for sensible values in the supplied primary key
1050          * position of the data.  The following values are considered empty:
1051          *   null, false, true, '', array()
1052          */
1053         if (array_key_exists($pkIdentity, $data)) {
1054             if ($data[$pkIdentity] === null                                        // null
1055                 || $data[$pkIdentity] === ''                                       // empty string
1056                 || is_bool($data[$pkIdentity])                                     // boolean
1057                 || (is_array($data[$pkIdentity]) && empty($data[$pkIdentity]))) {  // empty array
1058                 unset($data[$pkIdentity]);
1059             }
1060         }
1061 
1062         /**
1063          * If this table uses a database sequence object and the data does not
1064          * specify a value, then get the next ID from the sequence and add it
1065          * to the row.  We assume that only the first column in a compound
1066          * primary key takes a value from a sequence.
1067          */
1068         if (is_string($this->_sequence) && !isset($data[$pkIdentity])) {
1069             $data[$pkIdentity] = $this->_db->nextSequenceId($this->_sequence);
1070         }
1071 
1072         /**
1073          * INSERT the new row.
1074          */
1075         $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
1076         $this->_db->insert($tableSpec, $data);
1077 
1078         /**
1079          * Fetch the most recent ID generated by an auto-increment
1080          * or IDENTITY column, unless the user has specified a value,
1081          * overriding the auto-increment mechanism.
1082          */
1083         if ($this->_sequence === true && !isset($data[$pkIdentity])) {
1084             $data[$pkIdentity] = $this->_db->lastInsertId();
1085         }
1086 
1087         /**
1088          * Return the primary key value if the PK is a single column,
1089          * else return an associative array of the PK column/value pairs.
1090          */
1091         $pkData = array_intersect_key($data, array_flip($primary));
1092         if (count($primary) == 1) {
1093             reset($pkData);
1094             return current($pkData);
1095         }
1096 
1097         return $pkData;
1098     }
1099 
1100     /**
1101      * Check if the provided column is an identity of the table
1102      *
1103      * @param  string $column
1104      * @throws Zend_Db_Table_Exception
1105      * @return boolean
1106      */
1107     public function isIdentity($column)
1108     {
1109         $this->_setupPrimaryKey();
1110 
1111         if (!isset($this->_metadata[$column])) {
1112             /**
1113              * @see Zend_Db_Table_Exception
1114              */
1115             // require_once 'Zend/Db/Table/Exception.php';
1116 
1117             throw new Zend_Db_Table_Exception('Column "' . $column . '" not found in table.');
1118         }
1119 
1120         return (bool) $this->_metadata[$column]['IDENTITY'];
1121     }
1122 
1123     /**
1124      * Updates existing rows.
1125      *
1126      * @param  array        $data  Column-value pairs.
1127      * @param  array|string $where An SQL WHERE clause, or an array of SQL WHERE clauses.
1128      * @return int          The number of rows updated.
1129      */
1130     public function update(array $data, $where)
1131     {
1132         $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
1133         return $this->_db->update($tableSpec, $data, $where);
1134     }
1135 
1136     /**
1137      * Called by a row object for the parent table's class during save() method.
1138      *
1139      * @param  string $parentTableClassname
1140      * @param  array  $oldPrimaryKey
1141      * @param  array  $newPrimaryKey
1142      * @return int
1143      */
1144     public function _cascadeUpdate($parentTableClassname, array $oldPrimaryKey, array $newPrimaryKey)
1145     {
1146         $this->_setupMetadata();
1147         $rowsAffected = 0;
1148         foreach ($this->_getReferenceMapNormalized() as $map) {
1149             if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_UPDATE])) {
1150                 switch ($map[self::ON_UPDATE]) {
1151                     case self::CASCADE:
1152                         $newRefs = array();
1153                         $where = array();
1154                         for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
1155                             $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
1156                             $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
1157                             if (array_key_exists($refCol, $newPrimaryKey)) {
1158                                 $newRefs[$col] = $newPrimaryKey[$refCol];
1159                             }
1160                             $type = $this->_metadata[$col]['DATA_TYPE'];
1161                             $where[] = $this->_db->quoteInto(
1162                                 $this->_db->quoteIdentifier($col, true) . ' = ?',
1163                                 $oldPrimaryKey[$refCol], $type);
1164                         }
1165                         $rowsAffected += $this->update($newRefs, $where);
1166                         break;
1167                     default:
1168                         // no action
1169                         break;
1170                 }
1171             }
1172         }
1173         return $rowsAffected;
1174     }
1175 
1176     /**
1177      * Deletes existing rows.
1178      *
1179      * @param  array|string $where SQL WHERE clause(s).
1180      * @return int          The number of rows deleted.
1181      */
1182     public function delete($where)
1183     {
1184         $depTables = $this->getDependentTables();
1185         if (!empty($depTables)) {
1186             $resultSet = $this->fetchAll($where);
1187             if (count($resultSet) > 0 ) {
1188                 foreach ($resultSet as $row) {
1189                     /**
1190                      * Execute cascading deletes against dependent tables
1191                      */
1192                     foreach ($depTables as $tableClass) {
1193                         $t = self::getTableFromString($tableClass, $this);
1194                         $t->_cascadeDelete(
1195                             get_class($this), $row->getPrimaryKey()
1196                         );
1197                     }
1198                 }
1199             }
1200         }
1201 
1202         $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
1203         return $this->_db->delete($tableSpec, $where);
1204     }
1205 
1206     /**
1207      * Called by parent table's class during delete() method.
1208      *
1209      * @param  string $parentTableClassname
1210      * @param  array  $primaryKey
1211      * @return int    Number of affected rows
1212      */
1213     public function _cascadeDelete($parentTableClassname, array $primaryKey)
1214     {
1215         // setup metadata
1216         $this->_setupMetadata();
1217 
1218         // get this class name
1219         $thisClass = get_class($this);
1220         if ($thisClass === 'Zend_Db_Table') {
1221             $thisClass = $this->_definitionConfigName;
1222         }
1223 
1224         $rowsAffected = 0;
1225 
1226         foreach ($this->_getReferenceMapNormalized() as $map) {
1227             if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_DELETE])) {
1228 
1229                 $where = array();
1230 
1231                 // CASCADE or CASCADE_RECURSE
1232                 if (in_array($map[self::ON_DELETE], array(self::CASCADE, self::CASCADE_RECURSE))) {
1233                     for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
1234                         $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
1235                         $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
1236                         $type = $this->_metadata[$col]['DATA_TYPE'];
1237                         $where[] = $this->_db->quoteInto(
1238                             $this->_db->quoteIdentifier($col, true) . ' = ?',
1239                             $primaryKey[$refCol], $type);
1240                     }
1241                 }
1242 
1243                 // CASCADE_RECURSE
1244                 if ($map[self::ON_DELETE] == self::CASCADE_RECURSE) {
1245 
1246                     /**
1247                      * Execute cascading deletes against dependent tables
1248                      */
1249                     $depTables = $this->getDependentTables();
1250                     if (!empty($depTables)) {
1251                         foreach ($depTables as $tableClass) {
1252                             $t = self::getTableFromString($tableClass, $this);
1253                             foreach ($this->fetchAll($where) as $depRow) {
1254                                 $rowsAffected += $t->_cascadeDelete($thisClass, $depRow->getPrimaryKey());
1255                             }
1256                         }
1257                     }
1258                 }
1259 
1260                 // CASCADE or CASCADE_RECURSE
1261                 if (in_array($map[self::ON_DELETE], array(self::CASCADE, self::CASCADE_RECURSE))) {
1262                     $rowsAffected += $this->delete($where);
1263                 }
1264 
1265             }
1266         }
1267         return $rowsAffected;
1268     }
1269 
1270     /**
1271      * Fetches rows by primary key.  The argument specifies one or more primary
1272      * key value(s).  To find multiple rows by primary key, the argument must
1273      * be an array.
1274      *
1275      * This method accepts a variable number of arguments.  If the table has a
1276      * multi-column primary key, the number of arguments must be the same as
1277      * the number of columns in the primary key.  To find multiple rows in a
1278      * table with a multi-column primary key, each argument must be an array
1279      * with the same number of elements.
1280      *
1281      * The find() method always returns a Rowset object, even if only one row
1282      * was found.
1283      *
1284      * @param  mixed $key The value(s) of the primary keys.
1285      * @return Zend_Db_Table_Rowset_Abstract Row(s) matching the criteria.
1286      * @throws Zend_Db_Table_Exception
1287      */
1288     public function find()
1289     {
1290         $this->_setupPrimaryKey();
1291         $args = func_get_args();
1292         $keyNames = array_values((array) $this->_primary);
1293 
1294         if (count($args) < count($keyNames)) {
1295             // require_once 'Zend/Db/Table/Exception.php';
1296             throw new Zend_Db_Table_Exception("Too few columns for the primary key");
1297         }
1298 
1299         if (count($args) > count($keyNames)) {
1300             // require_once 'Zend/Db/Table/Exception.php';
1301             throw new Zend_Db_Table_Exception("Too many columns for the primary key");
1302         }
1303 
1304         $whereList = array();
1305         $numberTerms = 0;
1306         foreach ($args as $keyPosition => $keyValues) {
1307             $keyValuesCount = count($keyValues);
1308             // Coerce the values to an array.
1309             // Don't simply typecast to array, because the values
1310             // might be Zend_Db_Expr objects.
1311             if (!is_array($keyValues)) {
1312                 $keyValues = array($keyValues);
1313             }
1314             if ($numberTerms == 0) {
1315                 $numberTerms = $keyValuesCount;
1316             } else if ($keyValuesCount != $numberTerms) {
1317                 // require_once 'Zend/Db/Table/Exception.php';
1318                 throw new Zend_Db_Table_Exception("Missing value(s) for the primary key");
1319             }
1320             $keyValues = array_values($keyValues);
1321             for ($i = 0; $i < $keyValuesCount; ++$i) {
1322                 if (!isset($whereList[$i])) {
1323                     $whereList[$i] = array();
1324                 }
1325                 $whereList[$i][$keyPosition] = $keyValues[$i];
1326             }
1327         }
1328 
1329         $whereClause = null;
1330         if (count($whereList)) {
1331             $whereOrTerms = array();
1332             $tableName = $this->_db->quoteTableAs($this->_name, null, true);
1333             foreach ($whereList as $keyValueSets) {
1334                 $whereAndTerms = array();
1335                 foreach ($keyValueSets as $keyPosition => $keyValue) {
1336                     $type = $this->_metadata[$keyNames[$keyPosition]]['DATA_TYPE'];
1337                     $columnName = $this->_db->quoteIdentifier($keyNames[$keyPosition], true);
1338                     $whereAndTerms[] = $this->_db->quoteInto(
1339                         $tableName . '.' . $columnName . ' = ?',
1340                         $keyValue, $type);
1341                 }
1342                 $whereOrTerms[] = '(' . implode(' AND ', $whereAndTerms) . ')';
1343             }
1344             $whereClause = '(' . implode(' OR ', $whereOrTerms) . ')';
1345         }
1346 
1347         // issue ZF-5775 (empty where clause should return empty rowset)
1348         if ($whereClause == null) {
1349             $rowsetClass = $this->getRowsetClass();
1350             if (!class_exists($rowsetClass)) {
1351                 // require_once 'Zend/Loader.php';
1352                 Zend_Loader::loadClass($rowsetClass);
1353             }
1354             return new $rowsetClass(array('table' => $this, 'rowClass' => $this->getRowClass(), 'stored' => true));
1355         }
1356 
1357         return $this->fetchAll($whereClause);
1358     }
1359 
1360     /**
1361      * Fetches all rows.
1362      *
1363      * Honors the Zend_Db_Adapter fetch mode.
1364      *
1365      * @param string|array|Zend_Db_Table_Select $where  OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
1366      * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
1367      * @param int                               $count  OPTIONAL An SQL LIMIT count.
1368      * @param int                               $offset OPTIONAL An SQL LIMIT offset.
1369      * @return Zend_Db_Table_Rowset_Abstract The row results per the Zend_Db_Adapter fetch mode.
1370      */
1371     public function fetchAll($where = null, $order = null, $count = null, $offset = null)
1372     {
1373         if (!($where instanceof Zend_Db_Table_Select)) {
1374             $select = $this->select();
1375 
1376             if ($where !== null) {
1377                 $this->_where($select, $where);
1378             }
1379 
1380             if ($order !== null) {
1381                 $this->_order($select, $order);
1382             }
1383 
1384             if ($count !== null || $offset !== null) {
1385                 $select->limit($count, $offset);
1386             }
1387 
1388         } else {
1389             $select = $where;
1390         }
1391 
1392         $rows = $this->_fetch($select);
1393 
1394         $data  = array(
1395             'table'    => $this,
1396             'data'     => $rows,
1397             'readOnly' => $select->isReadOnly(),
1398             'rowClass' => $this->getRowClass(),
1399             'stored'   => true
1400         );
1401 
1402         $rowsetClass = $this->getRowsetClass();
1403         if (!class_exists($rowsetClass)) {
1404             // require_once 'Zend/Loader.php';
1405             Zend_Loader::loadClass($rowsetClass);
1406         }
1407         return new $rowsetClass($data);
1408     }
1409 
1410     /**
1411      * Fetches one row in an object of type Zend_Db_Table_Row_Abstract,
1412      * or returns null if no row matches the specified criteria.
1413      *
1414      * @param string|array|Zend_Db_Table_Select $where  OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
1415      * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
1416      * @param int                               $offset OPTIONAL An SQL OFFSET value.
1417      * @return Zend_Db_Table_Row_Abstract|null The row results per the
1418      *     Zend_Db_Adapter fetch mode, or null if no row found.
1419      */
1420     public function fetchRow($where = null, $order = null, $offset = null)
1421     {
1422         if (!($where instanceof Zend_Db_Table_Select)) {
1423             $select = $this->select();
1424 
1425             if ($where !== null) {
1426                 $this->_where($select, $where);
1427             }
1428 
1429             if ($order !== null) {
1430                 $this->_order($select, $order);
1431             }
1432 
1433             $select->limit(1, ((is_numeric($offset)) ? (int) $offset : null));
1434 
1435         } else {
1436             $select = $where->limit(1, $where->getPart(Zend_Db_Select::LIMIT_OFFSET));
1437         }
1438 
1439         $rows = $this->_fetch($select);
1440 
1441         if (count($rows) == 0) {
1442             return null;
1443         }
1444 
1445         $data = array(
1446             'table'   => $this,
1447             'data'     => $rows[0],
1448             'readOnly' => $select->isReadOnly(),
1449             'stored'  => true
1450         );
1451 
1452         $rowClass = $this->getRowClass();
1453         if (!class_exists($rowClass)) {
1454             // require_once 'Zend/Loader.php';
1455             Zend_Loader::loadClass($rowClass);
1456         }
1457         return new $rowClass($data);
1458     }
1459 
1460     /**
1461      * Fetches a new blank row (not from the database).
1462      *
1463      * @return Zend_Db_Table_Row_Abstract
1464      * @deprecated since 0.9.3 - use createRow() instead.
1465      */
1466     public function fetchNew()
1467     {
1468         return $this->createRow();
1469     }
1470 
1471     /**
1472      * Fetches a new blank row (not from the database).
1473      *
1474      * @param  array $data OPTIONAL data to populate in the new row.
1475      * @param  string $defaultSource OPTIONAL flag to force default values into new row
1476      * @return Zend_Db_Table_Row_Abstract
1477      */
1478     public function createRow(array $data = array(), $defaultSource = null)
1479     {
1480         $cols     = $this->_getCols();
1481         $defaults = array_combine($cols, array_fill(0, count($cols), null));
1482 
1483         // nothing provided at call-time, take the class value
1484         if ($defaultSource == null) {
1485             $defaultSource = $this->_defaultSource;
1486         }
1487 
1488         if (!in_array($defaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE))) {
1489             $defaultSource = self::DEFAULT_NONE;
1490         }
1491 
1492         if ($defaultSource == self::DEFAULT_DB) {
1493             foreach ($this->_metadata as $metadataName => $metadata) {
1494                 if (($metadata['DEFAULT'] != null) &&
1495                     ($metadata['NULLABLE'] !== true || ($metadata['NULLABLE'] === true && isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === true)) &&
1496                     (!(isset($this->_defaultValues[$metadataName]) && $this->_defaultValues[$metadataName] === false))) {
1497                     $defaults[$metadataName] = $metadata['DEFAULT'];
1498                 }
1499             }
1500         } elseif ($defaultSource == self::DEFAULT_CLASS && $this->_defaultValues) {
1501             foreach ($this->_defaultValues as $defaultName => $defaultValue) {
1502                 if (array_key_exists($defaultName, $defaults)) {
1503                     $defaults[$defaultName] = $defaultValue;
1504                 }
1505             }
1506         }
1507 
1508         $config = array(
1509             'table'    => $this,
1510             'data'     => $defaults,
1511             'readOnly' => false,
1512             'stored'   => false
1513         );
1514 
1515         $rowClass = $this->getRowClass();
1516         if (!class_exists($rowClass)) {
1517             // require_once 'Zend/Loader.php';
1518             Zend_Loader::loadClass($rowClass);
1519         }
1520         $row = new $rowClass($config);
1521         $row->setFromArray($data);
1522         return $row;
1523     }
1524 
1525     /**
1526      * Generate WHERE clause from user-supplied string or array
1527      *
1528      * @param  string|array $where  OPTIONAL An SQL WHERE clause.
1529      * @return Zend_Db_Table_Select
1530      */
1531     protected function _where(Zend_Db_Table_Select $select, $where)
1532     {
1533         $where = (array) $where;
1534 
1535         foreach ($where as $key => $val) {
1536             // is $key an int?
1537             if (is_int($key)) {
1538                 // $val is the full condition
1539                 $select->where($val);
1540             } else {
1541                 // $key is the condition with placeholder,
1542                 // and $val is quoted into the condition
1543                 $select->where($key, $val);
1544             }
1545         }
1546 
1547         return $select;
1548     }
1549 
1550     /**
1551      * Generate ORDER clause from user-supplied string or array
1552      *
1553      * @param  string|array $order  OPTIONAL An SQL ORDER clause.
1554      * @return Zend_Db_Table_Select
1555      */
1556     protected function _order(Zend_Db_Table_Select $select, $order)
1557     {
1558         if (!is_array($order)) {
1559             $order = array($order);
1560         }
1561 
1562         foreach ($order as $val) {
1563             $select->order($val);
1564         }
1565 
1566         return $select;
1567     }
1568 
1569     /**
1570      * Support method for fetching rows.
1571      *
1572      * @param  Zend_Db_Table_Select $select  query options.
1573      * @return array An array containing the row results in FETCH_ASSOC mode.
1574      */
1575     protected function _fetch(Zend_Db_Table_Select $select)
1576     {
1577         $stmt = $this->_db->query($select);
1578         $data = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
1579         return $data;
1580     }
1581 
1582     /**
1583      * Get table gateway object from string
1584      *
1585      * @param  string                 $tableName
1586      * @param  Zend_Db_Table_Abstract $referenceTable
1587      * @throws Zend_Db_Table_Row_Exception
1588      * @return Zend_Db_Table_Abstract
1589      */
1590     public static function getTableFromString($tableName, Zend_Db_Table_Abstract $referenceTable = null)
1591     {
1592         if ($referenceTable instanceof Zend_Db_Table_Abstract) {
1593             $tableDefinition = $referenceTable->getDefinition();
1594 
1595             if ($tableDefinition !== null && $tableDefinition->hasTableConfig($tableName)) {
1596                 return new Zend_Db_Table($tableName, $tableDefinition);
1597             }
1598         }
1599 
1600         // assume the tableName is the class name
1601         if (!class_exists($tableName)) {
1602             try {
1603                 // require_once 'Zend/Loader.php';
1604                 Zend_Loader::loadClass($tableName);
1605             } catch (Zend_Exception $e) {
1606                 // require_once 'Zend/Db/Table/Row/Exception.php';
1607                 throw new Zend_Db_Table_Row_Exception($e->getMessage(), $e->getCode(), $e);
1608             }
1609         }
1610 
1611         $options = array();
1612 
1613         if ($referenceTable instanceof Zend_Db_Table_Abstract) {
1614             $options['db'] = $referenceTable->getAdapter();
1615         }
1616 
1617         if (isset($tableDefinition) && $tableDefinition !== null) {
1618             $options[Zend_Db_Table_Abstract::DEFINITION] = $tableDefinition;
1619         }
1620 
1621         return new $tableName($options);
1622     }
1623 
1624 }