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

Iconv::strlen2()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 22
Code Lines 13

Duplication

Lines 3
Ratio 13.64 %

Code Coverage

Tests 12
CRAP Score 6.0164

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 3
loc 22
ccs 12
cts 13
cp 0.9231
rs 8.6738
cc 6
eloc 13
nc 8
nop 2
crap 6.0164
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
 * iconv implementation in pure PHP, UTF-8 centric.
16
 *
17
 * Implemented:
18
 * - iconv              - Convert string to requested character encoding
19
 * - iconv_mime_decode  - Decodes a MIME header field
20
 * - iconv_mime_decode_headers - Decodes multiple MIME header fields at once
21
 * - iconv_get_encoding - Retrieve internal configuration variables of iconv extension
22
 * - iconv_set_encoding - Set current setting for character encoding conversion
23
 * - iconv_mime_encode  - Composes a MIME header field
24
 * - ob_iconv_handler   - Convert character encoding as output buffer handler
25
 * - iconv_strlen       - Returns the character count of string
26
 * - iconv_strpos       - Finds position of first occurrence of a needle within a haystack
27
 * - iconv_strrpos      - Finds the last occurrence of a needle within a haystack
28
 * - iconv_substr       - Cut out part of a string
29
 *
30
 * Charsets available for convertion are defined by files
31
 * in the charset/ directory and by Iconv::$alias below.
32
 * You're welcome to send back any addition you make.
33
 *
34
 * @package voku\helper\shim
35
 */
