Completed
Push — master ( d38a64...a65b20 )
by stéphane
03:22
created

Loader::__construct()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 4
nc 8
nop 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
0 ignored issues
show
Coding Style introduced by
Missing file doc comment
Loading history...
3
4
namespace Dallgoot\Yaml;
5
6
use Dallgoot\Yaml\Yaml as Y;
7
8
/**
9
 * TODO
10
 * @category tag in class comment
0 ignored issues
show
Coding Style introduced by
There must be exactly one blank line before the tags in a doc comment
Loading history...
Coding Style introduced by
Category name "tag in class comment" is not valid; consider "Tag_In_Class_Comment" instead
Loading history...
11
 * @package tag in class comment
1 ignored issue
show
Coding Style introduced by
Package name "tag in class comment" is not valid; consider "Tag_In_Class_Comment" instead
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
12
 * @author tag in class comment
1 ignored issue
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 3 spaces but found 1
Loading history...
13
 * @license tag in class comment
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
14
 */
0 ignored issues
show
Coding Style introduced by
Missing @link tag in class comment
Loading history...
15
final class Loader
16
{
17
    //public
18
19
    /* @var null|string */
20
    public $error;
21
22
    public const EXCLUDE_DIRECTIVES = 1;//DONT include_directive
23
    public const IGNORE_COMMENTS    = 2;//DONT include_comments
24
    public const NO_PARSING_EXCEPTIONS = 4;//THROW Exception on parsing Errors
25
    public const NO_OBJECT_FOR_DATE = 8;//DONT import date strings as dateTime Object
26
    //privates
27
    /* @var null|string */
28
    private $content;
0 ignored issues
show
Coding Style introduced by
Private member variable "content" must be prefixed with an underscore
Loading history...
29
    /* @var null|string */
30
    private $filePath;
0 ignored issues
show
Coding Style introduced by
Private member variable "filePath" must be prefixed with an underscore
Loading history...
31
    /* @var integer */
32
    private $debug = 0;///TODO: determine levels
0 ignored issues
show
Coding Style introduced by
Private member variable "debug" must be prefixed with an underscore
Loading history...
33
    /* @var integer */
34
    private $options = 0;
0 ignored issues
show
Coding Style introduced by
Private member variable "options" must be prefixed with an underscore
Loading history...
35
    //Exceptions messages
36
    private const INVALID_VALUE        = self::class.": at line %d";
37
    private const EXCEPTION_NO_FILE    = self::class.": file '%s' does not exists (or path is incorrect?)";
38
    private const EXCEPTION_READ_ERROR = self::class.": file '%s' failed to be loaded (permission denied ?)";
39
    private const EXCEPTION_LINE_SPLIT = self::class.": content is not a string(maybe a file error?)";
40
41
    public function __construct($absolutePath = null, $options = null, $debug = 0)
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
42
    {
43
        $this->debug   = is_int($debug) ? min($debug, 3) : 1;
44
        $this->options = is_int($options) ? $options : $this->options;
45
        if (is_string($absolutePath)) {
46
            $this->load($absolutePath);
47
        }
48
    }
49
50
    /**
51
     * load a file and save its content as $content
0 ignored issues
show
Coding Style introduced by
Doc comment short description must start with a capital letter
Loading history...
52
     *
53
     * @param      string       $absolutePath  The absolute path of a file
3 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter type; 7 found
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 6
Loading history...
54
     *
55
     * @throws     \Exception   if file don't exist OR reading failed
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 5
Loading history...
56
     *
57
     * @return     self  ( returns the same Loader  )
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 5
Loading history...
58
     */
59
    public function load(string $absolutePath):Loader
60
    {
61
        $this->filePath = $absolutePath;
62
        if (!file_exists($absolutePath)) {
63
            throw new \Exception(sprintf(self::EXCEPTION_NO_FILE, $absolutePath));
64
        }
65
        $adle = "auto_detect_line_endings";
66
        $prevADLE = ini_get($adle);
67
        !$prevADLE && ini_set($adle, "true");
68
        $content = file($absolutePath, FILE_IGNORE_NEW_LINES);
69
        !$prevADLE && ini_set($adle, "false");
70
        if (is_bool($content)) {
71
            throw new \Exception(sprintf(self::EXCEPTION_READ_ERROR, $absolutePath));
72
        }
73
        $this->content = $content;
0 ignored issues
show
Documentation Bug introduced by
It seems like $content of type array is incompatible with the declared type null|string of property $content.

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...
74
        return $this;
75
    }
76
77
    /**
78
     * Parse Yaml lines into a hierarchy of Node
79
     *
80
     * @param      string       $strContent  The Yaml string or null to parse loaded content
3 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter type; 7 found
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter name; 2 found
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 6
Loading history...
81
     * @throws     \Exception    if content is not available as $strContent or as $this->content (from file)
1 ignored issue
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 5
Loading history...
82
     * @throws     \ParseError  if any error during parsing or building
1 ignored issue
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 5
Loading history...
83
     *
84
     * @return     array|YamlObject|null      null on errors if NO_PARSING_EXCEPTIONS is set, otherwise an array of YamlObject or just YamlObject
1 ignored issue
show
Coding Style introduced by
Tag value indented incorrectly; expected 1 spaces but found 5
Loading history...
85
     */
86
    public function parse($strContent = null)
87
    {
88
        $source = $this->content ?? preg_split("/([^\n\r]+)/um", $strContent, 0, PREG_SPLIT_DELIM_CAPTURE);
89
        //TODO : be more permissive on $strContent values
90
        if (!is_array($source) || !count($source)) throw new \Exception(self::EXCEPTION_LINE_SPLIT);
91
        $previous = $root = new Node();
92
        $emptyLines = [];
93
        try {
94
            $gen = function() use($source) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after USE keyword; found 0
Loading history...
95
                foreach ($source as $key => $value) {
96
                    yield ++$key => $value;
97
                }
98
            };
99
            foreach ($gen() as $lineNb => $lineString) {
100
                $n = new Node($lineString, $lineNb);
101
                if ($n->type & (Y::LITTERALS|Y::BLANK)) {
102
                    if ($this->onSpecialType($n, $previous, $emptyLines)) continue;
103
                } else {
104
                    foreach ($emptyLines as $blankNode) {
105
                        $blankNode->getParent()->add($blankNode);
106
                    }
107
                }
108
                $emptyLines = [];
109
                switch ($n->indent <=> $previous->indent) {
110
                    case -1: $target = $previous->getParent($n->indent);
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
111
                        break;
112
                    case 0:  $target = $previous->getParent();
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
113
                        break;
114
                    default: $target = $previous;
1 ignored issue
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
115
                }
116
                if ($this->onContextType($n, $target, $lineString)) continue;
117
                $target->add($n);
118
                $previous = $n;
119
            }
120
            if ($this->debug === 2) echo "\033[33mParsed Structure\033[0m\n",var_export($root, true);
121
            $out = Builder::buildContent($root, $this->debug);
122
            return $out;
123
        } catch (\Error|\Exception|\ParseError $e) {
124
            $file = basename($this->filePath);
125
            $message = basename($e->getFile())."@".$e->getLine().":".$e->getMessage()." in '$file' @".($lineNb)."\n";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $lineNb seems to be defined by a foreach iteration on line 99. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
126
            if ($e instanceof \ParseError && ($this->options & self::NO_PARSING_EXCEPTIONS)) {
127
                trigger_error($message, E_USER_WARNING);
128
                $this->error = $message;
129
                return null;
130
            }
131
            var_dump($root);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($root) looks like debug code. Are you sure you do not want to remove it?
Loading history...
132
            throw new \Exception($message, 3);
133
        }
134
    }
