1<?php
2
3interface TStringFunc
4{
5    public function substr($str, $start, $length = null);
6    public function strlen($str);
7}
8
9class TStringFunc_Core
10implements TStringFunc {
11    public function substr($str, $start, $length = null)
12    {
13        // specifying a null $length would return an empty string
14        if ($length === null) {
15            return substr($str, $start);
16        }
17
18        return substr($str, $start, $length);
19    }
20
21    public function strlen($str)
22    {
23        return strlen($str);
24    }
25}
26
27class TStringFunc_Mbstring
28implements TStringFunc {
29    public function substr($str, $start, $length = null)
30    {
31        /**
32         * We need to set the charset parameter, which is the second
33         * optional parameter and the first optional parameter can't
34         * be null or false as a "magic" value because that would
35         * cause an empty string to be returned, so we need to
36         * actually calculate the proper length value.
37         */
38        if ($length === null) {
39            $length = $this->strlen($str) - $start;
40        }
41
42        return mb_substr($str, $start, $length, '8bit');
43    }
44
45    public function strlen($str)
46    {
47        return mb_strlen($str, '8bit');
48    }
49}
50
51class TStringFuncFactory
52{
53    private static $_instance;
54
55    /**
56     * Get the Singleton instance of TStringFunc implementation that is
57     * compatible with the current system's mbstring.func_overload settings.
58     *
59     * @return TStringFunc
60     */
61    public static function create()
62    {
63        if (!self::$_instance) {
64            self::_setInstance();
65        }
66
67        return self::$_instance;
68    }
69
70    private static function _setInstance()
71    {
72        /**
73         * Cannot use str* functions for byte counting because multibyte
74         * characters will be read a single bytes.
75         *
76         * See: http://us.php.net/manual/en/mbstring.overload.php
77         */
78        if (ini_get('mbstring.func_overload') & 2) {
79            self::$_instance = new TStringFunc_Mbstring();
80        }
81        /**
82         * mbstring is not installed or does not have function overloading
83         * of the str* functions enabled so use PHP core str* functions for
84         * byte counting.
85         */
86        else {
87            self::$_instance = new TStringFunc_Core();
88        }
89    }
90}
91