* @copyright 2008-2010 Phergie Development Team (http://phergie.org) * @license http://phergie.org/license New BSD License * @link http://pear.phergie.org/package/Phergie_Plugin_Php */ /** * Data source for {@see Phergie_Plugin_Php}. This source reads function * descriptions from a file and stores them in a SQLite database. When a * function description is requested, the function is retrieved from the * local database. * * @category Phergie * @package Phergie_Plugin_Php * @author Phergie Development Team * @license http://phergie.org/license New BSD License * @link http://pear.phergie.org/package/Phergie_Plugin_Php * @uses extension pdo * @uses extension pdo_sqlite * @uses Phergie_Plugin_Command pear.phergie.org */ class Phergie_Plugin_Php_Source_Local implements Phergie_Plugin_Php_Source { /** * Local database for storage * * @var PDO */ protected $database; /** * Source of the PHP function summary * * @var string */ protected $url = 'http://cvs.php.net/viewvc.cgi/phpdoc/funcsummary.txt?revision=HEAD'; /** * Constructor to initialize the data source. * * @return void */ public function __construct() { $path = dirname(__FILE__); try { $this->database = new PDO('sqlite:' . $path . '/functions.db'); $this->buildDatabase(); // @todo Modify this to be rethrown as an appropriate // Phergie_Plugin_Exception and handled in Phergie_Plugin_Php } catch (PDOException $e) { echo 'PDO failure: '.$e->getMessage(); } } /** * Searches for a description of the function. * * @param string $function Search pattern to match against the function * name, wildcards supported using % * * @return array|null Associative array containing the function name and * description or NULL if no results are found */ public function findFunction($function) { // Remove possible parentheses $split = preg_split('{\(|\)}', $function); $function = (count($split)) ? array_shift($split) : $function; // Prepare the database statement $stmt = $this->database->prepare('SELECT `name`, `description` FROM `functions` WHERE `name` LIKE :function'); $stmt->execute(array(':function' => $function)); // Check the results if (count($stmt) > 0) { $result = $stmt->fetch(PDO::FETCH_ASSOC); /** * @todo add class and function URLS * class methods: http://php.net/manual/en/classname.methodname.php * functions: http://php.net/manual/en/function.functionname.php * where '_' is replaced with '-' */ return $result; } // No results found, return return null; } /** * Build the database and parses the function summary file into it. * * @param bool $rebuild TRUE to force a rebuild of the table used to * house function information, FALSE otherwise, defaults to FALSE * * @return void */ protected function buildDatabase($rebuild = false) { // Check to see if the functions table exists $checkstmt = $this->database->query("SELECT COUNT(*) FROM `sqlite_master` WHERE `name` = 'functions'"); $checkstmt->execute(); $result = $checkstmt->fetch(PDO::FETCH_ASSOC); unset( $checkstmt ); $table = $result['COUNT(*)']; unset( $result ); // If the table doesn't exist, create it if (!$table) { $this->database->exec('CREATE TABLE `functions` (`name` VARCHAR(255), `description` TEXT)'); $this->database->exec('CREATE UNIQUE INDEX `functions_name` ON `functions` (`name`)'); } // If we created a new table, fill it with data if (!$table || $rebuild) { // Get the contents of the source file // @todo Handle possible error cases better here; the @ operator // shouldn't be needed $contents = @file($this->url, FILE_IGNORE_NEW_LINES + FILE_SKIP_EMPTY_LINES); if (!$contents) { return; } // Parse the contents $valid = array(); $firstPart = ''; $lineNumber = 0; foreach ($contents as $line) { // Clean the current line $line = trim($line); // Skip comment lines if (0 === strpos($line, '#')) { // reset the line if the current line is odd if (($lineNumber % 2) !== 0) { $lineNumber--; } continue; } /* * If the current line is even, it's the first part of the * complete function description ... */ if (($lineNumber % 2) === 0) { $firstPart = $line; } else { // ... it's the last part of the complete function description $completeLine = $firstPart . ' ' . $line; $firstPart = ''; if (preg_match('{^([^\s]*)[\s]?([^)]*)\(([^\)]*)\)[\sU]+([\sa-zA-Z0-9\.,\-_()]*)$}', $completeLine, $matches)) { $valid[] = $matches; } } // Up the line number before going to the next line $lineNumber++; } // free up some memory unset($contents); // Process the valid matches if (count($valid) > 0) { // Clear the database $this->database->exec('DELETE * FROM `functions`'); // Prepare the sql statement $stmt = $this->database->prepare('INSERT INTO `functions` (`name`, `description`) VALUES (:name, :description)'); $this->database->beginTransaction(); // Insert the data foreach ($valid as $function) { // Extract function values list( , $retval, $name, $params, $desc) = $function; if (empty($name)) { $name = $retval; $retval = ''; } // Reconstruct the complete function line $line = trim($retval . ' ' . $name . '(' . $params . ') - ' . $desc); // Execute the statement $stmt->execute(array(':name' => $name, ':description' => $line)); } // Commit the changes to the database $this->database->commit(); } // free up some more memory unset($valid); } } }