File indexing completed on 2024-05-12 06:02:26

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 Profiler
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 /**
0025  * @category   Zend
0026  * @package    Zend_Db
0027  * @subpackage Profiler
0028  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0029  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0030  */
0031 class Zend_Db_Profiler
0032 {
0033 
0034     /**
0035      * A connection operation or selecting a database.
0036      */
0037     const CONNECT = 1;
0038 
0039     /**
0040      * Any general database query that does not fit into the other constants.
0041      */
0042     const QUERY = 2;
0043 
0044     /**
0045      * Adding new data to the database, such as SQL's INSERT.
0046      */
0047     const INSERT = 4;
0048 
0049     /**
0050      * Updating existing information in the database, such as SQL's UPDATE.
0051      *
0052      */
0053     const UPDATE = 8;
0054 
0055     /**
0056      * An operation related to deleting data in the database,
0057      * such as SQL's DELETE.
0058      */
0059     const DELETE = 16;
0060 
0061     /**
0062      * Retrieving information from the database, such as SQL's SELECT.
0063      */
0064     const SELECT = 32;
0065 
0066     /**
0067      * Transactional operation, such as start transaction, commit, or rollback.
0068      */
0069     const TRANSACTION = 64;
0070 
0071     /**
0072      * Inform that a query is stored (in case of filtering)
0073      */
0074     const STORED = 'stored';
0075 
0076     /**
0077      * Inform that a query is ignored (in case of filtering)
0078      */
0079     const IGNORED = 'ignored';
0080 
0081     /**
0082      * Array of Zend_Db_Profiler_Query objects.
0083      *
0084      * @var array
0085      */
0086     protected $_queryProfiles = array();
0087 
0088     /**
0089      * Stores enabled state of the profiler.  If set to False, calls to
0090      * queryStart() will simply be ignored.
0091      *
0092      * @var boolean
0093      */
0094     protected $_enabled = false;
0095 
0096     /**
0097      * Stores the number of seconds to filter.  NULL if filtering by time is
0098      * disabled.  If an integer is stored here, profiles whose elapsed time
0099      * is less than this value in seconds will be unset from
0100      * the self::$_queryProfiles array.
0101      *
0102      * @var integer
0103      */
0104     protected $_filterElapsedSecs = null;
0105 
0106     /**
0107      * Logical OR of any of the filter constants.  NULL if filtering by query
0108      * type is disable.  If an integer is stored here, it is the logical OR of
0109      * any of the query type constants.  When the query ends, if it is not
0110      * one of the types specified, it will be unset from the
0111      * self::$_queryProfiles array.
0112      *
0113      * @var integer
0114      */
0115     protected $_filterTypes = null;
0116 
0117     /**
0118      * Class constructor.  The profiler is disabled by default unless it is
0119      * specifically enabled by passing in $enabled here or calling setEnabled().
0120      *
0121      * @param  boolean $enabled
0122      * @return void
0123      */
0124     public function __construct($enabled = false)
0125     {
0126         $this->setEnabled($enabled);
0127     }
0128 
0129     /**
0130      * Enable or disable the profiler.  If $enable is false, the profiler
0131      * is disabled and will not log any queries sent to it.
0132      *
0133      * @param  boolean $enable
0134      * @return Zend_Db_Profiler Provides a fluent interface
0135      */
0136     public function setEnabled($enable)
0137     {
0138         $this->_enabled = (boolean) $enable;
0139 
0140         return $this;
0141     }
0142 
0143     /**
0144      * Get the current state of enable.  If True is returned,
0145      * the profiler is enabled.
0146      *
0147      * @return boolean
0148      */
0149     public function getEnabled()
0150     {
0151         return $this->_enabled;
0152     }
0153 
0154     /**
0155      * Sets a minimum number of seconds for saving query profiles.  If this
0156      * is set, only those queries whose elapsed time is equal or greater than
0157      * $minimumSeconds will be saved.  To save all queries regardless of
0158      * elapsed time, set $minimumSeconds to null.
0159      *
0160      * @param  integer $minimumSeconds OPTIONAL
0161      * @return Zend_Db_Profiler Provides a fluent interface
0162      */
0163     public function setFilterElapsedSecs($minimumSeconds = null)
0164     {
0165         if (null === $minimumSeconds) {
0166             $this->_filterElapsedSecs = null;
0167         } else {
0168             $this->_filterElapsedSecs = (integer) $minimumSeconds;
0169         }
0170 
0171         return $this;
0172     }
0173 
0174     /**
0175      * Returns the minimum number of seconds for saving query profiles, or null if
0176      * query profiles are saved regardless of elapsed time.
0177      *
0178      * @return integer|null
0179      */
0180     public function getFilterElapsedSecs()
0181     {
0182         return $this->_filterElapsedSecs;
0183     }
0184 
0185     /**
0186      * Sets the types of query profiles to save.  Set $queryType to one of
0187      * the Zend_Db_Profiler::* constants to only save profiles for that type of
0188      * query.  To save more than one type, logical OR them together.  To
0189      * save all queries regardless of type, set $queryType to null.
0190      *
0191      * @param  integer $queryTypes OPTIONAL
0192      * @return Zend_Db_Profiler Provides a fluent interface
0193      */
0194     public function setFilterQueryType($queryTypes = null)
0195     {
0196         $this->_filterTypes = $queryTypes;
0197 
0198         return $this;
0199     }
0200 
0201     /**
0202      * Returns the types of query profiles saved, or null if queries are saved regardless
0203      * of their types.
0204      *
0205      * @return integer|null
0206      * @see    Zend_Db_Profiler::setFilterQueryType()
0207      */
0208     public function getFilterQueryType()
0209     {
0210         return $this->_filterTypes;
0211     }
0212 
0213     /**
0214      * Clears the history of any past query profiles.  This is relentless
0215      * and will even clear queries that were started and may not have
0216      * been marked as ended.
0217      *
0218      * @return Zend_Db_Profiler Provides a fluent interface
0219      */
0220     public function clear()
0221     {
0222         $this->_queryProfiles = array();
0223 
0224         return $this;
0225     }
0226 
0227     /**
0228      * Clone a profiler query
0229      *
0230      * @param  Zend_Db_Profiler_Query $query
0231      * @return integer or null
0232      */
0233     public function queryClone(Zend_Db_Profiler_Query $query)
0234     {
0235         $this->_queryProfiles[] = clone $query;
0236 
0237         end($this->_queryProfiles);
0238 
0239         return key($this->_queryProfiles);
0240     }
0241 
0242     /**
0243      * Starts a query.  Creates a new query profile object (Zend_Db_Profiler_Query)
0244      * and returns the "query profiler handle".  Run the query, then call
0245      * queryEnd() and pass it this handle to make the query as ended and
0246      * record the time.  If the profiler is not enabled, this takes no
0247      * action and immediately returns null.
0248      *
0249      * @param  string  $queryText   SQL statement
0250      * @param  integer $queryType   OPTIONAL Type of query, one of the Zend_Db_Profiler::* constants
0251      * @return integer|null
0252      */
0253     public function queryStart($queryText, $queryType = null)
0254     {
0255         if (!$this->_enabled) {
0256             return null;
0257         }
0258 
0259         // make sure we have a query type
0260         if (null === $queryType) {
0261             switch (strtolower(substr(ltrim($queryText), 0, 6))) {
0262                 case 'insert':
0263                     $queryType = self::INSERT;
0264                     break;
0265                 case 'update':
0266                     $queryType = self::UPDATE;
0267                     break;
0268                 case 'delete':
0269                     $queryType = self::DELETE;
0270                     break;
0271                 case 'select':
0272                     $queryType = self::SELECT;
0273                     break;
0274                 default:
0275                     $queryType = self::QUERY;
0276                     break;
0277             }
0278         }
0279 
0280         /**
0281          * @see Zend_Db_Profiler_Query
0282          */
0283         // require_once 'Zend/Db/Profiler/Query.php';
0284         $this->_queryProfiles[] = new Zend_Db_Profiler_Query($queryText, $queryType);
0285 
0286         end($this->_queryProfiles);
0287 
0288         return key($this->_queryProfiles);
0289     }
0290 
0291     /**
0292      * Ends a query. Pass it the handle that was returned by queryStart().
0293      * This will mark the query as ended and save the time.
0294      *
0295      * @param  integer $queryId
0296      * @throws Zend_Db_Profiler_Exception
0297      * @return string   Inform that a query is stored or ignored.
0298      */
0299     public function queryEnd($queryId)
0300     {
0301         // Don't do anything if the Zend_Db_Profiler is not enabled.
0302         if (!$this->_enabled) {
0303             return self::IGNORED;
0304         }
0305 
0306         // Check for a valid query handle.
0307         if (!isset($this->_queryProfiles[$queryId])) {
0308             /**
0309              * @see Zend_Db_Profiler_Exception
0310              */
0311             // require_once 'Zend/Db/Profiler/Exception.php';
0312             throw new Zend_Db_Profiler_Exception("Profiler has no query with handle '$queryId'.");
0313         }
0314 
0315         $qp = $this->_queryProfiles[$queryId];
0316 
0317         // Ensure that the query profile has not already ended
0318         if ($qp->hasEnded()) {
0319             /**
0320              * @see Zend_Db_Profiler_Exception
0321              */
0322             // require_once 'Zend/Db/Profiler/Exception.php';
0323             throw new Zend_Db_Profiler_Exception("Query with profiler handle '$queryId' has already ended.");
0324         }
0325 
0326         // End the query profile so that the elapsed time can be calculated.
0327         $qp->end();
0328 
0329         /**
0330          * If filtering by elapsed time is enabled, only keep the profile if
0331          * it ran for the minimum time.
0332          */
0333         if (null !== $this->_filterElapsedSecs && $qp->getElapsedSecs() < $this->_filterElapsedSecs) {
0334             unset($this->_queryProfiles[$queryId]);
0335             return self::IGNORED;
0336         }
0337 
0338         /**
0339          * If filtering by query type is enabled, only keep the query if
0340          * it was one of the allowed types.
0341          */
0342         if (null !== $this->_filterTypes && !($qp->getQueryType() & $this->_filterTypes)) {
0343             unset($this->_queryProfiles[$queryId]);
0344             return self::IGNORED;
0345         }
0346 
0347         return self::STORED;
0348     }
0349 
0350     /**
0351      * Get a profile for a query.  Pass it the same handle that was returned
0352      * by queryStart() and it will return a Zend_Db_Profiler_Query object.
0353      *
0354      * @param  integer $queryId
0355      * @throws Zend_Db_Profiler_Exception
0356      * @return Zend_Db_Profiler_Query
0357      */
0358     public function getQueryProfile($queryId)
0359     {
0360         if (!array_key_exists($queryId, $this->_queryProfiles)) {
0361             /**
0362              * @see Zend_Db_Profiler_Exception
0363              */
0364             // require_once 'Zend/Db/Profiler/Exception.php';
0365             throw new Zend_Db_Profiler_Exception("Query handle '$queryId' not found in profiler log.");
0366         }
0367 
0368         return $this->_queryProfiles[$queryId];
0369     }
0370 
0371     /**
0372      * Get an array of query profiles (Zend_Db_Profiler_Query objects).  If $queryType
0373      * is set to one of the Zend_Db_Profiler::* constants then only queries of that
0374      * type will be returned.  Normally, queries that have not yet ended will
0375      * not be returned unless $showUnfinished is set to True.  If no
0376      * queries were found, False is returned. The returned array is indexed by the query
0377      * profile handles.
0378      *
0379      * @param  integer $queryType
0380      * @param  boolean $showUnfinished
0381      * @return array|false
0382      */
0383     public function getQueryProfiles($queryType = null, $showUnfinished = false)
0384     {
0385         $queryProfiles = array();
0386         foreach ($this->_queryProfiles as $key => $qp) {
0387             if ($queryType === null) {
0388                 $condition = true;
0389             } else {
0390                 $condition = ($qp->getQueryType() & $queryType);
0391             }
0392 
0393             if (($qp->hasEnded() || $showUnfinished) && $condition) {
0394                 $queryProfiles[$key] = $qp;
0395             }
0396         }
0397 
0398         if (empty($queryProfiles)) {
0399             $queryProfiles = false;
0400         }
0401 
0402         return $queryProfiles;
0403     }
0404 
0405     /**
0406      * Get the total elapsed time (in seconds) of all of the profiled queries.
0407      * Only queries that have ended will be counted.  If $queryType is set to
0408      * one or more of the Zend_Db_Profiler::* constants, the elapsed time will be calculated
0409      * only for queries of the given type(s).
0410      *
0411      * @param  integer $queryType OPTIONAL
0412      * @return float
0413      */
0414     public function getTotalElapsedSecs($queryType = null)
0415     {
0416         $elapsedSecs = 0;
0417         foreach ($this->_queryProfiles as $key => $qp) {
0418             if (null === $queryType) {
0419                 $condition = true;
0420             } else {
0421                 $condition = ($qp->getQueryType() & $queryType);
0422             }
0423             if (($qp->hasEnded()) && $condition) {
0424                 $elapsedSecs += $qp->getElapsedSecs();
0425             }
0426         }
0427         return $elapsedSecs;
0428     }
0429 
0430     /**
0431      * Get the total number of queries that have been profiled.  Only queries that have ended will
0432      * be counted.  If $queryType is set to one of the Zend_Db_Profiler::* constants, only queries of
0433      * that type will be counted.
0434      *
0435      * @param  integer $queryType OPTIONAL
0436      * @return integer
0437      */
0438     public function getTotalNumQueries($queryType = null)
0439     {
0440         if (null === $queryType) {
0441             return count($this->_queryProfiles);
0442         }
0443 
0444         $numQueries = 0;
0445         foreach ($this->_queryProfiles as $qp) {
0446             if ($qp->hasEnded() && ($qp->getQueryType() & $queryType)) {
0447                 $numQueries++;
0448             }
0449         }
0450 
0451         return $numQueries;
0452     }
0453 
0454     /**
0455      * Get the Zend_Db_Profiler_Query object for the last query that was run, regardless if it has
0456      * ended or not.  If the query has not ended, its end time will be null.  If no queries have
0457      * been profiled, false is returned.
0458      *
0459      * @return Zend_Db_Profiler_Query|false
0460      */
0461     public function getLastQueryProfile()
0462     {
0463         if (empty($this->_queryProfiles)) {
0464             return false;
0465         }
0466 
0467         end($this->_queryProfiles);
0468 
0469         return current($this->_queryProfiles);
0470     }
0471 
0472 }
0473