web/lib/Zend/Db/Profiler.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Db
       
    17  * @subpackage Profiler
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: Profiler.php 20096 2010-01-06 02:05:09Z bkarwin $
       
    21  */
       
    22 
       
    23 
       
    24 /**
       
    25  * @category   Zend
       
    26  * @package    Zend_Db
       
    27  * @subpackage Profiler
       
    28  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    29  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    30  */
       
    31 class Zend_Db_Profiler
       
    32 {
       
    33 
       
    34     /**
       
    35      * A connection operation or selecting a database.
       
    36      */
       
    37     const CONNECT = 1;
       
    38 
       
    39     /**
       
    40      * Any general database query that does not fit into the other constants.
       
    41      */
       
    42     const QUERY = 2;
       
    43 
       
    44     /**
       
    45      * Adding new data to the database, such as SQL's INSERT.
       
    46      */
       
    47     const INSERT = 4;
       
    48 
       
    49     /**
       
    50      * Updating existing information in the database, such as SQL's UPDATE.
       
    51      *
       
    52      */
       
    53     const UPDATE = 8;
       
    54 
       
    55     /**
       
    56      * An operation related to deleting data in the database,
       
    57      * such as SQL's DELETE.
       
    58      */
       
    59     const DELETE = 16;
       
    60 
       
    61     /**
       
    62      * Retrieving information from the database, such as SQL's SELECT.
       
    63      */
       
    64     const SELECT = 32;
       
    65 
       
    66     /**
       
    67      * Transactional operation, such as start transaction, commit, or rollback.
       
    68      */
       
    69     const TRANSACTION = 64;
       
    70 
       
    71     /**
       
    72      * Inform that a query is stored (in case of filtering)
       
    73      */
       
    74     const STORED = 'stored';
       
    75 
       
    76     /**
       
    77      * Inform that a query is ignored (in case of filtering)
       
    78      */
       
    79     const IGNORED = 'ignored';
       
    80 
       
    81     /**
       
    82      * Array of Zend_Db_Profiler_Query objects.
       
    83      *
       
    84      * @var array
       
    85      */
       
    86     protected $_queryProfiles = array();
       
    87 
       
    88     /**
       
    89      * Stores enabled state of the profiler.  If set to False, calls to
       
    90      * queryStart() will simply be ignored.
       
    91      *
       
    92      * @var boolean
       
    93      */
       
    94     protected $_enabled = false;
       
    95 
       
    96     /**
       
    97      * Stores the number of seconds to filter.  NULL if filtering by time is
       
    98      * disabled.  If an integer is stored here, profiles whose elapsed time
       
    99      * is less than this value in seconds will be unset from
       
   100      * the self::$_queryProfiles array.
       
   101      *
       
   102      * @var integer
       
   103      */
       
   104     protected $_filterElapsedSecs = null;
       
   105 
       
   106     /**
       
   107      * Logical OR of any of the filter constants.  NULL if filtering by query
       
   108      * type is disable.  If an integer is stored here, it is the logical OR of
       
   109      * any of the query type constants.  When the query ends, if it is not
       
   110      * one of the types specified, it will be unset from the
       
   111      * self::$_queryProfiles array.
       
   112      *
       
   113      * @var integer
       
   114      */
       
   115     protected $_filterTypes = null;
       
   116 
       
   117     /**
       
   118      * Class constructor.  The profiler is disabled by default unless it is
       
   119      * specifically enabled by passing in $enabled here or calling setEnabled().
       
   120      *
       
   121      * @param  boolean $enabled
       
   122      * @return void
       
   123      */
       
   124     public function __construct($enabled = false)
       
   125     {
       
   126         $this->setEnabled($enabled);
       
   127     }
       
   128 
       
   129     /**
       
   130      * Enable or disable the profiler.  If $enable is false, the profiler
       
   131      * is disabled and will not log any queries sent to it.
       
   132      *
       
   133      * @param  boolean $enable
       
   134      * @return Zend_Db_Profiler Provides a fluent interface
       
   135      */
       
   136     public function setEnabled($enable)
       
   137     {
       
   138         $this->_enabled = (boolean) $enable;
       
   139 
       
   140         return $this;
       
   141     }
       
   142 
       
   143     /**
       
   144      * Get the current state of enable.  If True is returned,
       
   145      * the profiler is enabled.
       
   146      *
       
   147      * @return boolean
       
   148      */
       
   149     public function getEnabled()
       
   150     {
       
   151         return $this->_enabled;
       
   152     }
       
   153 
       
   154     /**
       
   155      * Sets a minimum number of seconds for saving query profiles.  If this
       
   156      * is set, only those queries whose elapsed time is equal or greater than
       
   157      * $minimumSeconds will be saved.  To save all queries regardless of
       
   158      * elapsed time, set $minimumSeconds to null.
       
   159      *
       
   160      * @param  integer $minimumSeconds OPTIONAL
       
   161      * @return Zend_Db_Profiler Provides a fluent interface
       
   162      */
       
   163     public function setFilterElapsedSecs($minimumSeconds = null)
       
   164     {
       
   165         if (null === $minimumSeconds) {
       
   166             $this->_filterElapsedSecs = null;
       
   167         } else {
       
   168             $this->_filterElapsedSecs = (integer) $minimumSeconds;
       
   169         }
       
   170 
       
   171         return $this;
       
   172     }
       
   173 
       
   174     /**
       
   175      * Returns the minimum number of seconds for saving query profiles, or null if
       
   176      * query profiles are saved regardless of elapsed time.
       
   177      *
       
   178      * @return integer|null
       
   179      */
       
   180     public function getFilterElapsedSecs()
       
   181     {
       
   182         return $this->_filterElapsedSecs;
       
   183     }
       
   184 
       
   185     /**
       
   186      * Sets the types of query profiles to save.  Set $queryType to one of
       
   187      * the Zend_Db_Profiler::* constants to only save profiles for that type of
       
   188      * query.  To save more than one type, logical OR them together.  To
       
   189      * save all queries regardless of type, set $queryType to null.
       
   190      *
       
   191      * @param  integer $queryTypes OPTIONAL
       
   192      * @return Zend_Db_Profiler Provides a fluent interface
       
   193      */
       
   194     public function setFilterQueryType($queryTypes = null)
       
   195     {
       
   196         $this->_filterTypes = $queryTypes;
       
   197 
       
   198         return $this;
       
   199     }
       
   200 
       
   201     /**
       
   202      * Returns the types of query profiles saved, or null if queries are saved regardless
       
   203      * of their types.
       
   204      *
       
   205      * @return integer|null
       
   206      * @see    Zend_Db_Profiler::setFilterQueryType()
       
   207      */
       
   208     public function getFilterQueryType()
       
   209     {
       
   210         return $this->_filterTypes;
       
   211     }
       
   212 
       
   213     /**
       
   214      * Clears the history of any past query profiles.  This is relentless
       
   215      * and will even clear queries that were started and may not have
       
   216      * been marked as ended.
       
   217      *
       
   218      * @return Zend_Db_Profiler Provides a fluent interface
       
   219      */
       
   220     public function clear()
       
   221     {
       
   222         $this->_queryProfiles = array();
       
   223 
       
   224         return $this;
       
   225     }
       
   226 
       
   227     /**
       
   228      * @param  integer $queryId
       
   229      * @return integer or null
       
   230      */
       
   231     public function queryClone(Zend_Db_Profiler_Query $query)
       
   232     {
       
   233         $this->_queryProfiles[] = clone $query;
       
   234 
       
   235         end($this->_queryProfiles);
       
   236 
       
   237         return key($this->_queryProfiles);
       
   238     }
       
   239 
       
   240     /**
       
   241      * Starts a query.  Creates a new query profile object (Zend_Db_Profiler_Query)
       
   242      * and returns the "query profiler handle".  Run the query, then call
       
   243      * queryEnd() and pass it this handle to make the query as ended and
       
   244      * record the time.  If the profiler is not enabled, this takes no
       
   245      * action and immediately returns null.
       
   246      *
       
   247      * @param  string  $queryText   SQL statement
       
   248      * @param  integer $queryType   OPTIONAL Type of query, one of the Zend_Db_Profiler::* constants
       
   249      * @return integer|null
       
   250      */
       
   251     public function queryStart($queryText, $queryType = null)
       
   252     {
       
   253         if (!$this->_enabled) {
       
   254             return null;
       
   255         }
       
   256 
       
   257         // make sure we have a query type
       
   258         if (null === $queryType) {
       
   259             switch (strtolower(substr(ltrim($queryText), 0, 6))) {
       
   260                 case 'insert':
       
   261                     $queryType = self::INSERT;
       
   262                     break;
       
   263                 case 'update':
       
   264                     $queryType = self::UPDATE;
       
   265                     break;
       
   266                 case 'delete':
       
   267                     $queryType = self::DELETE;
       
   268                     break;
       
   269                 case 'select':
       
   270                     $queryType = self::SELECT;
       
   271                     break;
       
   272                 default:
       
   273                     $queryType = self::QUERY;
       
   274                     break;
       
   275             }
       
   276         }
       
   277 
       
   278         /**
       
   279          * @see Zend_Db_Profiler_Query
       
   280          */
       
   281         require_once 'Zend/Db/Profiler/Query.php';
       
   282         $this->_queryProfiles[] = new Zend_Db_Profiler_Query($queryText, $queryType);
       
   283 
       
   284         end($this->_queryProfiles);
       
   285 
       
   286         return key($this->_queryProfiles);
       
   287     }
       
   288 
       
   289     /**
       
   290      * Ends a query.  Pass it the handle that was returned by queryStart().
       
   291      * This will mark the query as ended and save the time.
       
   292      *
       
   293      * @param  integer $queryId
       
   294      * @throws Zend_Db_Profiler_Exception
       
   295      * @return void
       
   296      */
       
   297     public function queryEnd($queryId)
       
   298     {
       
   299         // Don't do anything if the Zend_Db_Profiler is not enabled.
       
   300         if (!$this->_enabled) {
       
   301             return self::IGNORED;
       
   302         }
       
   303 
       
   304         // Check for a valid query handle.
       
   305         if (!isset($this->_queryProfiles[$queryId])) {
       
   306             /**
       
   307              * @see Zend_Db_Profiler_Exception
       
   308              */
       
   309             require_once 'Zend/Db/Profiler/Exception.php';
       
   310             throw new Zend_Db_Profiler_Exception("Profiler has no query with handle '$queryId'.");
       
   311         }
       
   312 
       
   313         $qp = $this->_queryProfiles[$queryId];
       
   314 
       
   315         // Ensure that the query profile has not already ended
       
   316         if ($qp->hasEnded()) {
       
   317             /**
       
   318              * @see Zend_Db_Profiler_Exception
       
   319              */
       
   320             require_once 'Zend/Db/Profiler/Exception.php';
       
   321             throw new Zend_Db_Profiler_Exception("Query with profiler handle '$queryId' has already ended.");
       
   322         }
       
   323 
       
   324         // End the query profile so that the elapsed time can be calculated.
       
   325         $qp->end();
       
   326 
       
   327         /**
       
   328          * If filtering by elapsed time is enabled, only keep the profile if
       
   329          * it ran for the minimum time.
       
   330          */
       
   331         if (null !== $this->_filterElapsedSecs && $qp->getElapsedSecs() < $this->_filterElapsedSecs) {
       
   332             unset($this->_queryProfiles[$queryId]);
       
   333             return self::IGNORED;
       
   334         }
       
   335 
       
   336         /**
       
   337          * If filtering by query type is enabled, only keep the query if
       
   338          * it was one of the allowed types.
       
   339          */
       
   340         if (null !== $this->_filterTypes && !($qp->getQueryType() & $this->_filterTypes)) {
       
   341             unset($this->_queryProfiles[$queryId]);
       
   342             return self::IGNORED;
       
   343         }
       
   344 
       
   345         return self::STORED;
       
   346     }
       
   347 
       
   348     /**
       
   349      * Get a profile for a query.  Pass it the same handle that was returned
       
   350      * by queryStart() and it will return a Zend_Db_Profiler_Query object.
       
   351      *
       
   352      * @param  integer $queryId
       
   353      * @throws Zend_Db_Profiler_Exception
       
   354      * @return Zend_Db_Profiler_Query
       
   355      */
       
   356     public function getQueryProfile($queryId)
       
   357     {
       
   358         if (!array_key_exists($queryId, $this->_queryProfiles)) {
       
   359             /**
       
   360              * @see Zend_Db_Profiler_Exception
       
   361              */
       
   362             require_once 'Zend/Db/Profiler/Exception.php';
       
   363             throw new Zend_Db_Profiler_Exception("Query handle '$queryId' not found in profiler log.");
       
   364         }
       
   365 
       
   366         return $this->_queryProfiles[$queryId];
       
   367     }
       
   368 
       
   369     /**
       
   370      * Get an array of query profiles (Zend_Db_Profiler_Query objects).  If $queryType
       
   371      * is set to one of the Zend_Db_Profiler::* constants then only queries of that
       
   372      * type will be returned.  Normally, queries that have not yet ended will
       
   373      * not be returned unless $showUnfinished is set to True.  If no
       
   374      * queries were found, False is returned. The returned array is indexed by the query
       
   375      * profile handles.
       
   376      *
       
   377      * @param  integer $queryType
       
   378      * @param  boolean $showUnfinished
       
   379      * @return array|false
       
   380      */
       
   381     public function getQueryProfiles($queryType = null, $showUnfinished = false)
       
   382     {
       
   383         $queryProfiles = array();
       
   384         foreach ($this->_queryProfiles as $key => $qp) {
       
   385             if ($queryType === null) {
       
   386                 $condition = true;
       
   387             } else {
       
   388                 $condition = ($qp->getQueryType() & $queryType);
       
   389             }
       
   390 
       
   391             if (($qp->hasEnded() || $showUnfinished) && $condition) {
       
   392                 $queryProfiles[$key] = $qp;
       
   393             }
       
   394         }
       
   395 
       
   396         if (empty($queryProfiles)) {
       
   397             $queryProfiles = false;
       
   398         }
       
   399 
       
   400         return $queryProfiles;
       
   401     }
       
   402 
       
   403     /**
       
   404      * Get the total elapsed time (in seconds) of all of the profiled queries.
       
   405      * Only queries that have ended will be counted.  If $queryType is set to
       
   406      * one or more of the Zend_Db_Profiler::* constants, the elapsed time will be calculated
       
   407      * only for queries of the given type(s).
       
   408      *
       
   409      * @param  integer $queryType OPTIONAL
       
   410      * @return float
       
   411      */
       
   412     public function getTotalElapsedSecs($queryType = null)
       
   413     {
       
   414         $elapsedSecs = 0;
       
   415         foreach ($this->_queryProfiles as $key => $qp) {
       
   416             if (null === $queryType) {
       
   417                 $condition = true;
       
   418             } else {
       
   419                 $condition = ($qp->getQueryType() & $queryType);
       
   420             }
       
   421             if (($qp->hasEnded()) && $condition) {
       
   422                 $elapsedSecs += $qp->getElapsedSecs();
       
   423             }
       
   424         }
       
   425         return $elapsedSecs;
       
   426     }
       
   427 
       
   428     /**
       
   429      * Get the total number of queries that have been profiled.  Only queries that have ended will
       
   430      * be counted.  If $queryType is set to one of the Zend_Db_Profiler::* constants, only queries of
       
   431      * that type will be counted.
       
   432      *
       
   433      * @param  integer $queryType OPTIONAL
       
   434      * @return integer
       
   435      */
       
   436     public function getTotalNumQueries($queryType = null)
       
   437     {
       
   438         if (null === $queryType) {
       
   439             return count($this->_queryProfiles);
       
   440         }
       
   441 
       
   442         $numQueries = 0;
       
   443         foreach ($this->_queryProfiles as $qp) {
       
   444             if ($qp->hasEnded() && ($qp->getQueryType() & $queryType)) {
       
   445                 $numQueries++;
       
   446             }
       
   447         }
       
   448 
       
   449         return $numQueries;
       
   450     }
       
   451 
       
   452     /**
       
   453      * Get the Zend_Db_Profiler_Query object for the last query that was run, regardless if it has
       
   454      * ended or not.  If the query has not ended, its end time will be null.  If no queries have
       
   455      * been profiled, false is returned.
       
   456      *
       
   457      * @return Zend_Db_Profiler_Query|false
       
   458      */
       
   459     public function getLastQueryProfile()
       
   460     {
       
   461         if (empty($this->_queryProfiles)) {
       
   462             return false;
       
   463         }
       
   464 
       
   465         end($this->_queryProfiles);
       
   466 
       
   467         return current($this->_queryProfiles);
       
   468     }
       
   469 
       
   470 }
       
   471