Issues (3)

src/BladeOne_Engine.php (1 issue)

Labels
Severity
1
<?php
2
3
declare( strict_types=1 );
4
5
/**
6
 * Implementation of BladeOne for the PinkCrab Perique frameworks Renderable interface
7
 *
8
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
 *
20
 * @author Glynn Quelch <[email protected]>
21
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
22
 * @package PinkCrab\BladeOne_Engine
23
 */
24
25
namespace PinkCrab\BladeOne;
26
27
use Exception;
28
use ReflectionClass;
29
use BadMethodCallException;
30
use eftec\bladeone\BladeOne;
0 ignored issues
show
This use statement conflicts with another class in this namespace, PinkCrab\BladeOne\BladeOne. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
31
use eftec\bladeonehtml\BladeOneHtml;
32
use PinkCrab\BladeOne\PinkCrab_BladeOne;
33
use PinkCrab\Perique\Interfaces\Renderable;
34
use PinkCrab\Perique\Services\View\View_Model;
35
use PinkCrab\Perique\Services\View\Component\Component;
36
use PinkCrab\Perique\Services\View\Component\Component_Compiler;
37
38
/**
39
 * BladeOne Engine for the PinkCrab Perique Framework
40
 */
41
class BladeOne_Engine implements Renderable {
42
43
	/**
44
	 * BladeOne Instance
45
	 *
46
	 * @var PinkCrab_BladeOne
47
	 */
48
	protected static PinkCrab_BladeOne $blade;
49
50
	/**
51
	 * Access to the component compiler.
52
	 *
53
	 * @var Component_Compiler|null
54
	 */
55
	protected ?Component_Compiler $component_compiler = null;
56
57
	/**
58
	 * Creates an instance with blade one.
59
	 *
60
	 * @param PinkCrab_BladeOne $blade
61
	 */
62
	final public function __construct( PinkCrab_BladeOne $blade ) {
63
		static::$blade = $blade;
64
	}
65
66
	/**
67
	 * Static constructor with BladeOne initialisation details
68
	 *
69
	 * @param string  $template_path If null then it uses (caller_folder)/views
70
	 * @param string  $compiled_path If null then it uses (caller_folder)/compiles
71
	 * @param integer $mode          =[BladeOne::MODE_AUTO,BladeOne::MODE_DEBUG,BladeOne::MODE_FAST,BladeOne::MODE_SLOW][$i]
72
	 * @return self
73
	 */
74
	public static function init(
75
		$template_path = null,
76
		?string $compiled_path = null,
77
		int $mode = 0
78
	): self {
79
		return new static( new PinkCrab_BladeOne( $template_path, $compiled_path, $mode ) );
80
	}
81
82
	/**
83
	 * Returns the current BladeOne instance.
84
	 *
85
	 * @return BladeOne
86
	 */
87
	public function get_blade(): BladeOne {
88
		return static::$blade;
89
	}
90
91
	/**
92
	 * Returns the base view path.
93
	 *
94
	 * @return string
95
	 * @since 1.4.0
96
	 */
97
	public function base_view_path(): string {
98
		$paths = static::$blade->get_template_paths();
99
		return ! empty( $paths ) ? reset( $paths ) : '';
100
	}
101
102
	/**
103
	 * Sets the esc function.
104
	 *
105
	 * @param string $esc
106
	 * @return self
107
	 */
108
	public function set_esc_function( string $esc ): self {
109
		static::$blade->set_esc_function( $esc );
110
		return $this;
111
	}
112
113
	/**
114
	 * Sets the component compiler.
115
	 *
116
	 * @param Component_Compiler $compiler
117
	 * @return void
118
	 */
119
	public function set_component_compiler( Component_Compiler $compiler ): void {
120
		$this->component_compiler = $compiler;
121
	}
122
123
	/**
124
	 * Display a view and its context.
125
	 *
126
	 * @param string                  $view       The view to render.
127
	 * @param iterable<string, mixed> $data       The data to pass to the view.
128
	 * @param boolean                 $print_mode If true it will print the view, if false it will return the view as a string.
129
	 *
130
	 * @return void|string
131
	 */
132
	public function render( string $view, iterable $data, bool $print_mode = true ) {
133
		if ( $print_mode ) {
134
			print static::$blade->run( $view, (array) $data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
135
		} else {
136
			return static::$blade->run( $view, (array) $data );
137
		}
138
	}
139
140
	/**
141
	 * Renders a view Model
142
	 *
143
	 * @param View_Model $view_model The View Model to render
144
	 * @param boolean    $print_mode If true it will print the view, if false it will return the view as a string.
145
	 *
146
	 * @return string|void
147
	 */
148
	public function view_model( View_Model $view_model, bool $print_mode = true ) {
149
		return $this->render( str_replace( array( '/', '\\' ), '.', $view_model->template() ), $view_model->data(), $print_mode );
150
	}
151
152
		/**
153
	 * Renders a component.
154
	 *
155
	 * @param Component $component
156
	 * @param boolean   $print_mode If true it will print the view, if false it will return the view as a string.
157
	 *
158
	 * @return string|void
159
	 */
160
	public function component( Component $component, bool $print_mode = true ) {
161
162
		// Throw exception of no compiler passed.
163
		if ( null === $this->component_compiler ) {
164
			throw new Exception( 'No component compiler passed to BladeOne' );
165
		}
166
167
		// Compile the component.
168
		$compiled = $this->component_compiler->compile( $component );
169
		return $this->render( str_replace( array( '/', '\\' ), '.', $compiled->template() ), $compiled->data(), $print_mode );
170
	}
171
172
	/**
173
	 * Magic instanced method caller.
174
	 *
175
	 * @param string       $method
176
	 * @param array<mixed> $args
177
	 * @return mixed
178
	 * @throws BadMethodCallException
179
	 */
180
	public function __call( string $method, array $args = array() ) {
181
		if ( ! $this->is_method( $method ) ) {
182
			throw new BadMethodCallException( esc_attr( "{$method} is not a valid method on the BladeOne instance." ) );
183
		}
184
185
		return static::$blade->{$method}( ...$args );
186
	}
187
188
	/**
189
	 * Magic static method caller.
190
	 *
191
	 * @param string       $method
192
	 * @param array<mixed> $args
193
	 * @return mixed
194
	 * @throws BadMethodCallException
195
	 */
196
	public static function __callStatic( string $method, array $args = array() ) {
197
		if ( ! static::is_static_method( $method ) ) {
198
			throw new BadMethodCallException( esc_attr( "{$method} is not a valid method on the BladeOne instance." ) );
199
		}
200
201
		return static::$blade::{$method}( ...$args );
202
	}
203
204
	/**
205
	 * Checks if the passed method exists, is public and isnt static.
206
	 *
207
	 * @param string $method
208
	 * @return boolean
209
	 */
210
	protected function is_method( string $method ): bool {
211
		$class_reflection = new ReflectionClass( static::$blade );
212
213
		// Check method exists.
214
		if ( ! $class_reflection->hasMethod( $method ) ) {
215
			return false;
216
		}
217
218
		$method_reflection = $class_reflection->getMethod( $method );
219
220
		return $method_reflection->isPublic() && ! $method_reflection->isStatic();
221
	}
222
223
	/**
224
	 * Checks if the passed method exists, is public and IS static.
225
	 *
226
	 * @param string $method
227
	 * @return boolean
228
	 */
229
	protected static function is_static_method( string $method ): bool {
230
		$class_reflection = new ReflectionClass( static::$blade );
231
232
		// Check method exists.
233
		if ( ! $class_reflection->hasMethod( $method ) ) {
234
			return false;
235
		}
236
237
		$method_reflection = $class_reflection->getMethod( $method );
238
		return $method_reflection->isPublic() && $method_reflection->isStatic();
239
	}
240
241
	/**
242
	 * Sets if piping is enabled in templates.
243
	 *
244
	 * @param boolean $allow_pipe
245
	 * @return self
246
	 */
247
	public function allow_pipe( bool $allow_pipe = true ): self {
248
		static::$blade->pipeEnable = $allow_pipe; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
249
		return $this;
250
	}
251
252
	/**
253
	 * Register a handler for custom directives.
254
	 *
255
	 * @param string   $name
256
	 * @param callable $handler
257
	 * @return self
258
	 */
259
	public function directive( string $name, callable $handler ): self {
260
		static::$blade->directive( $name, $handler );
261
		return $this;
262
	}
263
264
	/**
265
	 * Register a handler for custom directives for run at runtime
266
	 *
267
	 * @param string   $name
268
	 * @param callable $handler
269
	 * @return self
270
	 */
271
	public function directive_rt( $name, callable $handler ): self {
272
		static::$blade->directiveRT( $name, $handler );
273
		return $this;
274
	}
275
276
	/**
277
	 * Define a template alias
278
	 *
279
	 * @param string      $view  example "folder.template"
280
	 * @param string|null $alias example "mynewop". If null then it uses the name of the template.
281
	 * @return self
282
	 */
283
	public function add_include( $view, $alias = null ): self {
284
		static::$blade->addInclude( $view, $alias );
285
		return $this;
286
	}
287
288
	/**
289
	 * Define a class with a namespace
290
	 *
291
	 * @param string $alias_name
292
	 * @param string $class_with_namespace
293
	 * @return self
294
	 */
295
	public function add_alias_classes( $alias_name, $class_with_namespace ): self {
296
		static::$blade->addAliasClasses( $alias_name, $class_with_namespace );
297
		return $this;
298
	}
299
300
	/**
301
	 * Set the compile mode
302
	 *
303
	 * @param integer $mode BladeOne::MODE_AUTO, BladeOne::MODE_DEBUG, BladeOne::MODE_FAST, BladeOne::MODE_SLOW
304
	 * @return self
305
	 */
306
	public function set_mode( int $mode ): self {
307
		static::$blade->setMode( $mode );
308
		return $this;
309
	}
310
311
	/**
312
	 * Set the comment mode
313
	 *
314
	 * @param integer $comment_mode BladeOne::COMMENT_PHP, BladeOne::COMMENT_RAW, BladeOne::COMMENT_NONE
315
	 * @return self
316
	 */
317
	public function set_comment_mode( int $comment_mode ): self {
318
		static::$blade->setCommentMode( $comment_mode );
319
		return $this;
320
	}
321
322
	/**
323
	 * Adds a global variable. If <b>$var_name</b> is an array then it merges all the values.
324
	 * <b>Example:</b>
325
	 * <pre>
326
	 * $this->share('variable',10.5);
327
	 * $this->share('variable2','hello');
328
	 * // or we could add the two variables as:
329
	 * $this->share(['variable'=>10.5,'variable2'=>'hello']);
330
	 * </pre>
331
	 *
332
	 * @param string|array<string, mixed> $var_name It is the name of the variable or it is an associative array
333
	 * @param mixed                       $value
334
	 * @return $this
335
	 */
336
	public function share( $var_name, $value = null ): self {
337
		static::$blade->share( $var_name, $value );
338
		return $this;
339
	}
340
341
	/**
342
	 * Sets the function used for resolving classes with inject.
343
	 *
344
	 * @param callable $resolver
345
	 * @return $this
346
	 */
347
	public function set_inject_resolver( callable $resolver ): self {
348
		static::$blade->setInjectResolver( $resolver );
349
		return $this;
350
	}
351
352
	/**
353
	 * Set the file extension for the template files.
354
	 * It must includes the leading dot e.g. .blade.php
355
	 *
356
	 * @param string $file_extension Example: .prefix.ext
357
	 * @return $this
358
	 */
359
	public function set_file_extension( string $file_extension ): self {
360
		static::$blade->setFileExtension( $file_extension );
361
		return $this;
362
	}
363
364
	/**
365
	 * Set the file extension for the compiled files.
366
	 * Including the leading dot for the extension is required, e.g. .bladec
367
	 *
368
	 * @param string $file_extension
369
	 * @return $this
370
	 */
371
	public function set_compiled_extension( string $file_extension ): self {
372
		static::$blade->setCompiledExtension( $file_extension );
373
		return $this;
374
	}
375
}
376