36
class Iconv
37
{
38
  const ERROR_ILLEGAL_CHARACTER = 'iconv(): Detected an illegal character in input string';
39
  const ERROR_WRONG_CHARSET     = 'iconv(): Wrong charset, conversion from `%s\' to `%s\' is not allowed';
40
41
  public static $input_encoding    = 'utf-8';
42
  public static $output_encoding   = 'utf-8';
43
  public static $internal_encoding = 'utf-8';
44
  public static $translit_map      = array();
45
  public static $convert_map       = array();
46
  public static $error_handler, $last_error, $is_valid_utf8;
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
47
  public static $ulen_mask         = array(
48
      "\xC0" => 2,
49
      "\xD0" => 2,
50
      "\xE0" => 3,
51
      "\xF0" => 4,
52
  );
53
  protected static $alias = array(
54
      'utf8'        => 'utf-8',
55
      'ascii'       => 'us-ascii',
56
      'tis-620'     => 'iso-8859-11',
57
      'cp1250'      => 'windows-1250',
58
      'cp1251'      => 'windows-1251',
59
      'cp1252'      => 'windows-1252',
60
      'cp1253'      => 'windows-1253',
61
      'cp1254'      => 'windows-1254',
62
      'cp1255'      => 'windows-1255',
63
      'cp1256'      => 'windows-1256',
64
      'cp1257'      => 'windows-1257',
65
      'cp1258'      => 'windows-1258',
66
      'shift-jis'   => 'cp932',
67
      'shift_jis'   => 'cp932',
68
      'latin1'      => 'iso-8859-1',
69
      'latin2'      => 'iso-8859-2',
70
      'latin3'      => 'iso-8859-3',
71
      'latin4'      => 'iso-8859-4',
72
      'latin5'      => 'iso-8859-9',
73
      'latin6'      => 'iso-8859-10',
74
      'latin7'      => 'iso-8859-13',
75
      'latin8'      => 'iso-8859-14',
76
      'latin9'      => 'iso-8859-15',
77
      'latin10'     => 'iso-8859-16',
78
      'iso8859-1'   => 'iso-8859-1',
79
      'iso8859-2'   => 'iso-8859-2',
80
      'iso8859-3'   => 'iso-8859-3',
81
      'iso8859-4'   => 'iso-8859-4',
82
      'iso8859-5'   => 'iso-8859-5',
83
      'iso8859-6'   => 'iso-8859-6',
84
      'iso8859-7'   => 'iso-8859-7',
85
      'iso8859-8'   => 'iso-8859-8',
86
      'iso8859-9'   => 'iso-8859-9',
87
      'iso8859-10'  => 'iso-8859-10',
88
      'iso8859-11'  => 'iso-8859-11',
89
      'iso8859-12'  => 'iso-8859-12',
90
      'iso8859-13'  => 'iso-8859-13',
91
      'iso8859-14'  => 'iso-8859-14',
92
      'iso8859-15'  => 'iso-8859-15',
93
      'iso8859-16'  => 'iso-8859-16',
94
      'iso_8859-1'  => 'iso-8859-1',
95
      'iso_8859-2'  => 'iso-8859-2',
96
      'iso_8859-3'  => 'iso-8859-3',
97
      'iso_8859-4'  => 'iso-8859-4',
98
      'iso_8859-5'  => 'iso-8859-5',
99
      'iso_8859-6'  => 'iso-8859-6',
100
      'iso_8859-7'  => 'iso-8859-7',
101
      'iso_8859-8'  => 'iso-8859-8',
102
      'iso_8859-9'  => 'iso-8859-9',
103
      'iso_8859-10' => 'iso-8859-10',
104
      'iso_8859-11' => 'iso-8859-11',
105
      'iso_8859-12' => 'iso-8859-12',
106
      'iso_8859-13' => 'iso-8859-13',
107
      'iso_8859-14' => 'iso-8859-14',
108
      'iso_8859-15' => 'iso-8859-15',
109
      'iso_8859-16' => 'iso-8859-16',
110
      'iso88591'    => 'iso-8859-1',
111
      'iso88592'    => 'iso-8859-2',
112
      'iso88593'    => 'iso-8859-3',
113
      'iso88594'    => 'iso-8859-4',
114
      'iso88595'    => 'iso-8859-5',
115
      'iso88596'    => 'iso-8859-6',
116
      'iso88597'    => 'iso-8859-7',
117
      'iso88598'    => 'iso-8859-8',
118
      'iso88599'    => 'iso-8859-9',
119
      'iso885910'   => 'iso-8859-10',
120
      'iso885911'   => 'iso-8859-11',
121
      'iso885912'   => 'iso-8859-12',
122
      'iso885913'   => 'iso-8859-13',
123
      'iso885914'   => 'iso-8859-14',
124
      'iso885915'   => 'iso-8859-15',
125
      'iso885916'   => 'iso-8859-16',
126
  );
127
128
  /**
129
   * @param string $str
130
   * @param int    $mode
131
   * @param string $charset
132
   *
133
   * @return array|bool
134
   */
135 1
  public static function iconv_mime_decode_headers($str, $mode = 0, $charset = INF)
136
  {
137 1
    INF === $charset && $charset = self::$internal_encoding;
138
139 1
    false !== strpos($str, "\r") && $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n");
140 1
    $str = explode("\n\n", $str, 2);
141
142 1
    $headers = array();
143
144 1
    $str = preg_split('/\n(?![ \t])/', $str[0]);
145
    // $str as $str ??
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
146 1
    foreach ($str as $str) {
147 1
      $str = self::iconv_mime_decode($str, $mode, $charset);
148
149 1
      if (false === $str) {
150
        return false;
151
      }
152 1
      $str = explode(':', $str, 2);
153
154 1
      if (2 === count($str)) {
155 1
        if (isset($headers[$str[0]])) {
156 1
          is_array($headers[$str[0]]) || $headers[$str[0]] = array($headers[$str[0]]);
157 1
          $headers[$str[0]][] = ltrim($str[1]);
158
        } else {
159 1
          $headers[$str[0]] = ltrim($str[1]);
160
        }
161
      }
162
    }
163
164 1
    return $headers;
165
  }
166
167
  /**
168
   * @param string $str
169
   * @param int    $mode
170
   * @param string $charset
171
   *
172
   * @return false|string
173
   */
174 2
  public static function iconv_mime_decode($str, $mode = 0, $charset = INF)
175
  {
176 2
    INF === $charset && $charset = self::$internal_encoding;
177
178 2
    if (ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) {
179 2
      $charset .= '//IGNORE';
180
    }
181
182 2
    false !== strpos($str, "\r") && $str = strtr(str_replace("\r\n", "\n", $str), "\r", "\n");
183 2
    $str = preg_split('/\n(?![ \t])/', rtrim($str), 2);
184 2
    $str = preg_replace('/[ \t]*\n[ \t]+/', ' ', rtrim($str[0]));
185 2
    $str = preg_split('/=\?([^?]+)\?([bqBQ])\?(.*?)\?=/', $str, -1, PREG_SPLIT_DELIM_CAPTURE);
186
187 2
    $result = self::iconv('utf-8', $charset, $str[0]);
188 2
    if (false === $result) {
189
      return false;
190
    }
191
192 2
    $i = 1;
193 2
    $len = count($str);
194
195 2
    while ($i < $len) {
196 2
      $c = strtolower($str[$i]);
197 2
      if ((ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode)
198 2
          && 'utf-8' !== $c
199 2
          && !isset(self::$alias[$c])
200 2
          && !static::loadMap('from.', $c, $d)
0 ignored issues
show
Documentation introduced by
$d is of type string|false, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
201
      ) {
202 1
        $d = false;
203 2
      } elseif ('B' === strtoupper($str[$i + 1])) {
204 2
        $d = base64_decode($str[$i + 2]);
205
      } else {
206 2
        $d = rawurldecode(strtr(str_replace('%', '%25', $str[$i + 2]), '=_', '% '));
207
      }
208
209 2
      if (false !== $d) {
210 2
        $result .= self::iconv($c, $charset, $d);
211 2
        $d = self::iconv('utf-8', $charset, $str[$i + 3]);
212 2
        if ('' !== trim($d)) {
213 2
          $result .= $d;
214
        }
215 1
      } elseif (ICONV_MIME_DECODE_CONTINUE_ON_ERROR & $mode) {
216 1
        $result .= "=?{$str[$i]}?{$str[$i + 1]}?{$str[$i + 2]}?={$str[$i + 3]}";
217
      } else {
218
        $result = false;
219
        break;
220
      }
221
222 2
      $i += 4;
223
    }
224
225 2
    return $result;
226
  }
227
228
  /**
229
   * @param string $in_charset
230
   * @param string $out_charset
231
   * @param string $str
232
   *
233
   * @return string|false
234
   */
235 3
  public static function iconv($in_charset, $out_charset, $str)
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
236
  {
237 3
    if ('' === $str .= '') {
238 1
      return '';
239
    }
240
241
    // Prepare for //IGNORE and //TRANSLIT
242
243 3
    $TRANSLIT = $IGNORE = '';
244
245 3
    $out_charset = strtolower($out_charset);
246 3
    $in_charset = strtolower($in_charset);
247
248 3
    '' === $out_charset && $out_charset = 'iso-8859-1';
249 3
    '' === $in_charset && $in_charset = 'iso-8859-1';
250
251 3
    if ('//translit' === substr($out_charset, -10)) {
252 1
      $TRANSLIT = '//TRANSLIT';
253 1
      $out_charset = substr($out_charset, 0, -10);
254
    }
255
256 3
    if ('//ignore' === substr($out_charset, -8)) {
257 3
      $IGNORE = '//IGNORE';
258 3
      $out_charset = substr($out_charset, 0, -8);
259
    }
260
261 3
    '//translit' === substr($in_charset, -10) && $in_charset = substr($in_charset, 0, -10);
262 3
    '//ignore' === substr($in_charset, -8) && $in_charset = substr($in_charset, 0, -8);
263
264 3
    isset(self::$alias[$in_charset]) && $in_charset = self::$alias[$in_charset];
265 3
    isset(self::$alias[$out_charset]) && $out_charset = self::$alias[$out_charset];
266
267
    // Load charset maps
268
269
    if (
270 3
        ('utf-8' !== $in_charset && !static::loadMap('from.', $in_charset, $in_map))
271
        ||
272 3
        ('utf-8' !== $out_charset && !static::loadMap('to.', $out_charset, $out_map))
273
    ) {
274
      user_error(sprintf(self::ERROR_WRONG_CHARSET, $in_charset, $out_charset));
275
276
      return false;
277
    }
278
279
280 3
    if ('utf-8' !== $in_charset) {
281
      // Convert input to UTF-8
282 2
      $result = '';
283
      /** @noinspection PhpUndefinedVariableInspection */
284 2
      if (self::map_to_utf8($result, $in_map, $str, $IGNORE)) {
0 ignored issues
show
Bug introduced by
The variable $in_map does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
285 2
        $str = $result;
286
      } else {
287
        $str = false;
288
      }
289 2
      self::$is_valid_utf8 = true;
290
    } else {
291 3
      self::$is_valid_utf8 = preg_match('//u', $str);
292
293 3
      if (!self::$is_valid_utf8 && !$IGNORE) {
294 1
        user_error(self::ERROR_ILLEGAL_CHARACTER);
295
296
        return false;
297
      }
298
299 3
      if ('utf-8' === $out_charset) {
300
        // UTF-8 validation
301 3
        $str = self::utf8_to_utf8($str, $IGNORE);
302
      }
303
    }
304
305
    if (
306 3
        'utf-8' !== $out_charset
307
        &&
308 3
        false !== $str
309
    ) {
310
      // Convert output to UTF-8
311 1
      $result = '';
312
      /** @noinspection PhpUndefinedVariableInspection */
313 1
      if (self::map_from_utf8($result, $out_map, $str, $IGNORE, $TRANSLIT)) {
0 ignored issues
show
Bug introduced by
The variable $out_map does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
314 1
        return $result;
315
      } else {
316 1
        return false;
317
      }
318
    } else {
319 3
      return $str;
320
    }
321
  }
322
323
  /**
324
   * @param string $type
325
   * @param string $charset
326
   * @param array  $map
327
   *
328
   * @return bool
329
   */
330 2
  protected static function loadMap($type, $charset, &$map)
331
  {
332 2
    if (!isset(self::$convert_map[$type . $charset])) {
333 2
      if (false === $map = static::getData($type . $charset)) {
334 2
        if ('to.' === $type && static::loadMap('from.', $charset, $map)) {
0 ignored issues
show
Documentation introduced by
$map is of type boolean, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
335 1
          $map = array_flip($map);
336
        } else {
337 1
          return false;
338
        }
339
      }
340
341 2
      self::$convert_map[$type . $charset] = $map;
342
    } else {
343 2
      $map = self::$convert_map[$type . $charset];
344
    }
345
346 2
    return true;
347
  }
348
349
  /**
350
   * get data
351
   *
352
   * @param string $file
353
   *
354
   * @return bool|mixed
355
   */
356 2 View Code Duplication
  protected static function getData($file)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
357
  {
358 2
    $file = __DIR__ . '/charset/' . $file . '.ser';
359 2
    if (file_exists($file)) {
360 2
      return unserialize(file_get_contents($file));
361
    } else {
362 2
      return false;
363
    }
364
  }
365
366
  /**
367
   * map to utf8
368
   *
369
   * @param string $result
370
   * @param array  $map
371
   * @param string $str
372
   * @param string $IGNORE
373
   *
374
   * @return bool
375
   */
376 2
  protected static function map_to_utf8(&$result, $map, $str, $IGNORE)
377
  {
378 2
    $len = strlen($str);
379 2
    for ($i = 0; $i < $len; ++$i) {
380 2
      if (isset($str[$i + 1], $map[$str[$i] . $str[$i + 1]])) {
381 1
        $result .= $map[$str[$i] . $str[++$i]];
382 2
      } elseif (isset($map[$str[$i]])) {
383 2
        $result .= $map[$str[$i]];
384
      } elseif (!$IGNORE) {
385
        user_error(self::ERROR_ILLEGAL_CHARACTER);
386
387
        return false;
388
      }
389
    }
390
391 2
    return true;
392
  }
393
394
  /**
395
   * utf8 to utf8
396
   *
397
   * @param string $str
398
   * @param string $IGNORE
399
   *
400
   * @return false|string
401
   */
402 3
  protected static function utf8_to_utf8($str, $IGNORE)
403
  {
404 3
    $ulen_mask = self::$ulen_mask;
405 3
    $valid = self::$is_valid_utf8;
406
407 3
    $u = $str;
408 3
    $i = $j = 0;
409 3
    $len = strlen($str);
410
411 3
    while ($i < $len) {
412 3
      if ($str[$i] < "\x80") {
413 3
        $u[$j++] = $str[$i++];
414
      } else {
415 2
        $ulen = $str[$i] & "\xF0";
416 2
        $ulen = isset($ulen_mask[$ulen]) ? $ulen_mask[$ulen] : 1;
417 2
        $uchr = substr($str, $i, $ulen);
418
419 2
        if (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr))) {
420 1
          if ($IGNORE) {
421 1
            ++$i;
422 1
            continue;
423
          }
424
425
          user_error(self::ERROR_ILLEGAL_CHARACTER);
426
427
          return false;
428
        } else {
429 1
          $i += $ulen;
430
        }
431
432 1
        $u[$j++] = $uchr[0];
433
434 1
        isset($uchr[1]) && 0 !== ($u[$j++] = $uchr[1])
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of 0 (integer) and $u[$j++] = $uchr[1] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
435 1
        && isset($uchr[2]) && 0 !== ($u[$j++] = $uchr[2])
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of 0 (integer) and $u[$j++] = $uchr[2] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
436 1
        && isset($uchr[3]) && 0 !== ($u[$j++] = $uchr[3]);
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of 0 (integer) and $u[$j++] = $uchr[3] (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
437
      }
438
    }
439
440 3
    return substr($u, 0, $j);
441
  }
