Version: current

      1 <?php
      2 /**
      3  * This file is part of phpMorphy library
      4  *
      5  * Copyright c 2007-2008 Kamaev Vladimir <heromantor@users.sourceforge.net>
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General Public
     18  * License along with this library; if not, write to the
     19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     20  * Boston, MA 02111-1307, USA.
     21  */
     22
     23 interface phpMorphy_Fsa_Interface {
     24     /**
     25      * Return root transition of fsa
     26      * @return array
     27      */
     28     function getRootTrans();
     29
     30     /**
     31      * Returns root state object
     32      * @return
     33      */
     34     function getRootState();
     35
     36     /**
     37      * Returns alphabet i.e. all chars used in automat
     38      * @return array
     39      */
     40     function getAlphabet();
     41
     42     /**
     43      * Return annotation for given transition(if annotation flag is set for given trans)
     44      *
     45      * @param array $trans
     46      * @return string
     47      */
     48     function getAnnot($trans);
     49
     50     /**
     51      * Find word in automat
     52      *
     53      * @param mixed $trans starting transition
     54      * @param string $word
     55      * @param bool $readAnnot read annot or simple check if word exists in automat
     56      * @return bool TRUE if word is found, FALSE otherwise
     57      */
     58     function walk($trans, $word, $readAnnot = true);
     59
     60     /**
     61      * Traverse automat and collect words
     62      * For each found words $callback function invoked with follow arguments:
     63      * call_user_func($callback, $word, $annot)
     64      * when $readAnnot is FALSE then $annot arg is always NULL
     65      *
     66      * @param mixed $startNode
     67      * @param mixed $callback callback function(in php format callback i.e. string or array(obj, method) or array(class, method)
     68      * @param bool $readAnnot read annot
     69      * @param string $path string to be append to all words
     70      */
     71     function collect($startNode, $callback, $readAnnot = true, $path = '');
     72
     73     /**
     74      * Read state at given index
     75      *
     76      * @param int $index
     77      * @return array
     78      */
     79     function readState($index);
     80
     81     /**
     82      * Unpack transition from binary form to array
     83      *
     84      * @param mixed $rawTranses may be array for convert more than one transitions
     85      * @return array
     86      */
     87     function unpackTranses($rawTranses);
     88 }
     89
     90 abstract class phpMorphy_Fsa implements phpMorphy_Fsa_Interface {
     91     const HEADER_SIZE = 128;
     92
     93     protected
     94         $resource,
     95         $header,
     96         $fsa_start,
     97         $root_trans,
     98         $alphabet;
     99
    100     protected function phpMorphy_Fsa($resource, $header) {
    101         $this->resource = $resource;
    102         $this->header = $header;
    103         $this->fsa_start = $header['fsa_offset'];
    104         $this->root_trans = $this->readRootTrans();
    105     }
    106
    107     // static
    108     static function create(phpMorphy_Storage $storage, $lazy) {
    109         if($lazy) {
    110             return new phpMorphy_Fsa_Proxy($storage);
    111         }
    112
    113         $header = phpMorphy_Fsa::readHeader(
    114             $storage->read(0, self::HEADER_SIZE, true)
    115         );
    116
    117         if(!phpMorphy_Fsa::validateHeader($header)) {
    118             throw new phpMorphy_Exception('Invalid fsa format');
    119         }
    120
    121         if($header['flags']['is_sparse']) {
    122             $type = 'sparse';
    123         } else if($header['flags']['is_tree']) {
    124             $type = 'tree';
    125         } else {
    126             throw new phpMorphy_Exception('Only sparse or tree fsa`s supported');
    127         }
    128
    129         $storage_type = $storage->getTypeAsString();
    130         $file_path = dirname(__FILE__) . "/access/fsa_{$type}_{$storage_type}.php";
    131         $clazz = 'phpMorphy_Fsa_' . ucfirst($type) . '_' . ucfirst($storage_type);
    132
    133         require_once($file_path);
    134         return new $clazz(
    135             $storage->getResource(),
    136             $header
    137         );
    138     }
    139
    140     function getRootTrans() { return $this->root_trans; }
    141
    142     function getRootState() {
    143         return $this->createState($this->getRootStateIndex());
    144     }
    145
    146     function getAlphabet() {
    147         if(!isset($this->alphabet)) {
    148             $this->alphabet = str_split($this->readAlphabet());
    149         }
    150
    151         return $this->alphabet;
    152     }
    153
    154     protected function createState($index) {
    155         require_once(PHPMORPHY_DIR . '/fsa/fsa_state.php');
    156         return new phpMorphy_State($this, $index);
    157     }
    158
    159     static protected function readHeader($headerRaw) {
    160         if($GLOBALS['__phpmorphy_strlen']($headerRaw) != self::HEADER_SIZE) {
    161             throw new phpMorphy_Exception('Invalid header string given');
    162         }
    163
    164         $header = unpack(
    165             'a4fourcc/Vver/Vflags/Valphabet_offset/Vfsa_offset/Vannot_offset/Valphabet_size/Vtranses_count/Vannot_length_size/' .
    166             'Vannot_chunk_size/Vannot_chunks_count/Vchar_size/Vpadding_size/Vdest_size/Vhash_size',
    167             $headerRaw
    168         );
    169
    170         if(false === $header) {
    171             throw new phpMorphy_Exception('Can`t unpack header');
    172         }
    173
    174         $flags = array();
    175         $raw_flags = $header['flags'];
    176         $flags['is_tree'] =  $raw_flags & 0x01 ? true : false;
    177         $flags['is_hash'] =  $raw_flags & 0x02 ? true : false;
    178         $flags['is_sparse'] = $raw_flags & 0x04 ? true : false;
    179         $flags['is_be'] =  $raw_flags & 0x08 ? true : false;
    180
    181         $header['flags'] = $flags;
    182
    183         $header['trans_size'] = $header['char_size'] + $header['padding_size'] + $header['dest_size'] + $header['hash_size'];
    184
    185         return $header;
    186     }
    187
    188     // static
    189     static protected function validateHeader($header) {
    190         if(
    191             'meal' != $header['fourcc'] ||
    192             3 != $header['ver'] ||
    193             $header['char_size'] != 1 ||
    194             $header['padding_size'] > 0 ||
    195             $header['dest_size'] != 3 ||
    196             $header['hash_size'] != 0 ||
    197             $header['annot_length_size'] != 1 ||
    198             $header['annot_chunk_size'] != 1 ||
    199             $header['flags']['is_be'] ||
    200             $header['flags']['is_hash'] ||
    201             1 == 0
    202         ) {
    203             return false;
    204         }
    205
    206         return true;
    207     }
    208
    209     protected function getRootStateIndex() { return 0; }
    210
    211     abstract protected function readRootTrans();
    212     abstract protected function readAlphabet();
    213 };
    214
    215 class phpMorphy_Fsa_WordsCollector {
    216     protected
    217         $items = array(),
    218         $limit;
    219
    220     function phpMorphy_Fsa_WordsCollector($collectLimit) {
    221         $this->limit = $collectLimit;
    222     }
    223
    224     function collect($word, $annot) {
    225         if(count($this->items) < $this->limit) {
    226             $this->items[$word] = $annot;
    227             return true;
    228         } else {
    229             return false;
    230         }
    231     }
    232
    233     function getItems() { return $this->items; }
    234     function clear() { $this->items = array(); }
    235     function getCallback() { return array($this, 'collect'); }
    236 };
    237
    238 class phpMorphy_Fsa_Decorator implements phpMorphy_Fsa_Interface {
    239     protected $fsa;
    240
    241     function phpMorphy_Fsa_Decorator(phpMorphy_Fsa_Interface $fsa) {
    242         $this->fsa = $fsa;
    243     }
    244
    245     function getRootTrans() { return $this->fsa->getRootTrans(); }
    246     function getRootState() { return $this->fsa->getRootState(); }
    247     function getAlphabet() { return $this->fsa->getAlphabet(); }
    248     function getAnnot($trans) { return $this->fsa->getAnnot($trans); }
    249     function walk($start, $word, $readAnnot = true) { return $this->fsa->walk($start, $word, $readAnnot); }
    250     function collect($start, $callback, $readAnnot = true, $path = '') { return $this->fsa->collect($start, $callback, $readAnnot, $path); }
    251     function readState($index) { return $this->fsa->readState($index); }
    252     function unpackTranses($transes) { return $this->fsa->unpackTranses($transes); }
    253 };
    254
    255 class phpMorphy_Fsa_Proxy extends phpMorphy_Fsa_Decorator {
    256     protected $storage;
    257
    258     function __construct(phpMorphy_Storage $storage) {
    259         $this->storage = $storage;
    260         unset($this->fsa);
    261     }
    262
    263     function __get($propName) {
    264         if($propName == 'fsa') {
    265             $this->fsa = phpMorphy_Fsa::create($this->storage, false);
    266
    267             unset($this->storage);
    268             return $this->fsa;
    269         }
    270
    271         throw new phpMorphy_Exception("Unknown prop name '$propName'");
    272     }
    273 }
    274

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -