Completed
Push — master ( ef6746...5a635f )
by Ron
07:26
created

FileWorker::getCurrentWorkDir()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
c 0
b 0
f 0
rs 9.4285
cc 3
eloc 10
nc 2
nop 1
1
<?php
2
namespace View\Workers;
3
4
use Exception;
5
use View\Delegates\Delegate;
6
use View\Exceptions\ResourceNotFoundException;
7
use View\Exceptions\VirtualPathNotRegisteredException;
8
use View\Helpers\Directories;
9
use View\Helpers\ViewTryFinallySimulator;
10
use View\Workers\FileWorker\FileWorkerConfiguration;
11
12
class FileWorker extends AbstractWorker {
13
	/** @var string */
14
	private $currentWorkDir;
15
	/** @var string */
16
	private $fileExt;
17
	/** @var Delegate */
18
	private $parent;
19
20
	/**
21
	 * @param string $basePath
22
	 * @param string $fileExt
23
	 * @param array $vars
24
	 * @param WorkerConfiguration $configuration
25
	 * @param Delegate $parent
26
	 */
27
	public function __construct($basePath, $fileExt = null, array $vars = array(), WorkerConfiguration $configuration = null, Delegate $parent = null) {
28
		if($fileExt === null)  {
29
			$fileExt = '.phtml';
30
		}
31
		if($configuration === null) {
32
			$configuration = new FileWorkerConfiguration();
33
		}
34
		parent::__construct($vars, [], $configuration);
35
		$this->currentWorkDir = $basePath;
36
		$this->fileExt = $fileExt;
37
		$this->parent = $parent;
38
	}
39
40
	/**
41
	 * @param string|callable $resource
42
	 * @param array $vars
43
	 * @throws \Exception
44
	 * @return string
45
	 */
46
	public function render($resource, array $vars = array()) {
47
		$worker = new FileWorker($this->currentWorkDir, $this->fileExt, $this->getVars(), $this->getConfiguration(), $this->parent);
48
		return $worker->getContent($resource, $vars, $this->getRegions());
0 ignored issues
show
Documentation introduced by
$resource is of type callable, but the function expects a string.

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...
49
	}
50
51
	/**
52
	 * @param string $resource
53
	 * @param array $vars
54
	 * @param array $regions
55
	 * @return string
56
	 * @throws \Exception
57
	 */
58
	public function getContent($resource, array $vars = array(), array $regions = array()) {
59
		list($oldVars, $oldRegions) = [$this->getVars(), $this->getRegions()];
60
		$subPath = dirname($resource) !== '.' ? dirname($resource) : '';
61
		$filename = basename($resource);
62
		
63
		$this->currentWorkDir = $this->getCurrentWorkDir($subPath);
64
65
		$vars = array_merge($oldVars, $vars);
66
		$regions = array_merge($oldRegions, $regions);
67
		$this->setVars($vars);
68
		$this->setRegions($regions);
69
70
		return ViewTryFinallySimulator::tryThis(function () use ($filename, $resource, $vars) {
71
			$content = $this->obRecord(function () use ($filename, $resource, $vars) {
72
				$templateFilename = Directories::concat($this->currentWorkDir, $filename);
73
				$templateFilename = $this->normalize($templateFilename);
74
				$templatePath = stream_resolve_include_path($templateFilename . $this->fileExt);
75
				if($templatePath === false) {
76
					$templatePath = stream_resolve_include_path($templateFilename);
77
				}
78
				if($templatePath !== false) {
79
					$templateFilename = $templatePath;
80
					$fn = function () use ($templateFilename) {
81
						/** @noinspection PhpIncludeInspection */
82
						require $templateFilename;
83
					};
84
					$fn->bindTo(new \stdClass());
85
					call_user_func($fn);
86
				} else {
87
					if($this->parent !== null) {
88
						echo $this->parent->render($resource, $vars);
89
					} else {
90
						throw new ResourceNotFoundException("Resource not found: {$resource}");
91
					}
92
				}
93
			});
94
			return $this->generateLayoutContent($content);
95
		}, function () use ($oldVars, $oldRegions) {
96
			$this->setVars($oldVars);
97
			$this->setRegions($oldRegions);
0 ignored issues
show
Documentation introduced by
$oldRegions is of type array<integer,object<View\Helpers\StringBucket>>, but the function expects a array<integer,array>.

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...
98
		});
99
	}
100
101
	/**
102
	 * @param string $content
103
	 * @return string
104
	 * @throws \Exception
105
	 */
106
	private function generateLayoutContent($content) {
107
		if($this->getLayout() !== null) {
108
			$regions = $this->getRegions();
109
			$regions['content'] = $content;
110
			$layoutResource = $this->getLayout();
111
			$layoutVars = $this->getLayoutVars();
112
			$worker = new FileWorker($this->currentWorkDir, $this->fileExt, [], $this->getConfiguration(), $this->parent);
113
			$content = $worker->getContent($layoutResource, $layoutVars, $regions);
114
		}
115
		return $content;
116
	}
117
118
	/**
119
	 * @param callback $fn
120
	 * @return string
121
	 * @throws \Exception
122
	 */
123
	private function obRecord($fn) {
124
		try {
125
			ob_start();
126
			call_user_func($fn);
127
			return ob_get_clean();
128
		} catch (Exception $e) {
129
			ob_end_flush();
130
			throw $e;
131
		}
132
	}
133
134
	/**
135
	 * @param string $templateFilename
136
	 * @return string
137
	 */
138
	private function normalize($templateFilename) {
139
		if(strpos($templateFilename, '..')) {
140
			$templateFilename = strtr($templateFilename, [DIRECTORY_SEPARATOR => '/']);
141
			$templateFilename = preg_replace('/\\/+/', '/', $templateFilename);
142
			$parts = explode('/', $templateFilename);
143
			$correctedParts = [];
144
			foreach($parts as $part) {
145
				if($part === '.') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
146
					// Skip
147
				} elseif($part === '..') {
148
					if(count($correctedParts)) {
149
						array_pop($correctedParts);
150
					} else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
151
						// Skip
152
					}
153
				} else {
154
					$correctedParts[] = $part;
155
				}
156
			}
157
			return join('/', $correctedParts);
158
		}
159
		return $templateFilename;
160
	}
161
	
162
	/**
163
	 * @param string $subPath
164
	 * @return string
165
	 */
166
	private function getCurrentWorkDir($subPath) {
167
		if(substr($subPath, 0, 1) === '@') {
168
			$replace = function ($matches) {
169
				$paths = $this->getConfiguration()->getPaths();
170
				if(!array_key_exists($matches[2], $paths)) {
171
					throw new VirtualPathNotRegisteredException("Virtual path not registered: {$matches[1]}{$matches[2]}");
172
				}
173
				return $paths[$matches[2]];
174
			};
175
			$subPath = preg_replace_callback('/^(@)([^\\/]+)/', $replace, $subPath);
176
			return $subPath;
177
		}
178
		return Directories::concat($this->currentWorkDir, $subPath);
179
	}
180
}
181