vendor/symfony/src/Symfony/Component/HttpKernel/Profiler/PdoProfilerStorage.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony package.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Symfony\Component\HttpKernel\Profiler;
       
    13 
       
    14 use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
       
    15 
       
    16 /**
       
    17  * Base PDO storage for profiling information in a PDO database.
       
    18  *
       
    19  * @author Fabien Potencier <fabien@symfony.com>
       
    20  * @author Jan Schumann <js@schumann-it.com>
       
    21  */
       
    22 abstract class PdoProfilerStorage implements ProfilerStorageInterface
       
    23 {
       
    24     protected $dsn;
       
    25     protected $username;
       
    26     protected $password;
       
    27     protected $lifetime;
       
    28     protected $db;
       
    29 
       
    30     /**
       
    31      * Constructor.
       
    32      *
       
    33      * @param string  $dsn      A data source name
       
    34      * @param string  $username The username for the database
       
    35      * @param string  $password The password for the database
       
    36      * @param integer $lifetime The lifetime to use for the purge
       
    37      */
       
    38     public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
       
    39     {
       
    40         $this->dsn = $dsn;
       
    41         $this->username = $username;
       
    42         $this->password = $password;
       
    43         $this->lifetime = (int) $lifetime;
       
    44     }
       
    45 
       
    46     /**
       
    47      * {@inheritdoc}
       
    48      */
       
    49     public function find($ip, $url, $limit)
       
    50     {
       
    51         list($criteria, $args) = $this->buildCriteria($ip, $url, $limit);
       
    52 
       
    53         $criteria = $criteria ? 'WHERE '.implode(' AND ', $criteria) : '';
       
    54 
       
    55         $db = $this->initDb();
       
    56         $tokens = $this->fetch($db, 'SELECT token, ip, url, time, parent FROM sf_profiler_data '.$criteria.' ORDER BY time DESC LIMIT '.((integer) $limit), $args);
       
    57         $this->close($db);
       
    58 
       
    59         return $tokens;
       
    60     }
       
    61 
       
    62     /**
       
    63      * {@inheritdoc}
       
    64      */
       
    65     public function read($token)
       
    66     {
       
    67         $db = $this->initDb();
       
    68         $args = array(':token' => $token);
       
    69         $data = $this->fetch($db, 'SELECT data, parent, ip, url, time FROM sf_profiler_data WHERE token = :token LIMIT 1', $args);
       
    70         $this->close($db);
       
    71         if (isset($data[0]['data'])) {
       
    72             return $this->createProfileFromData($token, $data[0]);
       
    73         }
       
    74 
       
    75         return null;
       
    76     }
       
    77 
       
    78     /**
       
    79      * {@inheritdoc}
       
    80      */
       
    81     public function write(Profile $profile)
       
    82     {
       
    83         $db = $this->initDb();
       
    84         $args = array(
       
    85             ':token'      => $profile->getToken(),
       
    86             ':parent'     => $profile->getParent() ? $profile->getParent()->getToken() : '',
       
    87             ':data'       => base64_encode(serialize($profile->getCollectors())),
       
    88             ':ip'         => $profile->getIp(),
       
    89             ':url'        => $profile->getUrl(),
       
    90             ':time'       => $profile->getTime(),
       
    91             ':created_at' => time(),
       
    92         );
       
    93         try {
       
    94             $this->exec($db, 'INSERT INTO sf_profiler_data (token, parent, data, ip, url, time, created_at) VALUES (:token, :parent, :data, :ip, :url, :time, :created_at)', $args);
       
    95             $this->cleanup();
       
    96             $status = true;
       
    97         } catch (\Exception $e) {
       
    98             $status = false;
       
    99         }
       
   100         $this->close($db);
       
   101 
       
   102         return $status;
       
   103     }
       
   104 
       
   105     /**
       
   106      * {@inheritdoc}
       
   107      */
       
   108     public function purge()
       
   109     {
       
   110         $db = $this->initDb();
       
   111         $this->exec($db, 'DELETE FROM sf_profiler_data');
       
   112         $this->close($db);
       
   113     }
       
   114 
       
   115     /**
       
   116      * Build SQL criteria to fetch records by ip and url
       
   117      *
       
   118      * @param string $ip    The IP
       
   119      * @param string $url   The URL
       
   120      * @param string $limit The maximum number of tokens to return
       
   121      *
       
   122      * @return array An array with (criteria, args)
       
   123      */
       
   124     abstract protected function buildCriteria($ip, $url, $limit);
       
   125 
       
   126     /**
       
   127      * Initializes the database
       
   128      *
       
   129      * @throws \RuntimeException When the requested database driver is not installed
       
   130      */
       
   131     abstract protected function initDb();
       
   132 
       
   133     protected function cleanup()
       
   134     {
       
   135         $db = $this->initDb();
       
   136         $this->exec($db, 'DELETE FROM sf_profiler_data WHERE created_at < :time', array(':time' => time() - $this->lifetime));
       
   137         $this->close($db);
       
   138     }
       
   139 
       
   140     protected function exec($db, $query, array $args = array())
       
   141     {
       
   142         $stmt = $this->prepareStatement($db, $query);
       
   143 
       
   144         foreach ($args as $arg => $val) {
       
   145             $stmt->bindValue($arg, $val, is_int($val) ? \PDO::PARAM_INT : \PDO::PARAM_STR);
       
   146         }
       
   147         $success = $stmt->execute();
       
   148         if (!$success) {
       
   149             throw new \RuntimeException(sprintf('Error executing query "%s"', $query));
       
   150         }
       
   151     }
       
   152 
       
   153     protected function prepareStatement($db, $query)
       
   154     {
       
   155         try {
       
   156             $stmt = $db->prepare($query);
       
   157         } catch (\Exception $e) {
       
   158             $stmt = false;
       
   159         }
       
   160 
       
   161         if (false === $stmt) {
       
   162             throw new \RuntimeException('The database cannot successfully prepare the statement');
       
   163         }
       
   164 
       
   165         return $stmt;
       
   166     }
       
   167 
       
   168     protected function fetch($db, $query, array $args = array())
       
   169     {
       
   170         $stmt = $this->prepareStatement($db, $query);
       
   171 
       
   172         foreach ($args as $arg => $val) {
       
   173             $stmt->bindValue($arg, $val, is_int($val) ? \PDO::PARAM_INT : \PDO::PARAM_STR);
       
   174         }
       
   175         $stmt->execute();
       
   176         $return = $stmt->fetchAll(\PDO::FETCH_ASSOC);
       
   177 
       
   178         return $return;
       
   179     }
       
   180 
       
   181     protected function close($db)
       
   182     {
       
   183     }
       
   184 
       
   185     protected function createProfileFromData($token, $data, $parent = null)
       
   186     {
       
   187         $profile = new Profile($token);
       
   188         $profile->setIp($data['ip']);
       
   189         $profile->setUrl($data['url']);
       
   190         $profile->setTime($data['time']);
       
   191         $profile->setCollectors(unserialize(base64_decode($data['data'])));
       
   192 
       
   193         if (!$parent && isset($data['parent']) && $data['parent']) {
       
   194             $parent = $this->read($data['parent']);
       
   195         }
       
   196 
       
   197         if ($parent) {
       
   198             $profile->setParent($parent);
       
   199         }
       
   200 
       
   201         $profile->setChildren($this->readChildren($token, $parent));
       
   202 
       
   203         return $profile;
       
   204     }
       
   205 
       
   206     /**
       
   207      * Reads the child profiles for the given token.
       
   208      *
       
   209      * @param string $token The parent token
       
   210      *
       
   211      * @return array An array of Profile instance
       
   212      */
       
   213     protected function readChildren($token, $parent)
       
   214     {
       
   215         $db = $this->initDb();
       
   216         $data = $this->fetch($db, 'SELECT token, data, ip, url, time FROM sf_profiler_data WHERE parent = :token', array(':token' => $token));
       
   217         $this->close($db);
       
   218 
       
   219         if (!$data) {
       
   220             return array();
       
   221         }
       
   222 
       
   223         $profiles = array();
       
   224         foreach ($data as $d) {
       
   225             $profiles[] = $this->createProfileFromData($d['token'], $d, $parent);
       
   226         }
       
   227 
       
   228         return $profiles;
       
   229     }
       
   230 }