442
443
  /**
444
   * map from utf8
445
   *
446
   * @param string $result
447
   * @param array  $map
448
   * @param string $str
449
   * @param string $IGNORE
450
   * @param string $TRANSLIT
451
   *
452
   * @return bool
453
   */
454 1
  protected static function map_from_utf8(&$result, $map, $str, $IGNORE, $TRANSLIT)
455
  {
456 1
    $ulen_mask = self::$ulen_mask;
457 1
    $valid = self::$is_valid_utf8;
458
459 1
    if ($TRANSLIT) {
460 1
      self::$translit_map or self::$translit_map = static::getData('translit');
0 ignored issues
show
Documentation Bug introduced by
It seems like static::getData('translit') of type * is incompatible with the declared type array of property $translit_map.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
461
    }
462
463 1
    $i = 0;
464 1
    $len = strlen($str);
465
466 1
    while ($i < $len) {
467 1
      if ($str[$i] < "\x80") {
468 1
        $uchr = $str[$i++];
469
      } else {
470 1
        $ulen = $str[$i] & "\xF0";
471 1
        $ulen = isset($ulen_mask[$ulen]) ? $ulen_mask[$ulen] : 1;
472 1
        $uchr = substr($str, $i, $ulen);
473
474 1
        if ($IGNORE && (1 === $ulen || !($valid || preg_match('/^.$/us', $uchr)))) {
475
          ++$i;
476
          continue;
477
        } else {
478 1
          $i += $ulen;
479
        }
480
      }
481
482 1
      if (isset($map[$uchr])) {
483 1
        $result .= $map[$uchr];
484 1
      } elseif ($TRANSLIT) {
485 1
        if (isset(self::$translit_map[$uchr])) {
486 1
          $uchr = self::$translit_map[$uchr];
487 1
        } elseif ($uchr >= "\xC3\x80") {
488 1
          $uchr = \Normalizer::normalize($uchr, \Normalizer::NFD);
489
490 1
          if ($uchr[0] < "\x80") {
491 1
            $uchr = $uchr[0];
492
          } elseif ($IGNORE) {
493
            continue;
494
          } else {
495
            return false;
496
          }
497
        }
498
499 1
        $str = $uchr . substr($str, $i);
500 1
        $len = strlen($str);
501 1
        $i = 0;
502 1
      } elseif (!$IGNORE) {
503 1
        return false;
504
      }
505
    }
506
507 1
    return true;
508
  }
