Passed
Push — master ( f81cc4...5281ab )
by stéphane
04:50
created

Regex::isProperlyQuoted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Dallgoot\Yaml;
4
5
/**
6
 * Define Regex patterns as constants
7
 * and some 'testing-type' methods
8
 *
9
 * @author  Stéphane Rebai <[email protected]>
10
 * @license Apache 2.0
11
 * @link    https://github.com/dallgoot/yaml
12
 */
13
class Regex
14
{
15
    const OCTAL_NUM = "/^(0o\d+)$/i";
16
    const HEX_NUM   = "/^(0x[\da-f]+)$/i";
17
18
    const QUOTED = "(?'quot'(?'q'['\"]).*?(?<![\\\\])(?&q))";
19
    const NUM    = "(?'num'[-+]?(?:\\d+\\.?(?:\\d*(e[+-]?\\d+)?)|(\\.(inf|nan)))\z)";
20
    const WORD   = "(?'word'[^,]+)";
21
    const RC     = "(?'rc'\\*\\w+)"; //reference call
22
    const RD     = "(?'rd'&\\w+)"; //reference definition
23
    const TAG    = "(?'tag'!!?[\\w\\/\\-]+!?)";
24
    const ALL    = "(?'all'(?:(?:(?&rd)|(?&tag)) +)?(?:(?&quot)|(?&num)|(?&rc)|(?&word)|(?&map)|(?&seq)))";
25
    const MAP    = "(?'map'\\{ *?(?'pair'((?:(?&quot)|[^:]+) *?: *(?&all)) *,? *)* *?\\})";
26
    const SEQ    = "(?'seq'\\[ *(?:(?'i'(?&all)) *,? *)* *\\])";
27
    const ALLDEF = "(?(DEFINE)".Regex::QUOTED.
28
                                Regex::NUM.
29
                                Regex::RC.
30
                                Regex::WORD.
31
                                Regex::TAG.
32
                                Regex::RD.
33
                                Regex::ALL.
34
                                Regex::MAP.
35
                                Regex::SEQ.")";
36
37
    const MAPPING  = "/".Regex::ALLDEF."^(?&map)$/";
38
    const MAPPING_VALUES = "/".Regex::ALLDEF."(?'k'(?&quot)|[^:]+) *: *(?'v'(?&all)) *,? */i";
39
40
    const SEQUENCE = "/".Regex::ALLDEF."^(?&seq)/";
41
    const SEQUENCE_VALUES = "/".Regex::ALLDEF."(?'item'(?&all)) *,? */i";
42
43
    const KEY  = '/^([\w\'"~!][\w\'" \-.\/~!]*[ \t]*)(?::([ \t]+[^\n]+)|:[ \t]*)$/i';
44
    const ITEM = '/^-([ \t]+(.*))?$/';
45
46
    const NODE_ACTIONS = "/(?(DEFINE)".Regex::RC.Regex::RD.Regex::TAG.")(?'action'(?&rc)|(?&rd)|(?&tag))( +(?'content'.*))?$/";
47
48
    // %TAG ! tag:example.com,2000:app/
49
    // %TAG !! tag:example.com,2000:app/
50
    // %TAG !e! tag:example.com,2000:app/
51
    // %TAG !m! !my-
52
    // !<!bar> baz
53
    // !<tag:clarkevans.com,2002:invoice>
54
    const TAG_URI = "(?'url'tag:\\w+\\.\\w{2,},\\d{4}:\\w+)";
55
    const TAG_PARTS = "/(?'handle'!(?:[\\w\\d\\-_]!|!)*)(?'tagname'(?:<!?)?[\\w\\d\\-:.,_]+>?)?/";
56
    const DIRECTIVE_TAG = "/(?(DEFINE)".Regex::TAG_URI.")%TAG +(?'handle'!|!!|![\\w\\d\-_]+!) +(?'uri'(?&url)|(?'prefix'![\\w\\d\-_]+))/";
57
    const DIRECTIVE_VERSION = "/%YAML +(?'version'1\\.\\d)/";
58
59
60
    /**
61
     * Determines if a valid Date format
62
     * @param string $v a string value
63
     * @return bool
64
     * @throws \Exception if any preg_match has invalid regex
65
     */
66 2
    public static function isDate(string $v):bool
67
    {
68 2
        $d         = "\\d{4}([-\\/])\\d{2}\\1\\d{2}";
69 2
        $h         = "\\d{2}(:)\\d{2}\\2\\d{2}";
70 2
        $date      = "/^$d$/"; // 2002-12-14, 2002/12/14
71 2
        $canonical = "/^$d(?:t| )$h\\.\\dz?$/im"; // 2001-12-15T02:59:43.1Z
72 2
        $spaced    = "/^$d(?:t| )$h\\.\\d{2} [-+]\\d$/im"; // 2001-12-14 21:59:43.10 -5
73 2
        $iso8601   = "/^$d(?:t| )$h\\.\\d{2}[-+]\\d{2}\\2\\d{2}/im"; // 2001-12-14t21:59:43.10-05:00
74 2
        $matchDate      = preg_match($date, $v);
75 2
        $matchCanonical = preg_match($canonical, $v);
76 2
        $matchSpaced    = preg_match($spaced, $v);
77 2
        $matchIso       = preg_match($iso8601, $v);
78 2
        if (empty($v) || is_bool($matchDate) || is_bool($matchCanonical) || is_bool($matchSpaced) || is_bool($matchIso)) {
79 1
            throw new \Exception(__METHOD__." regex ERROR");
80
        }
81 1
        return $matchDate || $matchCanonical || $matchSpaced || $matchIso;
82
    }
83
84
    /**
85
     * Determines if number.
86
     *
87
     * @param string $var A string value
88
     *
89
     * @return boolean  True if number, False otherwise.
90
     * @todo   replace regex expression with class constants, use is_numeric ?
91
     */
92 1
    public static function isNumber(string $var):bool
93
    {
94
        //TODO: https://secure.php.net/manual/en/function.is-numeric.php
95 1
        return (bool) preg_match("/^((0o\d+)|(0x[\da-f]+)|([\d.]+e[-+]\d{1,2})|([-+]?(\d*\.?\d+)))$/i", $var);
96
    }
97
98
    /**
99
     * Determines if properly quoted.
100
     *
101
     * @param string $var The variable
102
     *
103
     * @return boolean True if properly quoted, False otherwise.
104
     */
105 1
    public static function isProperlyQuoted(string $var):bool
106
    {
107 1
        return (bool) preg_match("/^(['\"]).*?(?<![\\\\])\\1$/s", trim($var));
108
    }
109
}
110