9 * This source file is subject to the new BSD license that is bundled
10 * with this package in the file LICENSE.
11 * It is also available through the world-wide-web at this URL:
12 * http://phergie.org/license
15 * @package Phergie_Plugin_Php
16 * @author Phergie Development Team <team@phergie.org>
17 * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
18 * @license http://phergie.org/license New BSD License
19 * @link http://pear.phergie.org/package/Phergie_Plugin_Php
23 * Data source for {@see Phergie_Plugin_Php}. This source reads function
24 * descriptions from a file and stores them in a SQLite database. When a
25 * function description is requested, the function is retrieved from the
29 * @package Phergie_Plugin_Php
30 * @author Phergie Development Team <team@phergie.org>
31 * @license http://phergie.org/license New BSD License
32 * @link http://pear.phergie.org/package/Phergie_Plugin_Php
34 * @uses extension pdo_sqlite
35 * @uses Phergie_Plugin_Command pear.phergie.org
37 class Phergie_Plugin_Php_Source_Local implements Phergie_Plugin_Php_Source
40 * Local database for storage
47 * Source of the PHP function summary
51 protected $url = 'http://cvs.php.net/viewvc.cgi/phpdoc/funcsummary.txt?revision=HEAD';
54 * Constructor to initialize the data source.
58 public function __construct()
60 $path = dirname(__FILE__);
63 $this->database = new PDO('sqlite:' . $path . '/functions.db');
64 $this->buildDatabase();
65 // @todo Modify this to be rethrown as an appropriate
66 // Phergie_Plugin_Exception and handled in Phergie_Plugin_Php
67 } catch (PDOException $e) {
68 echo 'PDO failure: '.$e->getMessage();
73 * Searches for a description of the function.
75 * @param string $function Search pattern to match against the function
76 * name, wildcards supported using %
78 * @return array|null Associative array containing the function name and
79 * description or NULL if no results are found
81 public function findFunction($function)
83 // Remove possible parentheses
84 $split = preg_split('{\(|\)}', $function);
85 $function = (count($split)) ? array_shift($split) : $function;
87 // Prepare the database statement
88 $stmt = $this->database->prepare('SELECT `name`, `description` FROM `functions` WHERE `name` LIKE :function');
89 $stmt->execute(array(':function' => $function));
92 if (count($stmt) > 0) {
93 $result = $stmt->fetch(PDO::FETCH_ASSOC);
95 * @todo add class and function URLS
96 * class methods: http://php.net/manual/en/classname.methodname.php
97 * functions: http://php.net/manual/en/function.functionname.php
98 * where '_' is replaced with '-'
103 // No results found, return
108 * Build the database and parses the function summary file into it.
110 * @param bool $rebuild TRUE to force a rebuild of the table used to
111 * house function information, FALSE otherwise, defaults to FALSE
115 protected function buildDatabase($rebuild = false)
117 // Check to see if the functions table exists
118 $checkstmt = $this->database->query("SELECT COUNT(*) FROM `sqlite_master` WHERE `name` = 'functions'");
119 $checkstmt->execute();
120 $result = $checkstmt->fetch(PDO::FETCH_ASSOC);
122 $table = $result['COUNT(*)'];
124 // If the table doesn't exist, create it
126 $this->database->exec('CREATE TABLE `functions` (`name` VARCHAR(255), `description` TEXT)');
127 $this->database->exec('CREATE UNIQUE INDEX `functions_name` ON `functions` (`name`)');
130 // If we created a new table, fill it with data
131 if (!$table || $rebuild) {
132 // Get the contents of the source file
133 // @todo Handle possible error cases better here; the @ operator
134 // shouldn't be needed
135 $contents = @file($this->url, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES);
141 // Parse the contents
145 foreach ($contents as $line) {
146 // Clean the current line
149 // Skip comment lines
150 if (0 === strpos($line, '#')) {
151 // reset the line if the current line is odd
152 if (($lineNumber % 2) !== 0) {
159 * If the current line is even, it's the first part of the
160 * complete function description ...
162 if (($lineNumber % 2) === 0) {
165 // ... it's the last part of the complete function description
166 $completeLine = $firstPart . ' ' . $line;
168 if (preg_match('{^([^\s]*)[\s]?([^)]*)\(([^\)]*)\)[\sU]+([\sa-zA-Z0-9\.,\-_()]*)$}', $completeLine, $matches)) {
172 // Up the line number before going to the next line
175 // free up some memory
178 // Process the valid matches
179 if (count($valid) > 0) {
180 // Clear the database
181 $this->database->exec('DELETE * FROM `functions`');
183 // Prepare the sql statement
184 $stmt = $this->database->prepare('INSERT INTO `functions` (`name`, `description`) VALUES (:name, :description)');
185 $this->database->beginTransaction();
188 foreach ($valid as $function) {
189 // Extract function values
190 list( , $retval, $name, $params, $desc) = $function;
195 // Reconstruct the complete function line
196 $line = trim($retval . ' ' . $name . '(' . $params . ') - ' . $desc);
197 // Execute the statement
198 $stmt->execute(array(':name' => $name, ':description' => $line));
201 // Commit the changes to the database
202 $this->database->commit();
204 // free up some more memory