summaryrefslogtreecommitdiff
path: root/plugins/Irc/extlib/phergie/Phergie/Plugin/Php/Source/Local.php
blob: b72443c2e50ea610b710bb0b712ff94659189276 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
<?php
/**
 * Phergie 
 *
 * PHP version 5
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.
 * It is also available through the world-wide-web at this URL:
 * http://phergie.org/license
 *
 * @category  Phergie 
 * @package   Phergie_Plugin_Php
 * @author    Phergie Development Team <team@phergie.org>
 * @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 <team@phergie.org>
 * @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) {
        }
    }

    /**
     * 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
        $table = $this->database->exec("SELECT COUNT(*) FROM `sqlite_master` WHERE `name` = 'functions'");

        // 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);
        }
    }
}