509
510
  /**
511
   * iconv: get encoding
512
   *
513
   * @param string $type
514
   *
515
   * @return array|string
516
   */
517 1
  public static function iconv_get_encoding($type = 'all')
518
  {
519
    switch ($type) {
520 1
      case 'input_encoding'   :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
521 1
        return self::$input_encoding;
522
      case 'output_encoding'  :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
523 1
        return self::$output_encoding;
524 1
      case 'internal_encoding':
525 1
        return self::$internal_encoding;
526
    }
527
528
    return array(
529 1
        'input_encoding'    => self::$input_encoding,
530 1
        'output_encoding'   => self::$output_encoding,
531 1
        'internal_encoding' => self::$internal_encoding,
532
    );
533
  }
534
535
  /**
536
   * iconv: set encoding
537
   *
538
   * @param string $type
539
   * @param string $charset
540
   *
541
   * @return bool
542
   */
543 1
  public static function iconv_set_encoding($type, $charset)
544
  {
545
    switch ($type) {
546 1
      case 'input_encoding'   :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
547 1
        self::$input_encoding = $charset;
548 1
        break;
549
      case 'output_encoding'  :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
550 1
        self::$output_encoding = $charset;
551 1
        break;
552 1
      case 'internal_encoding':
553 1
        self::$internal_encoding = $charset;
554 1
        break;
555
556
      default:
557 1
        return false;
558
    }
559
560 1
    return true;
561
  }
