Completed
Push — master ( 3021e9...64aadc )
by Fenz
02:37
created

Document::getLineType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 28 and the first side effect is on line 1025.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
namespace Htsl\Parser;
4
5
use Htsl\Htsl;
6
use Htsl\ReadingBuffer\Contracts\ABuffer as Buffer;
7
use Htsl\ReadingBuffer\Line;
8
use Htsl\Helper\TGetter;
9
use Htsl\Helper\IConfigProvider;
10
use Htsl\Parser\Node\TagNode;
11
use Htsl\Parser\Node\StringNode;
12
use Htsl\Parser\Node\CommentNode;
13
use Htsl\Parser\Node\ControlNode;
14
use Htsl\Parser\Node\SectionNode;
15
use Htsl\Parser\Node\NamelessSectionNode;
16
use Htsl\Parser\Node\Contracts\ANode as Node;
17
18
////////////////////////////////////////////////////////////////
19
20
/**
21
 * @property-read string                                 $content     Result of document executing.
22
 * @property-read string                                 $doctype     Type of this document.
23
 * @property-read string|bool                            $indentation Indentation of document.
24
 * @property-read \Htsl\Parser\Node\Contracts\ANode|bool $scope       Current scope on top of stack.
25
 * @property-read \Htsl\Htsl                             $htsl        Htsl main object of document.
26
 * @property-read int                                    $indentLevel Current indent level.
27
 */
