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
?>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.