562
563
  /**
564
   * iconv: mime encode
565
   *
566
   * @param string $field_name
567
   * @param string $field_value
568
   * @param array  $pref
569
   *
570
   * @return false|string
571
   */
572 1
  public static function iconv_mime_encode($field_name, $field_value, $pref = INF)
573
  {
574 1
    if (is_array($pref) === false) {
575
      $pref = array();
576
    }
577
578 1
    $pref = array_merge(
579
        array(
580 1
            'scheme'           => 'B',
581 1
            'input-charset'    => self::$internal_encoding,
582 1
            'output-charset'   => self::$internal_encoding,
583 1
            'line-length'      => 76,
584 1
            'line-break-chars' => "\r\n",
585
        ),
586
        $pref
587
    );
588
589 1
    preg_match('/[\x80-\xFF]/', $field_name) && $field_name = '';
590
591 1
    $scheme = strtoupper(substr($pref['scheme'], 0, 1));
592 1
    $in = strtolower($pref['input-charset']);
593 1
    $out = strtolower($pref['output-charset']);
594
595 1
    if ('utf-8' !== $in && false === $field_value = self::iconv($in, 'utf-8', $field_value)) {
596
      return false;
597
    }
598
599 1
    preg_match_all('/./us', $field_value, $chars);
600
601 1
    $chars = isset($chars[0]) ? $chars[0] : array();
602
603 1
    $line_break = (int)$pref['line-length'];
604 1
    $line_start = "=?{$pref['output-charset']}?{$scheme}?";
605 1
    $line_length = strlen($field_name) + 2 + strlen($line_start) + 2;
606 1
    $line_offset = strlen($line_start) + 3;
607 1
    $line_data = '';
608
609 1
    $field_value = array();
610
611 1
    $Q = 'Q' === $scheme;
612
613 1
    foreach ($chars as $c) {
614 1
      if ('utf-8' !== $out && false === $c = self::iconv('utf-8', $out, $c)) {
615
        return false;
616
      }
617
618 1
      $o = $Q
619 1
          ? $c = preg_replace_callback(
620 1
              '/[=_\?\x00-\x1F\x80-\xFF]/',
621
              array(
622 1
                  __CLASS__,
623
                  'qp_byte_callback',
624
              ),
625
              $c
626
          )
627 1
          : base64_encode($line_data . $c);
628
629 1
      if (isset($o[$line_break - $line_length])) {
630 1
        $Q || $line_data = base64_encode($line_data);
631 1
        $field_value[] = $line_start . $line_data . '?=';
632 1
        $line_length = $line_offset;
633 1
        $line_data = '';
634
      }
635
636 1
      $line_data .= $c;
637 1
      $Q && $line_length += strlen($c);
638
    }
639
640 1
    if ('' !== $line_data) {
641 1
      $Q || $line_data = base64_encode($line_data);
642 1
      $field_value[] = $line_start . $line_data . '?=';
643
    }
644
645 1
    return $field_name . ': ' . implode($pref['line-break-chars'] . ' ', $field_value);
646
  }