28
class Document implements IConfigProvider
29
{
30
	use TGetter;
31
32
	/**
33
	 * Htsl main object owns this document.
34
	 *
35
	 * @access private
36
	 *
37
	 * @var \Htsl\Htsl
38
	 */
39
	private $htsl;
40
41
	/**
42
	 * Parent document.
43
	 *
44
	 * @access private
45
	 *
46
	 * @var \Htsl\Parser\Document | null
47
	 */
48
	private $parent;
49
50
	/**
51
	 * Reading buffer of this document.
52
	 *
53
	 * @access private
54
	 *
55
	 * @var \Htsl\ReadingBuffer\Contracts\ABuffer
56
	 */
57
	private $buffer;
58
59
	/**
60
	 * The indentation and whether output with linefeed and indent.
61
	 *
62
	 * @access private
63
	 *
64
	 * @var string | bool
65
	 */
66
	private $indentation;
67
68
	/**
69
	 * Type of this document.
70
	 *
71
	 * @access private
72
	 *
73
	 * @var string
74
	 */
75
	private $docType;
76
77
	/**
78
	 * Current embead script.
79
	 *
80
	 * @access private
81
	 *
82
	 * @var \Htsl\Embedment\Contract\Embedment
83
	 */
84
	private $embedment;
85
86
	/**
87
	 * Current indent level.
88
	 *
89
	 * @access private
90
	 *
91
	 * @var int
92
	 */
93
	private $level= 0;
94
95
	/**
96
	 * Section indent level.
97
	 *
98
	 * @access private
99
	 *
100
	 * @var int
101
	 */
102
	private $sectionLevel= 0;
103
104
	/**
105
	 * Opened nodes.
106
	 *
107
	 * @access private
108
	 *
109
	 * @var [ Htsl\Parser\Node\Contracts\ANode, ]
110
	 */
111
	private $openedNodes= [];
112
113
	/**
114
	 * Current scopes.
115
	 *
116
	 * @access private
117
	 *
118
	 * @var [ Htsl\Parser\Node\Contracts\ANode, ]
119
	 */
120
	private $scopes= [];
121
122
	/**
123
	 * Current line number.
124
	 *
125
	 * @access private
126
	 *
127
	 * @var int
128
	 */
129
	private $lineNumber= 0;
130
131
	/**
132
	 * Current line.
133
	 *
134
	 * @access private
135
	 *
136
	 * @var \Htsl\ReadingBuffer\Line
137
	 */
138
	private $currentLine;
139
140
	/**
141
	 * Sections that can be show.
142
	 *
143
	 * @access private
144
	 *
145
	 * @var [ Htsl\Parser\Section, ]
146
	 */
147
	private $sections=[];
148
149
	/**
150
	 * Whether the document is executed.
151
	 *
152
	 * @access private
153
	 *
154
	 * @var bool
155
	 */
156
	private $isExecuted;
157
158
	/**
159
	 * Current Section.
160
	 *
161
	 * @access private
162
	 *
163
	 * @var \Htsl\Parser\Section
164
	 */
165
	private $currentSection;
166
167
	/**
168
	 * The content of this document.
169
	 *
170
	 * @access private
171
	 *
172
	 * @var string
173
	 */
174
	private $content;
175
176
	/**
177
	 * Constructor of the Document.
178
	 *
179
	 * @access public
180
	 *
181
	 * @param \Htsl\Htsl                            $htsl
182
	 * @param \Htsl\ReadingBuffer\Contracts\ABuffer $buffer
183
	 * @param \Htsl\Parser\Document | null          $parent
184
	 */
185
	public function __construct( Htsl$htsl, Buffer$buffer, self$parent=null )
186
	{
187
		$this->htsl= $htsl;
188
		$this->buffer= $buffer;
189
190
		if( $parent ){
191
			$this->parent= $parent;
192
			$this->docType= $parent->docType;
193
			$this->indentation= $parent->indentation;
194
		}else{
195
			$this->parseFirstLine();
196
		}
197
	}
198
199
	/**
200
	 * Executing the document.
201
	 *
202
	 * @access public
203
	 *
204
	 * @return \Htsl\Parser\Document
205
	 */
206
	public function execute():self
207
	{
208
		if( $this->isExecuted ){
209
			return $this;
210
		}
211
		return $this->lineByLine()
212
		            ->bubbleSections()
213
		;
214
	}
215
216
	/**
217
	 * Alias of getContent.
218
	 *
219
	 * @access public
220
	 *
221
	 * @return string
222
	 */
223
	public function __toString():string
224
	{
225
		return $this->getContent();
226
	}
227
228
	/**
229
	 * Getting the result of document executing.
230
	 *
231
	 * @access protected
232
	 *
233
	 * @return string
234
	 */
235
	protected function getContent():string
236
	{
237
		if( $this->parent ){
238
			return $this->execute()->parent->getContent();
239
		}else{
240
			return $this->execute()->content;
241
		}
242
	}
243
244
	/**
245
	 * Getting the next line.
246
	 *
247
	 * @access protected
248
	 *
249
	 * @return \Htsl\ReadingBuffer\Line
250
	 */
251
	protected function getLine():Line
252
	{
253
		do{
254
			$line= $this->buffer->getLine();
255
		}while( $line->isEmpty() && $line->hasMore() );
256
257
		return $line;
258
	}
259
260
	/**
261
	 * Getting the config of type of this document.
262
	 *
263
	 * @access public
264
	 *
265
	 * @param  [ string, ] ...$keys
266
	 *
267
	 * @return mixed
268
	 */
269
	public function getConfig( string...$keys )
270
	{
271
		return $this->htsl->getConfig(array_shift($keys),$this->docType,...$keys);
272
	}
273
274
	/**
275
	 * Getting the type of this document.
276
	 *
277
	 * @access public
278
	 *
279
	 * @return string
280
	 */
281
	public function getDoctype():string
282
	{
283
		return $this->docType;
284
	}
285
286
	/**
287
	 * Getting the indentation.
288
	 *
289
	 * @access public
290
	 *
291
	 * @return string | bool
292
	 */
293
	public function getIndentation()
294
	{
295
		return $this->indentation;
296
	}
297
298
	/**
299
	 * Getting the indent level.
300
	 *
301
	 * @access public
302
	 *
303
	 * @return int
304
	 */
305
	public function getIndentLevel():int
306
	{
307
		return $this->level;
308
	}
309
310
	/**
311
	 * Parsing the first line.
312
	 *
313
	 * @access protected
314
	 *
315
	 * @return \Htsl\Parser\Document
316
	 */
317
	protected function parseFirstLine():self
318
	{
319
		$line= $this->getLine();
320
321
		if( '@'===$line->getChar(0) ){
322
			return $this->setExtending($line);
323
		}
324
325
		$this->docType= $line->content;
326
		$docTypeContent= $this->getConfig('doc_types') or $this->throw("DocType $this->docType is not supported");
327
328
		$this->indentation= $this->htsl->getConfig('indentation',$this->docType) ?? ( function( $scalarOrFalse ){ return is_scalar($scalarOrFalse)?$scalarOrFalse:false; } )($this->htsl->getConfig('indentation'));
329
330
		$this->appendLine($docTypeContent);
331
332
		return $this;
333
	}
334
335
	/**
336
	 * Setting that this document extends another document.
337
	 *
338
	 * @access protected
339
	 *
340
	 * @param \Htsl\ReadingBuffer\Line $firstLine
341
	 *
342
	 * @return \Htsl\Parser\Document
343
	 */
344
	protected function setExtending( Line$firstLine ):self
345
	{
346
		switch( $name= $firstLine->pregGet('/(?<=^@)[\w-:]+/') ){
347
			default:{
348
				$this->throw("The @$name is not supported.");
349
			}break;
350
			case 'extend':{
351
				$this->extend($firstLine->pregGet('/(?<=\( ).*(?= \))/'));
352
			}break;
353
			case 'show':
354
			case 'include':
355
			case 'section':{
356
				$this->throw("The @$name can not be used on first line.");
357
			}break;
358
		}
359
360
		return $this;
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $this.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
361
	}
362
363
	/**
364
	 * Parsing this document line by line.
365
	 *
366
	 * @access protected
367
	 *
368
	 * @return \Htsl\Parser\Document
369
	 */
370
	protected function lineByLine():self
371
	{
372
		while( ($line= $this->getLine())->hasMore() ){
373
			$this->lineNumber+= 1;
374
375
			if( $this->embedment ){
376
				$this->embeddingParse($line);
377
			}else{
378
				$this->parseLine($line);
379
			}
380
		}
381
382
		$this->embedment and $this->breakEmbedding();
383
384
		$this->closeNodes($this->level);
385
386
		$this->isExecuted= true;
387
388
		return $this;
389
	}
390
391
	/**
392
	 * Parsing embedded line.
393
	 *
394
	 * @access protected
395
	 *
396
	 * @param  \Htsl\ReadingBuffer\Line   $line
397
	 *
398
	 * @return \Htsl\Parser\Document
399
	 */
400
	protected function embeddingParse( Line$line ):self
401
	{
402
		if( $line->content==='<}' ){
403
			$this->breakEmbedding();
404
		}else{
405
			$this->embedment->parseLine($line->getSubIndentLine());
406
		}
407
		return $this;
408
	}
409
410
	/**
411
	 * Starting the embedding.
412
	 *
413
	 * @access protected
414
	 *
415
	 * @param  string $embedType
416
	 *
417
	 * @return \Htsl\Parser\Document
418
	 */
419
	protected function startEmbedding( string$embedType ):self
420
	{
421
		$embedmentClass= '\\Htsl\\Embedment\\'.ucfirst($embedType).'Embedment';
422
		class_exists($embedmentClass) or $this->throw("Embed type $embedType not exists.");
423
424
		$this->embedment= new $embedmentClass($this);
425
426
		return $this;
427
	}
428
429
	/**
430
	 * Ending the embedding.
431
	 *
432
	 * @access public
433
	 *
434
	 * @return \Htsl\Parser\Document
435
	 */
436
	public function breakEmbedding():self
437
	{
438
		$this->append($this->embedment->getContent());
439
		$this->embedment= null;
440
441
		return $this;
442
	}
443
444
	/**
445
	 * Parsing line.
446
	 *
447
	 * @access protected
448
	 *
449
	 * @param  \Htsl\ReadingBuffer\Line   $line
450
	 *
451
	 * @return \Htsl\Parser\Document
452
	 */
453
	protected function parseLine( Line$line ):self
454
	{
455
		$this->currentLine= $line;
456
		$this->setLevel($line->getIndentLevel());
457
458
		$this->{'parse'.ucfirst($this->getLineType($line))}($line);
459
460
		return $this;
461
	}
462
463
	/**
464
	 * Getting line type by analyzing first or first two characters of line.
465
	 *
466
	 * @access protected
467
	 *
468
	 * @param  \Htsl\ReadingBuffer\Line   $line
469
	 *
470
	 * @return string
471
	 */
472
	protected function getLineType( Line$line ):string
473
	{
474
		$possibleType= self::POSSIBLE_LINE_TYPES;
475
476
		for( $i=0; is_array($possibleType); ++$i ){
477
			$possibleType= $possibleType[$line->getChar($i)]??$possibleType[' '];
478
		}
479
480
		return $possibleType;
481
	}
482
483
	/**
484
	 * Possible line types.
485
	 *
486
	 * @access public
487
	 *
488
	 * @return array
489
	 *
490
	 * @todo Make this const private when php 7.1
491
	 */
492
	const POSSIBLE_LINE_TYPES= [
493
		'`'=> [
494
			'='=> 'expressionHtmlLine',
495
			' '=> 'htmlLine',
496
		],
497
		'='=> 'expressionLine',
498
		'!'=> 'commentLine',
499
		'-'=> 'tagLine',
500
		'~'=> 'controlLine',
501
		'@'=> 'docControlLine',
502
		' '=> 'stringLine',
503
	];
504
505
	/**
506
	 * Parsing line as HTML content.
507
	 *
508
	 * @access protected
509
	 *
510
	 * @param  \Htsl\ReadingBuffer\Line   $line
511
	 *
512
	 * @return \Htsl\Parser\Document
513
	 */
514 View Code Duplication
	protected function parseHtmlLine( Line$line ):self
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...
515
	{
516
		$node= new StringNode($this,$line);
517
518
		$this->openNode($node);
519
520
		$this->appendLine($line->slice(1));
521
522
		return $this;
523
	}
524
525
	/**
526
	 * Parsing line as string content.
527
	 *
528
	 * @access protected
529
	 *
530
	 * @param  \Htsl\ReadingBuffer\Line   $line
531
	 *
532
	 * @return \Htsl\Parser\Document
533
	 */
534
	protected function parseStringLine( Line$line ):self
535
	{
536
		$node= new StringNode($this,$line);
537
538
		$this->openNode($node);
539
540
		$this->appendLine($this->htmlEntities(trim($line->getContent())));
541
542
		return $this;
543
	}
544
545
	/**
546
	 * Parsing line as PHP expression.
547
	 *
548
	 * @access protected
549
	 *
550
	 * @param  \Htsl\ReadingBuffer\Line   $line
551
	 *
552
	 * @return \Htsl\Parser\Document
553
	 */
554
	protected function parseExpressionLine( Line$line ):self
555
	{
556
		$node= new StringNode($this,$line);
557
558
		$this->openNode($node);
559
560
		$content=  $line->slice(1);
561
		$ent_flag= var_export($this->htsl->getConfig('ENT_flags',$this->docType),true);
562
		$charset=   var_export($this->htsl->getConfig('charset'),true);
563
564
		$this->appendLine("<?=htmlentities($content,$ent_flag,$charset,false)?>");
565
566
		return $this;
567
	}
568
569
	/**
570
	 * Parsing line as PHP expression with HTML result.
571
	 *
572
	 * @access protected
573
	 *
574
	 * @param  \Htsl\ReadingBuffer\Line   $line
575
	 *
576
	 * @return \Htsl\Parser\Document
577
	 */
578 View Code Duplication
	protected function parseExpressionHtmlLine( Line$line ):self
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...
579
	{
580
		$node= new StringNode($this,$line);
581
582
		$this->openNode($node);
583
584
		$content=  $line->slice(1);
585
586
		$this->appendLine("<?$content?>");
587
588
		return $this;
589
	}
590
591
	/**
592
	 * Parsing line as comment.
593
	 *
594
	 * @access protected
595
	 *
596
	 * @param  \Htsl\ReadingBuffer\Line   $line
597
	 *
598
	 * @return \Htsl\Parser\Document
599
	 */
600
	protected function parseCommentLine( Line$line ):self
601
	{
602
		$node= new CommentNode($this,$line);
603
604
		$this->openNode($node);
605
606
		$this->appendLine($node->open());
607
608
		return $this;
609
	}
610
611
	/**
612
	 * Parsing line as HTSL tag.
613
	 *
614
	 * @access protected
615
	 *
616
	 * @param  \Htsl\ReadingBuffer\Line   $line
617
	 *
618
	 * @return \Htsl\Parser\Document
619
	 */
620
	protected function parseTagLine( Line$line ):self
621
	{
622
		$tag= new TagNode($this,$line);
623
624
		$this->appendLine($tag->open());
625
626
		$tag->embed and $this->startEmbedding($tag->embed);
627
628
		$this->openNode($tag);
629
630
		return $this;
631
	}
632
633
	/**
634
	 * Parsing line as control node of Htsl.php.
635
	 *
636
	 * @access protected
637
	 *
638
	 * @param  \Htsl\ReadingBuffer\Line   $line
639
	 *
640
	 * @return \Htsl\Parser\Document
641
	 */
642
	protected function parseControlLine( Line$line ):self
643
	{
644
		$controlStructure= new ControlNode($this,$line);
645
646
		$this->appendLine($controlStructure->open());
647
648
		$this->openNode($controlStructure);
649
650
		return $this;
651
	}
652
653
	/**
654
	 * Parsing line as document control node of Htsl.php.
655
	 *
656
	 * @access protected
657
	 *
658
	 * @param  \Htsl\ReadingBuffer\Line   $line
659
	 *
660
	 * @return \Htsl\Parser\Document
661
	 */
662
	protected function parseDocControlLine( Line$line ):self
663
	{
664
		switch( $name= $line->pregGet('/(?<=^@)[\w-:]+/') ){
665
			default:{
666
				$this->throw("The @$name is not supported.");
667
			}break;
668
			case 'extend':{
669
				$this->throw('The @extend can only be used on first line.');
670
			}break;
671
			case 'include':{
672
				$this->include($line);
673
			}break;
674
			case 'section':{
675
				$this->defineSection($line);
676
			}break;
677
			case 'show':{
678
				$this->showSection($line);
679
			}break;
680
		}
681
682
		return $this;
683
	}
684
685
	/**
686
	 * Parsing extending defination.
687
	 *
688
	 * @access protected
689
	 *
690
	 * @param  string $fileName
691
	 *
692
	 * @return \Htsl\Parser\Document
693
	 */
694
	protected function extend( string$fileName ):self
695
	{
696
		$this->parent= new static($this->htsl,$this->buffer->goSide($fileName));
697
698
		$this->docType= $this->parent->docType;
699
		$this->indentation= $this->parent->indentation;
700
701
		return $this;
702
	}
703
704
	/**
705
	 * Include another document.
706
	 *
707
	 * @access protected
708
	 *
709
	 * @param  \Htsl\ReadingBuffer\Line   $line
710
	 *
711
	 * @return \Htsl\Parser\Document
712
	 */
713
	protected function include( Line$line ):self
714
	{
715
		$inclued= (new static($this->htsl,$this->buffer->goSide($line->pregGet('/(?<=\( ).*(?= \))/')),$this))->execute()->content;
1 ignored issue
show
Documentation introduced by
$this is of type this<Htsl\Parser\Document>, but the function expects a null|object<self>.

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...
716
717 View Code Duplication
		if( false!==$this->indentation ){
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...
718
			$inclued= preg_replace('/(?<=^|\\n)(?!$)/',str_repeat($this->indentation,$this->level-$this->sectionLevel),$inclued);
719
		}
720
721
		$node= new StringNode($this,$line);
722
723
		$this->openNode($node);
724
725
		$this->append($inclued);
726
727
		return $this;
728
	}
729
730
	/**
731
	 * Starting to define a section.
732
	 *
733
	 * @access protected
734
	 *
735
	 * @param  \Htsl\ReadingBuffer\Line   $line
736
	 *
737
	 * @return \Htsl\Parser\Document
738
	 */
739
	protected function defineSection( Line$line ):self
740
	{
741
		$node= new SectionNode($this,$line);
742
743
		$node->open();
744
745
		$this->openNode($node);
746
747
		return $this;
748
	}
749
750
	/**
751
	 * Showing a section.
752
	 *
753
	 * @access protected
754
	 *
755
	 * @param  \Htsl\ReadingBuffer\Line   $line
756
	 *
757
	 * @return \Htsl\Parser\Document
758
	 */
759
	protected function showSection( Line$line ):self
760
	{
761
		$sectionName= $line->pregGet('/(?<=\( ).*(?= \))/');
762
763
		if( !isset($this->sections[$sectionName]) ){
764
			$this->openNode(new StringNode($this,$line));
765
766
			return $this;
767
		}
768
		$content= $this->sections[$sectionName]->content;
769
770 View Code Duplication
		if( false!==$this->indentation ){
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...
771
			$content= preg_replace('/(?<=^|\\n)(?!$)/',str_repeat($this->indentation,$this->level),$content);
772
		}
773
774
		$this->append($content);
775
776
		$node= new NamelessSectionNode($this,$line);
777
778
		$node->open();
779
780
		$this->openNode($node);
781
782
		return $this;
783
	}
784
785
	/**
786
	 * Setting document as section definer.
787
	 *
788
	 * @access public
789
	 *
790
	 * @param Section | null $section
791
	 */
792
	public function setSection( Section$section=null ):self
793
	{
794
		if( !$section ){
795
			$this->sectionLevel= 0;
796
			$this->currentSection= null;
797
798
			return $this;
799
		}
800
801
		if( $this->currentSection ){
802
			$this->throw('Nesting definition of section is forbidden.');
803
		}
804
805
		if( isset($this->parent->sections[$section->name]) ){
806
			$this->throw("Section {$section->name} already defined.");
807
		}
808
809
		$this->currentSection= $section;
810
811
		if( $section->name ){
812
			$this->parent->sections[$section->name]=$section;
813
		}
814
815
		$this->sectionLevel= $this->level+1;
816
817
		return $this;
818
	}
819
820
	/**
821
	 * Bubble the sections to parent document.
822
	 *
823
	 * @access protected
824
	 *
825
	 * @return \Htsl\Parser\Document
826
	 */
827
	protected function bubbleSections():self
828
	{
829
		if( $this->parent ){
830
			foreach( $this->sections as $name=>$section ){
831
				if( !isset($this->parent->sections[$name]) ){
832
					$this->parent->sections[$name]=$section;
833
				};
834
			}
835
		}
836
837
		return $this;
838
	}
839
840
	/**
841
	 * Escaping characters to HTML entities.
842
	 *
843
	 * @access public
844
	 *
845
	 * @param  string $input
846
	 *
847
	 * @return string
848
	 */
849
	public function htmlEntities( string$input ):string
850
	{
851
		return htmlentities($input,$this->htsl->getConfig('ENT_flags',$this->docType),$this->htsl->getConfig('charset'),false);
852
	}
853
854
	/**
855
	 * Setting indent level of this document.
856
	 *
857
	 * @access protected
858
	 *
859
	 * @param int $level
860
	 */
861
	protected function setLevel( int$level ):self
862
	{
863
		$level-= $this->level;
864
865
		if( $level<=0 ){
866
			$this->closeNodes(-$level);
867
		}elseif( $level==1 ){
868
			$this->level+= 1;
869
		}else{
870
			$this->throw('Indent error.');
871
		}
872
873
		return $this;
874
	}
875
876
	/**
877
	 * Opening a node.
878
	 *
879
	 * @access protected
880
	 *
881
	 * @param  \Htsl\Parser\Node\Contracts\ANode $node
882
	 *
883
	 * @return \Htsl\Parser\Document
884
	 */
885
	protected function openNode( Node$node ):self
886
	{
887
		array_push($this->openedNodes,$node);
888
889
		$node->scope and $this->setScope($node);
890
891
		return $this;
892
	}
893
894
	/**
895
	 * Closing open node or nodes.
896
	 *
897
	 * @access protected
898
	 *
899
	 * @param  int $level
900
	 *
901
	 * @return \Htsl\Parser\Document
902
	 */
903
	protected function closeNodes( int$level=0 ):self
904
	{
905
		if( empty($this->openedNodes) ) return $this;
906
907
		while( $level-->=0 ){
908
			$node= array_pop($this->openedNodes);
909
910
			$node->scope and $this->removeScope($node);
911
912
			$closer=$node->close($this->currentLine) and $this->appendLine($closer);
913
914
			$this->level-= $level>=0 ?1:0;
915
		}
916
917
		return $this;
918
	}
919
920
	/**
921
	 * Pushing a scope to stack.
922
	 *
923
	 * @access protected
924
	 *
925
	 * @param \Htsl\Parser\Node\Contracts\ANode $scope
926
	 */
927
	protected function setScope( Node$scope ):int
928
	{
929
		return array_unshift($this->scopes,$scope);
930
	}
931
932
	/**
933
	 * Getting current scope on top of stack.
934
	 *
935
	 * @access public
936
	 *
937
	 * @return \Htsl\Parser\Node\Contracts\ANode | null
938
	 */
939
	public function getScope()
940
	{
941
		return $this->scopes[0]??null;
942
	}
943
944
	/**
945
	 * Pop a scope from stack.
946
	 *
947
	 * @access protected
948
	 *
949
	 * @param  \Htsl\Parser\Node\Contracts\ANode $scope
950
	 *
951
	 * @return \Htsl\Parser\Document
952
	 */
953
	protected function removeScope( Node$scope ):self
954
	{
955
		if( $scope!==array_shift($this->scopes) ){
956
			$this->throw('Scope nesting error');
957
		};
958
959
		return $this;
960
	}
961
962
	/**
963
	 * Appending a line of content to parsing result.
964
	 *
965
	 * @access protected
966
	 *
967
	 * @param  string $content
968
	 *
969
	 * @return \Htsl\Parser\Document
970
	 */
971
	protected function appendLine( string$content ):self
972
	{
973 View Code Duplication
		if( false!==$this->indentation ){
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...
974
			$content= str_repeat($this->indentation,$this->level-$this->sectionLevel).$content."\n";
975
		}
976
977
		return $this->append($content);
978
	}
979
980
	/**
981
	 * Appending some content to parsing result.
982
	 *
983
	 * @access protected
984
	 *
985
	 * @param  string $content
986
	 *
987
	 * @return \Htsl\Parser\Document
988
	 */
989
	protected function append( string$content ):self
990
	{
991
		if( $this->currentSection ){
992
			$this->currentSection->append($content);
993
		}else{
994
			$this->content.=$content;
995
		}
996
997
		return $this;
998
	}
999
1000
	/**
1001
	 * Getting the Htsl main object.
1002
	 *
1003
	 * @access public
1004
	 *
1005
	 * @return \Htsl\Htsl
1006
	 */
1007
	public function getHtsl()
1008
	{
1009
		return $this->htsl;
1010
	}
1011
1012
	/**
1013
	 * Throw exception with document name and line number.
1014
	 *
1015
	 * @access public
1016
	 *
1017
	 * @param  string $message
1018
	 *
1019
	 * @throw \Htsl\Parser\HtslParsingException
1020
	 */
1021
	public function throw( string$message )
1022
	{
1023
		throw new HtslParsingException("$message at file {$this->buffer->fileName} line $this->lineNumber");
0 ignored issues
show
Documentation introduced by
The property fileName does not exist on object<Htsl\ReadingBuffer\Contracts\ABuffer>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1024
	}
1025
}
1026