135
136
    private function onSpecialType(&$n, &$previous, &$emptyLines):bool
0 ignored issues
show
Coding Style introduced by
Private method name "Loader::onSpecialType" must be prefixed with an underscore
Loading history...
Coding Style introduced by
Missing function doc comment
Loading history...
137
    {
138
        $deepest = $previous->getDeepestNode();
139
        if ($n->type & Y::BLANK) {
140
            if ($previous->type & Y::SCALAR) $emptyLines[] = $n->setParent($previous->getParent());
141
            if ($deepest->type & Y::LITTERALS) $emptyLines[] = $n->setParent($deepest);
142
            return true;
143
        }
144
        return false;
145
    }
146
147
    private function onContextType(&$n, &$previous, $lineString):bool
0 ignored issues
show
Coding Style introduced by
Missing function doc comment
Loading history...
Coding Style introduced by
Private method name "Loader::onContextType" must be prefixed with an underscore
Loading history...
148
    {
149
        $deepest = $previous->getDeepestNode();
150
        if ($deepest->type & Y::PARTIAL) {
151
            //TODO:verify this edge case
152
            // if ($n->type === Y::KEY && $n->indent === $previous->indent) {
153
            //     throw new \ParseError(sprintf(self::INVALID_VALUE, $lineNb), 1);
154
            // }
155
            $deepest->parse($deepest->value.' '.ltrim($lineString));
156
            return true;
157
        }
158
        if(($previous->type & Y::LITTERALS) || (($deepest->type & Y::LITTERALS) && is_null($deepest->value))) {
0 ignored issues
show
Coding Style introduced by
Expected "if (...) {\n"; found "if(...) {\n"
Loading history...
159
            $n->type = Y::SCALAR;
160
            $n->identifier = null;
161
            $n->value = trim($lineString);
162
        }
163
        if (($deepest->type & (Y::LITTERALS|Y::REF_DEF|Y::SET_VALUE|Y::TAG)) && is_null($deepest->value)) {
164
            $previous = $deepest;
165
        }
166
        return false;
167
    }
168
}
169