Completed
Push — master ( 302154...8eae75 )
by Lars
04:23
created

Intl::grapheme_strstr()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
dl 0
loc 4
c 1
b 0
f 1
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 3
crap 1
1
<?php
2
3
/*
4
 * Copyright (C) 2013 Nicolas Grekas - [email protected]
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the (at your option):
8
 * Apache License v2.0 (http://apache.org/licenses/LICENSE-2.0.txt), or
9
 * GNU General Public License v2.0 (http://gnu.org/licenses/gpl-2.0.txt).
10
 */
11
12
namespace voku\helper\shim;
13
14
/**
15
 * Partial intl implementation in pure PHP.
16
 *
17
 * Implemented:
18
 * - grapheme_extract  - Extract a sequence of grapheme clusters from a text buffer, which must be encoded in UTF-8
19
 * - grapheme_stripos  - Find position (in grapheme units) of first occurrence of a case-insensitive string
20
 * - grapheme_stristr  - Returns part of haystack string from the first occurrence of case-insensitive needle to the
21
 * end of haystack
22
 * - grapheme_strlen   - Get string length in grapheme units
23
 * - grapheme_strpos   - Find position (in grapheme units) of first occurrence of a string
24
 * - grapheme_strripos - Find position (in grapheme units) of last occurrence of a case-insensitive string
25
 * - grapheme_strrpos  - Find position (in grapheme units) of last occurrence of a string
26
 * - grapheme_strstr   - Returns part of haystack string from the first occurrence of needle to the end of haystack
27
 * - grapheme_substr   - Return part of a string
28
 *
29
 * @package voku\helper\shim
30
 */
