cvs: php-gtk-web /include/PhpGtkDoc Search2.php /include/PhpGtkDoc/Search2 Index.php /include/PhpGtkDoc/Search2/Result Html.php
[email protected] ("Christian Weiske")
| Newsgroups | php.gtk.webmaster |
|---|---|
| Message-ID | <cvscweiske1144748482@cvsserver> |
cweiske Tue Apr 11 09:41:22 2006 UTC
Added files:
/php-gtk-web/include/PhpGtkDoc Search2.php
/php-gtk-web/include/PhpGtkDoc/Search2 Index.php
/php-gtk-web/include/PhpGtkDoc/Search2/Result Html.php
Log:
Search2 classes.
I hate CVS
cweiske-20060411094122.txt
(text/plain, 25 KB)
http://cvs.php.net/viewcvs.cgi/php-gtk-web/include/PhpGtkDoc/Search2.php?view=markup&rev=1.1 Index: php-gtk-web/include/PhpGtkDoc/Search2.php +++ php-gtk-web/include/PhpGtkDoc/Search2.php <?php /** * Class to search the phpgtk2 manual. * * Use the static PhpGtkDoc_Search2::find() method * to search for a string, and use one of the * PhpGtkDoc_Search2_Result_* classes to display the result. * * @author Christian Weiske <[email protected]> */ class PhpGtkDoc_Search2 { /** * Searches for the given string * This function returns a nested array which allows the search results * being displayed by sections like enums/methods/classes/... * * @param string The string to search. It is automatically split into substrings by the space char * @param string The index file to use * * @return array Nested array with results. Format: * array( 1 => subarray(), 2 => subarray(), 3 => subarray()) * subarray( 'class' => array_of_files(), 'method' => array_of_files(), ...) */ public static function find($strSearch, $strIndexFile) { if (!file_exists($strIndexFile)) { throw new Exception('Index file does not exist: ' . $strIndexFile); } //TODO: Split better, so that we can exclude something with minus and so $arSearchWords = explode(' ', strtolower($strSearch)); $arIndex = unserialize(file_get_contents($strIndexFile)); $arResults = array();//all the found files for the indices $arWordsResults = array();//the found files which were found for each word foreach ($arSearchWords as $strWord) { $arWordsResults[$strWord] = array();//needs to be done if (isset($arIndex[$strWord])) { $arFound = $arIndex[$strWord]; foreach ($arFound as $nIndex => $arFiles) { foreach ($arFiles as $strFile) { $arWordsResults[$strWord][] = $strFile; $arResults[$nIndex][$strFile] = 1; } } } } //remove all the files which were not found for all the words //firstly, intersect all the single arrays of the words $arAllWords = array(); $bFirst = true; foreach ($arWordsResults as $strWord => $arWordResults) { if ($bFirst) { $arAllWords = $arWordResults; $bFirst = false; } else { $arAllWords = array_intersect($arAllWords, $arWordResults); } } ksort($arResults);//that key 1 is first //remove the not-in-all found files from the $arResults array foreach ($arResults as $nId => $arWords) { foreach ($arWords as $strWord => $nOne) { if (!in_array($strWord, $arAllWords)) { //remove the one file from the found list unset($arResults[$nId][$strWord]); } else { //remove the file from the accept list to ensure every file is //listed only once (try searching for "window gtk force") unset($arAllWords[array_search($strWord, $arAllWords)]); } } } //now split the subarrays 2 and 3 into sections: enums, properties, signals... foreach ($arResults as $nId => $arFiles) { $arResults[$nId] = self::splitIntoSections( $arFiles); } return self::reorderResult($arResults); }//public static function find($strSearch, $strIndexFile) /** * Re-orders the result to give a better order. * E.g. methods of the class which have the search string in it * will be moved to level 3 * * @param array $arResults Result array * @return array Fixed result array */ protected static function reorderResult($arResult) { //if a class is found, move the methods of this class to level 3, //as they all match. So non-same-class methods with the search //string should have higher priority if (isset($arResult[1]['class'])) { foreach ($arResult[1] as $strType => $arLevel) { if ($strType != 'class') { $arResult[3][$strType] = $arLevel; unset($arResult[1][$strType]); } } } return $arResult; }//protected static function reorderResult($arResult) /** * splits an array with files as keys into sections * like enums, properties, signals, ... * * @param array $arFiles array with file names as keys and a number as value * @return array array with subarrays, files as values in the subarrays. The keys for the subarrays are the section names */ protected static function splitIntoSections($arFiles) { $arSected = array(); $arRegexs['tutorials'] = '/^tutorials\\./';//has to be before class and method $arRegexs['class'] = '/^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+$/'; $arRegexs['constructor']= '/\\.constructor\\.([a-zA-Z0-9_]+\\.)?/'; $arRegexs['enum'] = '/^[a-zA-Z0-9]+\\.enum\\./'; $arRegexs['method'] = '/^[a-zA-Z0-9]+(\\.[a-zA-Z0-9]+)?\\.method\\./'; //the (..)? is for things like gdk::main() $arRegexs['property'] = '/^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.(prop|property)\\./'; $arRegexs['field'] = '/^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.(field)\\./'; $arRegexs['signal'] = '/^[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.signal\\./'; foreach ($arFiles as $strFile => $nOne) { $strBaseFile = basename($strFile); $bFound = false; foreach ($arRegexs as $strSection => $strRegex) { if( preg_match( $strRegex, $strBaseFile)) { $arSected[$strSection][] = $strFile; $bFound = true; break; } } if( !$bFound) { $arSected['unknown'][] = $strFile; } } //sort the sections internally by filename foreach( $arSected as $strSection => $arFiles) { sort( $arSected[$strSection]); } return $arSected; }//protected static function splitIntoSections($arFiles) /** * Returns the file title for the given file * for efficient working, the category has to be given * * The function tries to get the file title with some algorithms, * NOT from the direct html files. * * @param string $strFilename The file name, e.g. gtk.gtkcolorselection.method.get_color.php * @param string $strCategory The category of the file, e.g. class/enum/constructor/... * * @return string The file title, can be used for link titles */ public static function getFileTitle($strFilename, $strCategory) { $arParts = explode('.', basename($strFilename)); $nParts = count($arParts); switch ($strCategory) { case 'class': if ($arParts[1] == 'functions') { //function list gtk.functions.php return self::niceClassName($arParts[0]) . ' functions'; } else { //normal class name gtk.gtkentry.php return self::niceClassName($arParts[1]); } case 'constructor': if ($nParts == 5) {//static method constructor return self::niceClassName($arParts[1]) . '::' . $arParts[3] . '()'; } else { return self::niceClassName($arParts[1]) . ' constructor'; } case 'method': if ($nParts == 5) {//class with method return self::niceClassName($arParts[1]) . '::' . $arParts[3] . '()'; } else {//gtk/gdk method return self::niceClassName($arParts[0]) . '::' . $arParts[2] . '()'; } case 'property': if ($nParts == 5) {//class property return self::niceClassName($arParts[1]) . '::' . $arParts[3]; } else {//gtk/gdk property //does this exist? return self::niceClassName($arParts[0]) . '::' . $arParts[2]; } case 'field': if ($nParts == 5) {//class field return self::niceClassName($arParts[1]) . '::' . $arParts[3]; } else {//gtk/gdk field //does this exist? return self::niceClassName($arParts[0]) . '::' . $arParts[2]; } case 'signal': if ($nParts == 5) {//class signal return self::niceClassName($arParts[1]) . ': ' . $arParts[3]; } else {//gtk/gdk signal return self::niceClassName($arParts[0]) . ': ' . $arParts[2]; } case 'enum': return self::niceClassName($arParts[0]) . ucfirst($arParts[2]) . ' enum'; case 'tutorials': if ($nParts == 2) { return 'Tutorial list'; } else if ($nParts == 3) { return ucfirst($arParts[1]) . ' tutorial'; } else { return ucfirst( $arParts[1]) . ' tutorial: ' . $arParts[2]; } default: return $strFilename; } }//public static function getFileTitle($strFilename, $strCategory) /** * Makes a nice class name from a lowercase name * given "gdkcolor" it returns "GdkColor" * * @param string The lowercase class name * @return string The nice cased class name */ protected static function niceClassName( $strLowercaseClass) { $strPrefix = substr( $strLowercaseClass, 0, 3); if ($strPrefix == 'gdk' || $strPrefix == 'gtk' || $strPrefix == 'atk') { $strNice = ucfirst($strPrefix) . ucfirst(substr($strLowercaseClass, 3)); } else if ($strPrefix == 'pan') { $strNice = ucfirst($strPrefix) . ucfirst(substr($strLowercaseClass, 5)); } else { $strNice = ucfirst($strLowercaseClass); } return $strNice; }//protected static function niceClassName( $strLowercaseClass) }//class PhpGtkDoc_Search2 ?> http://cvs.php.net/viewcvs.cgi/php-gtk-web/include/PhpGtkDoc/Search2/Index.php?view=markup&rev=1.1 Index: php-gtk-web/include/PhpGtkDoc/Search2/Index.php +++ php-gtk-web/include/PhpGtkDoc/Search2/Index.php <?php /** * Class with all functionality needed to create the index for the * php-gtk2-doc search class. * * * Index explanation: * - Only full words are found. If you search by "wind", "window" will *not* be found. * - up to 2 with "_" connected full words are found, e.g. when searching for "this_is", * the file "this_is_a_long_name" will be found, but searching for "this_is_a" will not * bring the former result, as only 2 directly connected words are indexed. * Note that full method names are indexed, too: "this_is_a_long_name" will be found * by searching for "this_is_a_long_name" * - The index is prioritized. This means that results are sorted by priority: * (Example search: "window") * - class names will be first * found: gtk.gtkwindow.php, gdk.gdkwindow.php * - methods which contain the search word in method name are second * found: gdk.gdkdragcontext.property.dest_window.php * - methods with the search word not in the direct method name are third * found: gdk.gdkwindow.method.lower.php * * @author Christian Weiske <[email protected]> */ class PhpGtkDoc_Search2_Index { protected static $arReserved = array( 'atk', 'gtk', 'gdk', 'scn', 'pango', 'method', 'property', 'prop', 'field', 'enum', 'signal', 'constructor' ); protected static $arReservedMethods = array( 'get', 'set' ); /** * Creates the search index. * Required parameters are the documentation directory * and the index file, as which the index shall be stored. */ public static function createIndex($strDocumentationDirectory, $strIndexFile) { if (!file_exists($strDocumentationDirectory) || !is_dir($strDocumentationDirectory)) { throw new Exception('Documentation directory does not exist: ' . $strDocumentationDirectory); } if ((file_exists($strIndexFile) && !is_writable($strIndexFile)) || (!file_exists($strIndexFile) && !is_writable(dirname($strIndexFile)))) { throw new Exception('Index file is not writable: ' . $strIndexFile); } file_put_contents($strIndexFile, serialize( self::buildIndexFromFiles( self::getFiles($strDocumentationDirectory), $strDocumentationDirectory ) ) ); }//public static function createIndex($strDocumentationDirectory, $strIndexFile) /** * Creates an index array from the given files. * The filenames are meant to be relative to the doc directory, * so that e.g. "gdk/gdk.functions.html" or * "gdk/gdk.gdkcolormap.method.get_screen.html" are in it. * * The index array has the following structure: * [keyword] * - [1] priority level * - [doc file 1] * - [doc file 2] * - [2] priority level * - [doc file 1] * - [doc file 2] * - [3] priority level * - [doc file 1] * - [doc file 2] * * Priorities: * 1 class names, tutorial names, ... * 2 methods, signals, enums * 3 methods which have the keyword in the class name * * @param array $arFiles Array with all the files * @param string $strDocumentationDirectory The directory which the file names are relative to * * @return array The index array, can be used with PhpGtkDoc_Search2 */ protected static function buildIndexFromFiles($arFiles, $strDocumentationDirectory) { $arIndex = array(); $arCamelCaseWords = self::getCamelCaseWords( self::getTitles( self::getTitleFiles( $arFiles ), $strDocumentationDirectory ) ); foreach ($arFiles as $strFile) { $strBaseFile = basename($strFile); $strBaseFile = substr($strBaseFile, 0, strrpos($strBaseFile, '.')); $arPieces = explode('.', $strBaseFile); //remove reserved words so that they are not indexed //$arPieces = array_diff($arPieces, self::$arReserved); //when uncommenting the last line, change all "$nCountWords > 2" to "$nCountWords > 1" $arNewPieces = array(); $nCountWords = count($arPieces); $nWordPos = -1; foreach ($arPieces as $strWord) { $nWordPos++;//the indices do not have constant values (array_diff) if ($nWordPos == $nCountWords - 1 && $nCountWords > 2) { //last word in the filename $nPriority = 2; } else { //not the last word in the filename $nPriority = 1; } $arNewPieces[$nPriority][] = $strWord;//the word itself if (isset($arCamelCaseWords[$strWord])) { $arNewPieces[$nPriority] = array_merge($arNewPieces[$nPriority], $arCamelCaseWords[$strWord]); } /* $strPrefix = substr($strWord, 0, 3); if ($strPrefix == 'gtk' || $strPrefix == 'gdk' || $strPrefix == 'atk' || $strPrefix == 'pan') { //pango is 5 chars, all others are 3 $nCutPos = $strPrefix == 'pan' ? 5 : 3; //classes have gtk or gdk at the beginning, e.g. gtkfixed or gtkoptionmenu $arNewPieces[$nPriority][] = substr($strWord, $nCutPos); if (isset($arCamelCaseWords[$strWord])) { $arNewPieces[$nPriority] = array_merge($arNewPieces[$nPriority], $arCamelCaseWords[$strWord]); } } */ $arMethodPieces = explode( '_', $strWord); if (count($arMethodPieces) > 1) { //if you want to remove "get" and "set" from the index, uncomment the following line //$arMethodPieces = array_diff( $arMethodPieces, self::$arReservedMethods); $arNewPieces[2] = array_merge($arNewPieces[2], $arMethodPieces); if (count( $arMethodPieces) > 2) { //that we have some partly connections like do_this from do_this_thing foreach ($arMethodPieces as $nId => $strPiece) { if ($nId < count($arMethodPieces) - 1) { $arNewPieces[2][] = $strPiece . '_' . $arMethodPieces[$nId + 1]; } } } } }//foreach piece //append the search words to the index array foreach ($arNewPieces as $nPriority => $arPriorityPieces) { foreach ($arPriorityPieces as $strPiece) { $arIndex[$strPiece][$nPriority][] = $strFile; } } } //sort the index | should speed up searching and is nice for debugging ksort($arIndex); return $arIndex; }//protected static function buildIndexFromFiles($arFiles) /** * Returns an array of file names from the documentation directory. * The file names are relative to the doc directory * * @param string $strDocumentationDirectory The directory of the compiled manual * @return array All the files in there */ protected static function getFiles($strDocumentationDirectory) { $strDir = getcwd(); chdir($strDocumentationDirectory); //php-gtk-web specific #$arFiles = glob('*/*.{html,php}', GLOB_BRACE); $arFiles = glob('*.php'); chdir($strDir); if (count($arFiles) == 0) { throw new Exception('No files found in ' . $strDocumentationDirectory); } return $arFiles; }//protected static function getFiles($strDocumentationDirectory) /** * Returns an array of filenames that should contain title tags * needed for the camelCase title splitter * * @param array $arFiles Array with files that (@see getFiles()) * @return array Array with files that should have needed titles */ protected static function getTitleFiles($arFiles) { $nFiles = count($arFiles); for ($nA = 0; $nA < $nFiles; $nA++) { //class files (gtk.gtktreeview.html) or enums (gtk.enum.selectionmode.html) if (!preg_match('/^[a-z0-9]+\\.(enum\\.)?[a-z0-9]+\\.[a-z]+$/', basename($arFiles[$nA]))) { unset($arFiles[$nA]); } } return $arFiles; }//protected static function getTitleFiles($arFiles) /** * Returns an array with the contents of the html title tags * in the given files * * @param array $arFiles The files to check * @param string $strDocumentationDirectory The directory the file names are relative to * * @return array Array of titles. */ protected static function getTitles($arFiles, $strDocumentationDirectory) { $arTitles = array(); foreach ($arFiles as $strFile) { if (substr($strFile, -4) === '.php') { //.php files (make phpweb) don't have a title header if (preg_match('/manualHeader\\(\"(.+)\"\\,/', file_get_contents($strDocumentationDirectory . '/' . $strFile), $arMatches)) { $arTitles[] = $arMatches[1]; } } else { if (preg_match('/<title>(.+)<\\/title>/', file_get_contents($strDocumentationDirectory . '/' . $strFile), $arMatches)) { $arTitles[] = $arMatches[1]; } } } return $arTitles; }//protected static function getTitles($arFiles, $strDocumentationDirectory) /** * Splits all the titles from camelCase into several * words (camel and case). * * @param array $arTitles The titles from the files * @return array Array with strtolower(word) => split words array */ protected static function getCamelCaseWords($arTitles) { $arSplit = array(); foreach ($arTitles as $strTitle) { if (strpos($strTitle, ' ') !== false) { //will be tutorial title or "Gtk functions" //we don't want this now. continue; } $arSplit[strtolower($strTitle)] = self::varyWords(self::splitCamelCaseWord($strTitle)); } return $arSplit; }//protected static function getCamelCaseWords($arTitles) /** * Splits a camelCaseWord into words (camel, case, word) * * @param string $strWord The word to split * @return array Array with lowercase words */ protected static function splitCamelCaseWord($strWord) { $strWords = preg_replace('/([A-Z])/', ' \\1', $strWord); return explode(' ', strtolower($strWords)); }//protected static function splitCamelCaseWord($strWord) /** * Makes variations of an array of words. * E.g. array(Gtk, Tree, View, Column) will get the variations * gtktree, gtktreeview, treeview, treeviewcolumn, viewcolumn * added to the word list * * @param string $arWords Array with words * @return array Array with words and their variations */ protected static function varyWords($arWords) { $arVariations = $arWords; for ($nA = 0; $nA < count($arWords); $nA++) { $strVariation = ''; for ($nB = $nA; $nB < count($arWords); $nB++) { $strVariation .= $arWords[$nB]; $arVariations[] = $strVariation; } } return array_unique($arVariations); }//protected static function varyWords($arWords) }//class PhpGtkDoc_Search2_Index ?> http://cvs.php.net/viewcvs.cgi/php-gtk-web/include/PhpGtkDoc/Search2/Result/Html.php?view=markup&rev=1.1 Index: php-gtk-web/include/PhpGtkDoc/Search2/Result/Html.php +++ php-gtk-web/include/PhpGtkDoc/Search2/Result/Html.php <?php require_once 'PhpGtkDoc/Search2.php'; /** * PHP-Gtk2-Doc result formatter for HTML * * @author Christian Weiske <[email protected]> */ class PhpGtkDoc_Search2_Result_Html { /** * The result types are replaced with * this titles in the output * * @var array */ protected static $arTypeTitles = array( 'class' => 'Classes', 'method' => 'Methods', 'property' => 'Properties', 'field' => 'Fields', 'signal' => 'Signals', 'enum' => 'Enums', 'constructor' => 'Constructors', 'tutorial' => 'Tutorials', 'unknown' => 'Unknown type' ); /** * The levels have this titles * * @var array */ protected static $arLevelTitles = array( 1 => 'Very relevant', 2 => 'Relevant', 3 => 'Not so relevant' ); /** * Formats the result as html and returns it. * * @param array $arResult The result you get from PhpGtkDoc_Search2::find() * @param string $strPrefix The prefix to put before the file names * @param string $strFilter The type filter (pass e.g. "method" to find methods only) * * @return string Nicely formatted ascii output */ public static function format($arResult, $strPrefix = '', $strFilter = '') { $strOutput = ''; foreach ($arResult as $nLevel => $arLevel) { $strLevelOutput = ''; foreach ($arLevel as $strType => $arFiles) { if ($strFilter != '' && $strType != $strFilter) { continue; } $strLevelOutput .= '<h4>' . self::$arTypeTitles[$strType] . "</h4>\r\n"; $strLevelOutput .= '<ul>'; foreach ($arFiles as $strFile) { $strLevelOutput .= '<li><a href="' . htmlspecialchars($strPrefix . $strFile) . '">' . htmlspecialchars(PhpGtkDoc_Search2::getFileTitle($strFile, $strType)) . "</a></li>\r\n"; } $strLevelOutput .= '</ul>'; } if ($strLevelOutput != '') { $strOutput .= '<h3>' . self::$arLevelTitles[$nLevel] . "</h3>\r\n" . $strLevelOutput; } } return $strOutput; }//public static function format($arResult, $strPrefix = '', $strFilter = '') }//class PhpGtkDoc_Search2_Result_Html ?>