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_GramInfo_Interace {
     24     /**
     25      * Returns langugage for graminfo file
     26      * @return string
     27      */
     28     function getLocale();
     29
     30     /**
     31      * Return encoding for graminfo file
     32      * @return string
     33      */
     34     function getEncoding();
     35
     36     /**
     37      * Return size of character (cp1251 - 1, utf8 - 1, utf16 - 2, utf32 - 4 etc)
     38      * @return int
     39      */
     40     function getCharSize();
     41
     42     /**
     43      * Return end of string value (usually string with \0 value of char_size + 1 length)
     44      * @return string
     45      */
     46     function getEnds();
     47
     48     /**
     49      * Reads graminfo header
     50      *
     51      * @param int $offset
     52      * @return array
     53      */
     54     function readGramInfoHeader($offset);
     55
     56     /**
     57      * Returns size of header struct
     58      */
     59     function getGramInfoHeaderSize();
     60
     61     /**
     62      * Read ancodes section for header retrieved with readGramInfoHeader
     63      *
     64      * @param array $info
     65      * @return array
     66      */
     67     function readAncodes($info);
     68
     69     /**
     70      * Read flexias section for header retrieved with readGramInfoHeader
     71      *
     72      * @param array $info
     73      * @return array
     74      */
     75     function readFlexiaData($info);
     76
     77     /**
     78      * Read all graminfo headers offsets, which can be used latter for readGramInfoHeader method
     79      * @return array
     80      */
     81     function readAllGramInfoOffsets();
     82
     83     function getHeader();
     84     function readAllPartOfSpeech();
     85     function readAllGrammems();
     86     function readAllAncodes();
     87 }
     88
     89 abstract class phpMorphy_GramInfo implements phpMorphy_GramInfo_Interace {
     90     const HEADER_SIZE = 128;
     91
     92     protected
     93         $resource,
     94         $header,
     95         $ends,
     96         $ends_size;
     97
     98     protected function phpMorphy_GramInfo($resource, $header) {
     99         $this->resource = $resource;
    100         $this->header = $header;
    101
    102         $this->ends = str_repeat("\0", $header['char_size'] + 1);
    103         $this->ends_size = $GLOBALS['__phpmorphy_strlen']($this->ends);
    104     }
    105
    106     static function create(phpMorphy_Storage $storage, $lazy) {
    107         if($lazy) {
    108             return new phpMorphy_GramInfo_Proxy($storage);
    109         }
    110
    111         $header = phpMorphy_GramInfo::readHeader(
    112             $storage->read(0, self::HEADER_SIZE)
    113         );
    114
    115         if(!phpMorphy_GramInfo::validateHeader($header)) {
    116             throw new phpMorphy_Exception('Invalid graminfo format');
    117         }
    118
    119         $storage_type = $storage->getTypeAsString();
    120         $file_path = dirname(__FILE__) . "/access/graminfo_{$storage_type}.php";
    121         $clazz = 'phpMorphy_GramInfo_' . ucfirst($storage_type);
    122
    123         require_once($file_path);
    124         return new $clazz($storage->getResource(), $header);
    125     }
    126
    127     function getLocale() {
    128         return $this->header['lang'];
    129     }
    130
    131     function getEncoding() {
    132         return $this->header['encoding'];
    133     }
    134
    135     function getCharSize() {
    136         return $this->header['char_size'];
    137     }
    138
    139     function getEnds() {
    140         return $this->ends;
    141     }
    142
    143     function getHeader() {
    144         return $this->header;
    145     }
    146
    147     static protected function readHeader($headerRaw) {
    148         $header = unpack(
    149             'Vver/Vis_be/Vflex_count_old/' .
    150             'Vflex_offset/Vflex_size/Vflex_count/Vflex_index_offset/Vflex_index_size/' .
    151             'Vposes_offset/Vposes_size/Vposes_count/Vposes_index_offset/Vposes_index_size/' .
    152             'Vgrammems_offset/Vgrammems_size/Vgrammems_count/Vgrammems_index_offset/Vgrammems_index_size/' .
    153             'Vancodes_offset/Vancodes_size/Vancodes_count/Vancodes_index_offset/Vancodes_index_size/' .
    154             'Vchar_size/',
    155             $headerRaw
    156         );
    157
    158         $offset = 24 * 4;
    159         $len = ord($GLOBALS['__phpmorphy_substr']($headerRaw, $offset++, 1));
    160         $header['lang'] = rtrim($GLOBALS['__phpmorphy_substr']($headerRaw, $offset, $len));
    161
    162         $offset += $len;
    163
    164         $len = ord($GLOBALS['__phpmorphy_substr']($headerRaw, $offset++, 1));
    165         $header['encoding'] = rtrim($GLOBALS['__phpmorphy_substr']($headerRaw, $offset, $len));
    166
    167         return $header;
    168     }
    169
    170     static protected function validateHeader($header) {
    171         if(
    172             3 != $header['ver'] ||
    173             1 == $header['is_be']
    174         ) {
    175             return false;
    176         }
    177
    178         return true;
    179     }
    180
    181     protected function cleanupCString($string) {
    182         if(false !== ($pos = $GLOBALS['__phpmorphy_strpos']($string, $this->ends))) {
    183             $string = $GLOBALS['__phpmorphy_substr']($string, 0, $pos);
    184         }
    185
    186         return $string;
    187     }
    188
    189     abstract protected function readSectionIndex($offset, $count);
    190
    191     protected function readSectionIndexAsSize($offset, $count, $total_size) {
    192         if(!$count) {
    193             return array();
    194         }
    195
    196         $index = $this->readSectionIndex($offset, $count);
    197         $index[$count] = $index[0] + $total_size;
    198
    199         for($i = 0; $i < $count; $i++) {
    200             $index[$i] = $index[$i + 1] - $index[$i];
    201         }
    202
    203         unset($index[$count]);
    204
    205         return $index;
    206     }
    207 };
    208
    209 class phpMorphy_GramInfo_Decorator implements phpMorphy_GramInfo_Interace {
    210     protected $info;
    211
    212     function phpMorphy_GramInfo_Decorator(phpMorphy_GramInfo_Interace $info) {
    213         $this->info = $info;
    214     }
    215
    216     function readGramInfoHeader($offset) { return $this->info->readGramInfoHeader($offset); }
    217     function getGramInfoHeaderSize() { return $this->info->getGramInfoHeaderSize($offset); }
    218     function readAncodes($info) { return $this->info->readAncodes($info); }
    219     function readFlexiaData($info) { return $this->info->readFlexiaData($info); }
    220     function readAllGramInfoOffsets() { return $this->info->readAllGramInfoOffsets(); }
    221     function readAllPartOfSpeech() { return $this->info->readAllPartOfSpeech(); }
    222     function readAllGrammems() { return $this->info->readAllGrammems(); }
    223     function readAllAncodes() { return $this->info->readAllAncodes(); }
    224
    225     function getLocale()  { return $this->info->getLocale(); }
    226     function getEncoding()  { return $this->info->getEncoding(); }
    227     function getCharSize()  { return $this->info->getCharSize(); }
    228     function getEnds() { return $this->info->getEnds(); }
    229     function getHeader() { return $this->info->getHeader(); }
    230 }
    231
    232 class phpMorphy_GramInfo_Proxy extends phpMorphy_GramInfo_Decorator {
    233     protected $storage;
    234
    235     function __construct(phpMorphy_Storage $storage) {
    236         $this->storage = $storage;
    237         unset($this->info);
    238     }
    239
    240     function __get($propName) {
    241         if($propName == 'info') {
    242             $this->info = phpMorphy_GramInfo::create($this->storage, false);
    243             unset($this->storage);
    244             return $this->info;
    245         }
    246
    247         throw new phpMorphy_Exception("Unknown prop name '$propName'");
    248     }
    249 }
    250
    251 class phpMorphy_GramInfo_Proxy_WithHeader extends phpMorphy_GramInfo_Proxy {
    252     protected
    253         $cache,
    254         $ends;
    255
    256     function __construct(phpMorphy_Storage $storage, $cacheFile) {
    257         parent::__construct($storage);
    258
    259         $this->cache = $this->readCache($cacheFile);
    260         $this->ends = str_repeat("\0", $this->getCharSize() + 1);
    261     }
    262
    263     protected function readCache($fileName) {
    264         if(!is_array($result = include($fileName))) {
    265             throw new phpMorphy_Exception("Can`t get header cache from '$fileName' file'");
    266         }
    267
    268         return $result;
    269     }
    270
    271     function getLocale()  {
    272         return $this->cache['lang'];
    273     }
    274
    275     function getEncoding()  {
    276         return $this->cache['encoding'];
    277     }
    278
    279     function getCharSize()  {
    280         return $this->cache['char_size'];
    281     }
    282
    283     function getEnds() {
    284         return $this->ends;
    285     }
    286
    287     function getHeader() {
    288         return $this->cache;
    289     }
    290 }
    291
    292 class phpMorphy_GramInfo_RuntimeCaching extends phpMorphy_GramInfo_Decorator {
    293     protected
    294         $flexia = array(),
    295         $ancodes = array();
    296
    297     function readFlexiaData($info) {
    298         $offset = $info['offset'];
    299
    300         if(!isset($this->flexia_all[$offset])) {
    301             $this->flexia_all[$offset] = $this->info->readFlexiaData($info);
    302         }
    303
    304         return $this->flexia_all[$offset];
    305     }
    306 }
    307
    308 class phpMorphy_GramInfo_AncodeCache extends phpMorphy_GramInfo_Decorator {
    309     public
    310         $hits = 0,
    311         $miss = 0;
    312
    313     protected
    314         $cache;
    315
    316     function __construct(phpMorphy_GramInfo_Interace $inner, $resource) {
    317         parent::__construct($inner);
    318
    319         if(false === ($this->cache = unserialize($resource->read(0, $resource->getFileSize())))) {
    320             throw new phpMorphy_Exception("Can`t read ancodes cache");
    321         }
    322     }
    323
    324     function readAncodes($info) {
    325         $offset = $info['offset'];
    326
    327         if(isset($this->cache[$offset])) {
    328             $this->hits++;
    329
    330             return $this->cache[$offset];
    331         } else {
    332             // in theory misses never occur
    333             $this->miss++;
    334
    335             return parent::readAncodes($info);
    336         }
    337     }
    338 }

[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. -