31
class Intl
32
{
33
  // (CRLF|([ZWNJ-ZWJ]|T+|L*(LV?V+|LV|LVT)T*|L+|[^Control])[Extend]*|[Control])
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
34
  // This regular expression is a work around for http://bugs.exim.org/1279
35
  const GRAPHEME_CLUSTER_RX = '(?:\r\n|(?:[ -~\x{200C}\x{200D}]|[ᆨ-ᇹ]+|[ᄀ-ᅟ]*(?:[가개갸걔거게겨계고과괘괴교구궈궤귀규그긔기까깨꺄꺠꺼께껴꼐꼬꽈꽤꾀꾜꾸꿔꿰뀌뀨끄끠끼나내냐냬너네녀녜노놔놰뇌뇨누눠눼뉘뉴느늬니다대댜댸더데뎌뎨도돠돼되됴두둬뒈뒤듀드듸디따때땨떄떠떼뗘뗴또똬뙈뙤뚀뚜뚸뛔뛰뜌뜨띄띠라래랴럐러레려례로롸뢔뢰료루뤄뤠뤼류르릐리마매먀먜머메며몌모뫄뫠뫼묘무뭐뭬뮈뮤므믜미바배뱌뱨버베벼볘보봐봬뵈뵤부붜붸뷔뷰브븨비빠빼뺘뺴뻐뻬뼈뼤뽀뽜뽸뾔뾰뿌뿨쀄쀠쀼쁘쁴삐사새샤섀서세셔셰소솨쇄쇠쇼수숴쉐쉬슈스싀시싸쌔쌰썌써쎄쎠쎼쏘쏴쐐쐬쑈쑤쒀쒜쒸쓔쓰씌씨아애야얘어에여예오와왜외요우워웨위유으의이자재쟈쟤저제져졔조좌좨죄죠주줘줴쥐쥬즈즤지짜째쨔쨰쩌쩨쪄쪠쪼쫘쫴쬐쬬쭈쭤쮀쮜쮸쯔쯰찌차채챠챼처체쳐쳬초촤쵀최쵸추춰췌취츄츠츼치카캐캬컈커케켜켸코콰쾌쾨쿄쿠쿼퀘퀴큐크킈키타태탸턔터테텨톄토톼퇘퇴툐투퉈퉤튀튜트틔티파패퍄퍠퍼페펴폐포퐈퐤푀표푸풔풰퓌퓨프픠피하해햐햬허헤혀혜호화홰회효후훠훼휘휴흐희히]?[ᅠ-ᆢ]+|[가-힣])[ᆨ-ᇹ]*|[ᄀ-ᅟ]+|[^\p{Cc}\p{Cf}\p{Zl}\p{Zp}])[\p{Mn}\p{Me}\x{09BE}\x{09D7}\x{0B3E}\x{0B57}\x{0BBE}\x{0BD7}\x{0CC2}\x{0CD5}\x{0CD6}\x{0D3E}\x{0D57}\x{0DCF}\x{0DDF}\x{200C}\x{200D}\x{1D165}\x{1D16E}-\x{1D172}]*|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}])';
36
37
  /**
38
   * grapheme: extract
39
   *
40
   * @param string $s
41
   * @param int    $size
42
   * @param int    $type
43
   * @param int    $start
44
   * @param int    $next
45
   *
46
   * @return false|string
47
   */
48 2
  public static function grapheme_extract($s, $size, $type = GRAPHEME_EXTR_COUNT, $start = 0, &$next = 0)
49
  {
50 2
    if (!is_scalar($s)) {
51 2
      $hasError = false;
52
53 2
      set_error_handler(
54 2
          function() use (&$hasError) {
55 2
            $hasError = true;
56 2
          }
57
      );
58
59 2
      $next = substr($s, $start);
60 2
      restore_error_handler();
61
62 2
      if ($hasError) {
63 2
        substr($s, $start);
64 1
        $s = '';
65
      } else {
66 1
        $s = $next;
67
      }
68
69
    } else {
70 1
      $s = substr($s, $start);
71
    }
72 1
    $size = (int)$size;
73 1
    $type = (int)$type;
74 1
    $start = (int)$start;
75
76 1
    if (!isset($s[0]) || 0 > $size || 0 > $start || 0 > $type || 2 < $type) {
77 1
      return false;
78
    }
79
80 1
    if (0 === $size) {
81 1
      return '';
82
    }
83
84 1
    $next = $start;
85
86 1
    $s = preg_split(
87 1
        '/(' . GRAPHEME_CLUSTER_RX . ')/u',
88 1
        "\r\n" . $s,
89 1
        $size + 1,
90 1
        PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
91
    );
92
93 1
    if (!isset($s[1])) {
94
      return false;
95
    }
96
97 1
    $i = 1;
98 1
    $ret = '';
99
100
    do {
101 1
      if (GRAPHEME_EXTR_COUNT === $type) {
102 1
        --$size;
103 1
      } elseif (GRAPHEME_EXTR_MAXBYTES === $type) {
104 1
        $size -= strlen($s[$i]);
105
      } else {
106 1
        $size -= iconv_strlen($s[$i], 'UTF-8//IGNORE');
107
      }
108
109 1
      if ($size >= 0) {
110 1
        $ret .= $s[$i];
111
      }
112 1
    } while (isset($s[++$i]) && $size > 0);
113
114 1
    $next += strlen($ret);
115
116 1
    return $ret;
117
  }
118
119
  /**
120
   * grapheme: substr_workaround62759
121
   *
122
   * @param string $s
123
   * @param int    $start
124
   * @param int    $len
125
   *
126
   * @return false|string
127
   */
128 1
  public static function grapheme_substr_workaround62759($s, $start, $len)
129
  {
130
    // Intl based http://bugs.php.net/62759 and 55562 workaround
131
132 1
    if (2147483647 == $len) {
133 1
      return grapheme_substr($s, $start);
134
    }
135
136 1
    $s .= '';
137 1
    $slen = grapheme_strlen($s);
138 1
    $start = (int)$start;
139
140 1
    if (0 > $start) {
141 1
      $start += $slen;
142
    }
143
144 1
    if (0 > $start) {
145 1
      return false;
146
    }
147
148 1
    if ($start >= $slen) {
149 1
      return false;
150
    }
151
152 1
    $rem = $slen - $start;
153
154 1
    if (0 > $len) {
155 1
      $len += $rem;
156
    }
157
158 1
    if (0 === $len) {
159 1
      return '';
160
    }
161
162 1
    if (0 > $len) {
163 1
      return false;
164
    }
165
166 1
    if ($len > $rem) {
167 1
      $len = $rem;
168
    }
169
170 1
    return grapheme_substr($s, $start, $len);
171
  }
172
173
  /**
174
   * grapheme: strpos
175
   *
176
   * @param string $s
177
   * @param string $needle
178
   * @param int    $offset
179
   *
180
   * @return bool|int|null
181
   */
182 1
  public static function grapheme_strpos($s, $needle, $offset = 0)
183
  {
184 1
    return self::grapheme_position($s, $needle, $offset, 0);
185
  }
186
187
  /**
188
   * grapheme: position
189
   *
190
   * @param string $s
191
   * @param string $needle
192
   * @param int    $offset
193
   * @param int    $mode
194
   *
195
   * @return bool|int|null
196
   */
197 1
  protected static function grapheme_position($s, $needle, $offset, $mode)
198
  {
199 1
    if (!preg_match('/./us', $needle .= '')) {
200 1
      return false;
201
    }
202
203 1
    if (!preg_match('/./us', $s .= '')) {
204
      return false;
205
    }
206
207 1
    if ($offset > 0) {
208 1
      $s = self::grapheme_substr($s, $offset);
209 1
    } elseif ($offset < 0) {
210 1
      $offset = 0;
211
    }
212
213
    switch ($mode) {
214 1
      case 0:
215 1
        $needle = iconv_strpos($s, $needle, 0, 'UTF-8');
0 ignored issues
show
Security Bug introduced by
It seems like $s defined by self::grapheme_substr($s, $offset) on line 208 can also be of type false; however, iconv_strpos() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
216 1
        break;
217
      case 1:
218 1
        $needle = mb_stripos($s, $needle, 0, 'UTF-8');
0 ignored issues
show
Security Bug introduced by
It seems like $s defined by self::grapheme_substr($s, $offset) on line 208 can also be of type false; however, mb_stripos() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
219 1
        break;
220 1
      case 2:
221 1
        $needle = iconv_strrpos($s, $needle, 'UTF-8');
0 ignored issues
show
Security Bug introduced by
It seems like $s defined by self::grapheme_substr($s, $offset) on line 208 can also be of type false; however, iconv_strrpos() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
222 1
        break;
223
      default:
224 1
        $needle = mb_strripos($s, $needle, 0, 'UTF-8');
0 ignored issues
show
Security Bug introduced by
It seems like $s defined by self::grapheme_substr($s, $offset) on line 208 can also be of type false; however, mb_strripos() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
225 1
        break;
226
    }
227
228 1
    return $needle ? self::grapheme_strlen(iconv_substr($s, 0, $needle, 'UTF-8')) + $offset : $needle;
0 ignored issues
show
Security Bug introduced by
It seems like $s defined by self::grapheme_substr($s, $offset) on line 208 can also be of type false; however, iconv_substr() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
229
  }
230
231
  /**
232
   * grapheme: substr
233
   *
234
   * @param string $s
235
   * @param int    $start
236
   * @param int    $len
237
   *
238
   * @return false|string
239
   */
240 2
  public static function grapheme_substr($s, $start, $len = 2147483647)
241
  {
242 2
    preg_match_all('/' . GRAPHEME_CLUSTER_RX . '/u', $s, $s);
243
244 2
    $slen = count($s[0]);
245 2
    $start = (int)$start;
246
247 2
    if (0 > $start) {
248 1
      $start += $slen;
249
    }
250
251 2
    if (0 > $start) {
252 1
      return false;
253
    }
254
255 2
    if ($start >= $slen) {
256 2
      return false;
257
    }
258
259 1
    $rem = $slen - $start;
260
261 1
    if (0 > $len) {
262 1
      $len += $rem;
263
    }
264
265 1
    if (0 === $len) {
266 1
      return '';
267
    }
268
269 1
    if (0 > $len) {
270 1
      return false;
271
    }
272
273 1
    if ($len > $rem) {
274 1
      $len = $rem;
275
    }
276
277 1
    return implode('', array_slice($s[0], $start, $len));
278
  }
279
280
  /**
281
   * grapheme: strlen
282
   *
283
   * @param string $str
284
   *
285
   * @return integer|null
286
   */
287 2
  public static function grapheme_strlen($str)
288
  {
289 2
    preg_replace('/' . GRAPHEME_CLUSTER_RX . '/u', '', $str, -1, $len);
290
291 2
    return 0 === $len && '' !== $str ? null : $len;
292
  }
293
294
  /**
295
   * grapheme: stripos
296
   *
297
   * @param string $str
298
   * @param string $needle
299
   * @param int    $offset
300
   *
301
   * @return bool|int|null
302
   */
303 1
  public static function grapheme_stripos($str, $needle, $offset = 0)
304
  {
305 1
    return self::grapheme_position($str, $needle, $offset, 1);
306
  }
307
308
  /**
309
   * grapheme: strrpos
310
   *
311
   * @param string $str
312
   * @param string $needle
313
   * @param int    $offset
314
   *
315
   * @return bool|int|null
316
   */
317 1
  public static function grapheme_strrpos($str, $needle, $offset = 0)
318
  {
319 1
    return self::grapheme_position($str, $needle, $offset, 2);
320
  }
321
322
  /**
323
   * grapheme: strripos
324
   *
325
   * @param string $str
326
   * @param string $needle
327
   * @param int    $offset
328
   *
329
   * @return bool|int|null
330
   */
331 1
  public static function grapheme_strripos($str, $needle, $offset = 0)
332
  {
333 1
    return self::grapheme_position($str, $needle, $offset, 3);
334
  }
335
336
  /**
337
   * grapheme: stristr
338
   *
339
   * @param string $str
340
   * @param string $needle
341
   * @param bool   $before_needle
342
   *
343
   * @return false|string
344
   */
345 1
  public static function grapheme_stristr($str, $needle, $before_needle = false)
346
  {
347 1
    return mb_stristr($str, $needle, $before_needle, 'UTF-8');
348
  }
349
350
  /**
351
   * grapheme: strstr
352
   *
353
   * @param string $str
354
   * @param string $needle
355
   * @param bool   $before_needle
356
   *
357
   * @return false|string
358
   */
359 1
  public static function grapheme_strstr($str, $needle, $before_needle = false)
360
  {
361 1
    return mb_strstr($str, $needle, $before_needle, 'UTF-8');
362
  }
363
}
364