647
648
  /**
649
   * @param string $buffer
650
   * @param mixed  $mode
651
   *
652
   * @return string|false
653
   */
654
  public static function ob_iconv_handler($buffer, /** @noinspection PhpUnusedParameterInspection */ $mode)
0 ignored issues
show
Unused Code introduced by
The parameter $mode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
655
  {
656
    return self::iconv(self::$internal_encoding, self::$output_encoding, $buffer);
657
  }
658
659
  /**
660
   * iconv: strpos
661
   *
662
   * @param string $haystack
663
   * @param string $needle
664
   * @param int    $offset
665
   * @param string $encoding
666
   *
667
   * @return bool|int
668
   */
669 1
  public static function iconv_strpos($haystack, $needle, $offset = 0, $encoding = INF)
670
  {
671 1
    INF === $encoding && $encoding = self::$internal_encoding;
672
673 1 View Code Duplication
    if (0 !== strncasecmp($encoding, 'utf-8', 5)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
674
      if (false === $haystack = self::iconv($encoding, 'utf-8', $haystack)) {
675
        return false;
676
      }
677
678
      if (false === $needle = self::iconv($encoding, 'utf-8', $needle)) {
679
        return false;
680
      }
681
    }
682
683 1
    $offset = (int)$offset;
684
685 1
    if ($offset) {
686
      $haystack = self::iconv_substr($haystack, $offset, 2147483647, 'utf-8');
687
    }
688 1
    $pos = strpos($haystack, $needle);
689
690 1
    return false === $pos ? false : ($offset + ($pos ? self::iconv_strlen(substr($haystack, 0, $pos), 'utf-8') : 0));
691
  }
