Passed
Push — master ( 2b5c6e...94acd2 )
by Gilles
05:27 queued 10s
created

IntToCombinedStringsListType::escape()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
/*************************************************************************************/
3
/*      This file is part of the Thelia package.                                     */
4
/*                                                                                   */
5
/*      Copyright (c) OpenStudio                                                     */
6
/*      email : [email protected]                                                       */
7
/*      web : http://www.thelia.net                                                  */
8
/*                                                                                   */
9
/*      For the full copyright and license information, please view the LICENSE.txt  */
10
/*      file that was distributed with this source code.                             */
11
/*************************************************************************************/
12
13
namespace Thelia\Type;
14
15
/**
16
 * This class manages boolea expression, in the form:
17
 * number:expr, number:expr, ...
18
 * expr: string|(expr oper expr)
19
 * oper: | or &
20
 * Special characters , : ( ) & | should be escaped in strings, for example a\(b\).
21
 *
22
 * Sample expression : 1: foo & bar | (fooo &baar), 4: *, 67: (foooo & baaar), 11:(abc & def\&ghi\|ttt)
23
 *
24
 * @author Etienne Roudeix <[email protected]>, Franck Allimant <[email protected]>
25
 */
26
class IntToCombinedStringsListType extends BaseType
27
{
28
    public function getType()
29
    {
30
        return 'Int to combined strings list type';
31
    }
32
33
    public function isValid($values)
34
    {
35
        // Explode expession parts, ignoring escaped characters, (\, and \:)
36
        foreach (preg_split('#(?<!\\\),#', $values) as $intToCombinedStrings) {
37
            $parts = preg_split('#(?<!\\\):#', $intToCombinedStrings);
38
39
            if (count($parts) != 2) {
0 ignored issues
show
Bug introduced by
It seems like $parts can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

39
            if (count(/** @scrutinizer ignore-type */ $parts) != 2) {
Loading history...
40
                return false;
41
            }
42
            if (filter_var($parts[0], FILTER_VALIDATE_INT) === false) {
43
                return false;
44
            }
45
46
            if (false === $this->checkLogicalFormat($parts[1])) {
47
                return false;
48
            }
49
        }
50
51
        return true;
52
    }
53
54
    public function getFormattedValue($values)
55
    {
56
        if ($this->isValid($values)) {
57
            $return = [];
58
59
            foreach (preg_split('/(?<!\\\),/', $values) as $intToCombinedStrings) {
60
                $parts = preg_split('/(?<!\\\):/', $intToCombinedStrings);
61
62
                $return[trim($parts[0])] = array(
63
                    "values" =>  array_map(
64
                        function ($item) {
65
                            return trim(self::unescape($item));
66
                        },
67
                        preg_split(
0 ignored issues
show
Bug introduced by
It seems like preg_split('#(?<!\\)[&|]...(\)]#', '', $parts[1])) can also be of type false; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

67
                        /** @scrutinizer ignore-type */ preg_split(
Loading history...
68
                            '#(?<!\\\)[&|]#',
69
                            preg_replace(
70
                                '#(?<!\\\)[\(\)]#',
71
                                '',
72
                                $parts[1]
73
                            )
74
                        )
75
                    ),
76
                    "expression" =>  trim(self::unescape($parts[1])),
77
                );
78
            }
79
80
            return $return;
81
        } else {
82
            return null;
83
        }
84
    }
85
86
87
    /**
88
     * Escape a string to use it safely in an expression. abc:def => abc\:def.
89
     * Escapes characters are , : ( ) | &
90
     *
91
     * @param $string
92
     * @return string
93
     */
94
    public static function escape($string)
95
    {
96
        return preg_replace('/([,\:\(\)\|\&])/', '\\\$1', $string);
97
    }
98
99
    /**
100
     * Unescape a string and remove avai escape symbols. abc\:def => abc:def.
101
     *
102
     * @param $string
103
     * @return string
104
     */
105
    public static function unescape($string)
106
    {
107
        return preg_replace('/\\\([,\:\(\)\|\&])/', '\1', $string);
108
    }
109
110
    protected function checkLogicalFormat($string)
111
    {
112
        // Delete escaped characters
113
        $string = preg_replace('/\\\[,\:\(\)\|\&]/', '', $string);
114
115
        /* delete  all spaces and parentheses */
116
        $noSpaceString = preg_replace('#[\s]#', '', $string);
117
        $noParentheseString = preg_replace('#[\(\)]#', '', $noSpaceString);
118
119
        if (!preg_match('#^([a-zA-Z0-9_\-]+([\&\|][a-zA-Z0-9_\-]+)*|\*)$#', $noParentheseString)) {
120
            return false;
121
        }
122
123
        /* check parenteses use */
124
        $openingParenthesesCount = 0;
125
        $closingParenthesesCount = 0;
126
127
        $length = strlen($noSpaceString);
128
        for ($i=0; $i< $length; $i++) {
129
            $char = $noSpaceString[$i];
130
            if ($char == '(') {
131
                /* must be :
132
                 * - after a &| or () or at the begining of expression
133
                 * - before a number or ()
134
                 * must not be :
135
                 * - at the end of expression
136
                 */
137
                if (($i!=0 && !preg_match('#[\(\)\&\|]#', $noSpaceString[$i-1])) || !isset($noSpaceString[$i+1]) || !preg_match('#[\(\)a-zA-Z0-9_\-]#', $noSpaceString[$i+1])) {
138
                    return false;
139
                }
140
                $openingParenthesesCount++;
141
            } elseif ($char == ')') {
142
                /* must be :
143
                 * - after a number or ()
144
                 * - before a &| or () or at the end of expression
145
                 * must not be :
146
                 * - at the begining of expression
147
                 * - if no ( remain unclose
148
                 */
149
                if ($i == 0 || !preg_match('#[\(\)a-zA-Z0-9_\-]#', $noSpaceString[$i-1]) || (isset($noSpaceString[$i+1]) && !preg_match('#[\(\)\&\|]#', $noSpaceString[$i+1])) || $openingParenthesesCount-$closingParenthesesCount==0) {
150
                    return false;
151
                }
152
                $closingParenthesesCount++;
153
            }
154
        }
155
156
        if ($openingParenthesesCount != $closingParenthesesCount) {
157
            return false;
158
        }
159
160
        return true;
161
    }
162
163
    public function getFormType()
164
    {
165
        return 'text';
166
    }
167
168
    public function getFormOptions()
169
    {
170
        return array();
171
    }
172
}
173