692
693
  /**
694
   * iconv: substr
695
   *
696
   * @param string $s
697
   * @param int    $start
698
   * @param int    $length
699
   * @param string $encoding
700
   *
701
   * @return false|string
702
   */
703 1
  public static function iconv_substr($s, $start, $length = 2147483647, $encoding = INF)
704
  {
705 1
    INF === $encoding && $encoding = self::$internal_encoding;
706 1 View Code Duplication
    if (0 === strncasecmp($encoding, 'utf-8', 5)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
707 1
      $encoding = INF;
708
    } elseif (false === $s = self::iconv($encoding, 'utf-8', $s)) {
709
      return false;
710
    }
711
712 1
    $s .= '';
713 1
    $slen = self::iconv_strlen($s, 'utf-8');
714 1
    $start = (int)$start;
715
716 1
    if (0 > $start) {
717
      $start += $slen;
718
    }
719
720 1
    if (0 > $start) {
721
      return false;
722
    }
723
724 1
    if ($start >= $slen) {
725
      return false;
726
    }
727
728 1
    $rx = $slen - $start;
729
730 1
    if (0 > $length) {
731
      $length += $rx;
732
    }
733
734 1
    if (0 === $length) {
735
      return '';
736
    }
737
738 1
    if (0 > $length) {
739
      return false;
740
    }
741
742 1
    if ($length > $rx) {
743
      $length = $rx;
744
    }
745
746 1
    $rx = '/^' . ($start ? self::preg_offset($start) : '') . '(' . self::preg_offset($length) . ')/u';
747
748 1
    $s = preg_match($rx, $s, $s) ? $s[1] : '';
749
750 1
    if (INF === $encoding) {
751 1
      return $s;
752
    } else {
753
      return self::iconv('utf-8', $encoding, $s);
754
    }
755
  }
756
757
  /**
758
   * iconv: strlen
759
   *
760
   * @param string $s
761
   * @param string $encoding
762
   *
763
   * @return bool|int
764
   */
765 3
  public static function iconv_strlen($s, $encoding = INF)
766
  {
767 3
    if (extension_loaded('xml')) {
768 3
      return self::strlen1($s, $encoding);
769
    } else {
770
      return self::strlen2($s, $encoding);
771
    }
772
  }
773
774
  /**
775
   * @param string $s
776
   * @param string $encoding
777
   *
778
   * @return bool|int
779
   */
780 3
  public static function strlen1($s, $encoding = INF)
781
  {
782 3
    INF === $encoding && $encoding = self::$internal_encoding;
783
784 3 View Code Duplication
    if (0 !== strncasecmp($encoding, 'utf-8', 5) && false === $s = self::iconv($encoding, 'utf-8', $s)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
785
      return false;
786
    }
787
788 3
    return strlen(utf8_decode($s));
0 ignored issues
show
Security Bug introduced by
It seems like $s defined by self::iconv($encoding, 'utf-8', $s) on line 784 can also be of type false; however, utf8_decode() 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...
789
  }
790
791
  /**
792
   * @param string $s
793
   * @param string $encoding
794
   *
795
   * @return bool|int
796
   */
797 1
  public static function strlen2($s, $encoding = INF)
798
  {
799 1
    INF === $encoding && $encoding = self::$internal_encoding;
800
801 1 View Code Duplication
    if (0 !== strncasecmp($encoding, 'utf-8', 5) && false === $s = self::iconv($encoding, 'utf-8', $s)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
802
      return false;
803
    }
804
805 1
    $ulen_mask = self::$ulen_mask;
806
807 1
    $i = 0;
808 1
    $j = 0;
809 1
    $len = strlen($s);
810
811 1
    while ($i < $len) {
812 1
      $u = $s[$i] & "\xF0";
813 1
      $i += isset($ulen_mask[$u]) ? $ulen_mask[$u] : 1;
814 1
      ++$j;
815
    }
816
817 1
    return $j;
818
  }
819
820
  /**
821
   * @param int $offset
822
   *
823
   * @return string
824
   */
825 1
  protected static function preg_offset($offset)
826
  {
827 1
    $rx = array();
828 1
    $offset = (int)$offset;
829
830 1
    while ($offset > 65535) {
831
      $rx[] = '.{65535}';
832
      $offset -= 65535;
833
    }
834
835 1
    return implode('', $rx) . '.{' . $offset . '}';
836
  }
837
838
  /**
839
   * iconv: strrpos
840
   *
841
   * @param string $haystack
842
   * @param string $needle
843
   * @param string $encoding
844
   *
845
   * @return bool|int
846
   */
847 1
  public static function iconv_strrpos($haystack, $needle, $encoding = INF)
848
  {
849 1
    INF === $encoding && $encoding = self::$internal_encoding;
850
851 1 View Code Duplication
    if (0 !== strncasecmp($encoding, 'utf-8', 5)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
852
      if (false === $haystack = self::iconv($encoding, 'utf-8', $haystack)) {
853
        return false;
854
      }
855
      if (false === $needle = self::iconv($encoding, 'utf-8', $needle)) {
856
        return false;
857
      }
858
    }
859
860 1
    $pos = isset($needle[0]) ? strrpos($haystack, $needle) : false;
861
862 1
    return false === $pos ? false : self::iconv_strlen($pos ? substr($haystack, 0, $pos) : $haystack, 'utf-8');
863
  }
864
865
  /**
866
   * @param array $m
867
   *
868
   * @return string
869
   */
870 1
  protected static function qp_byte_callback($m)
871
  {
872 1
    return '=' . strtoupper(dechex(ord($m[0])));
873
  }
874
}
875