Passed
Push — master ( cd28b2...551bb3 )
by Alain
04:04
created

ShortcodeManager::init_shortcodes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 9.4285
cc 2
eloc 3
nc 2
nop 0
crap 6
1
<?php
2
/**
3
 * Shortcode Manager.
4
 *
5
 * @package   BrightNucleus\Shortcode
6
 * @author    Alain Schlesser <[email protected]>
7
 * @license   GPL-2.0+
8
 * @link      http://www.brightnucleus.com/
9
 * @copyright 2015-2016 Alain Schlesser, Bright Nucleus
10
 */
11
12
namespace BrightNucleus\Shortcode;
13
14
use BrightNucleus\Config\ConfigInterface;
15
use BrightNucleus\Config\ConfigTrait;
16
use BrightNucleus\Dependency\DependencyManagerInterface as DependencyManager;
17
use BrightNucleus\Exception\RuntimeException;
18
19
/**
20
 * Shortcode Manager.
21
 *
22
 * This class manages all the shortcodes that it gets passed within a
23
 * ConfigInterface.
24
 *
25
 * @since   0.1.0
26
 *
27
 * @package BrightNucleus\Shortcode
28
 * @author  Alain Schlesser <[email protected]>
29
 */
30
class ShortcodeManager implements ShortcodeManagerInterface {
31
32
	use ConfigTrait;
33
34
	/*
35
	 * The delimiter that is used to express key-subkey relations in the config.
36
	 */
37
	const CONFIG_SEPARATOR = '/';
38
39
	/*
40
	 * Default classes that are used when omitted from the config.
41
	 */
42
	const DEFAULT_SHORTCODE             = 'BrightNucleus\Shortcode\Shortcode';
43
	const DEFAULT_SHORTCODE_ATTS_PARSER = 'BrightNucleus\Shortcode\ShortcodeAttsParser';
44
	const DEFAULT_SHORTCODE_UI          = 'BrightNucleus\Shortcode\ShortcodeUI';
45
46
	/*
47
	 * The names of the configuration keys.
48
	 */
49
	const KEY_CUSTOM_ATTS_PARSER = 'custom_atts_parser';
50
	const KEY_CUSTOM_CLASS       = 'custom_class';
51
	const KEY_CUSTOM_UI          = 'custom_ui';
52
	const KEY_UI                 = 'ui';
53
	/**
54
	 * Collection of ShortcodeInterface objects.
55
	 *
56
	 * @since 0.1.0
57
	 *
58
	 * @var ShortcodeInterface[]
59
	 */
60
	protected $shortcodes = [ ];
61
62
	/**
63
	 * DependencyManagerInterface implementation.
64
	 *
65
	 * @since 0.1.0
66
	 *
67
	 * @var DependencyManagerInterface
68
	 */
69
	protected $dependencies;
70
71
	/**
72
	 * Collection of ShortcodeUIInterface objects.
73
	 *
74
	 * @since 0.1.0
75
	 *
76
	 * @var ShortcodeUIInterface[]
77
	 */
78
	protected $shortcode_uis = [ ];
79
80
	/**
81
	 * Instantiate a ShortcodeManager object.
82
	 *
83
	 * @since 0.1.0
84
	 *
85
	 * @param ConfigInterface        $config       Configuration to set up the
86
	 *                                             shortcodes.
87
	 * @param DependencyManager|null $dependencies Optional. Dependencies that
88
	 *                                             are needed by the shortcodes.
89
	 * @throws RuntimeException If the config could not be processed.
90
	 */
91
	public function __construct(
92
		ConfigInterface $config,
93
		DependencyManager $dependencies = null
94
	) {
95
		$this->processConfig( $config );
96
		$this->dependencies = $dependencies;
0 ignored issues
show
Documentation Bug introduced by
It seems like $dependencies can also be of type object<BrightNucleus\Dep...ndencyManagerInterface>. However, the property $dependencies is declared as type object<BrightNucleus\Sho...ndencyManagerInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
97
98
		$this->init_shortcodes();
99
	}
100
101
	/**
102
	 * Initialize the Shortcode Manager.
103
	 *
104
	 * @since 0.1.0
105
	 */
106
	protected function init_shortcodes() {
107
		foreach ( $this->getConfigKeys() as $tag ) {
108
			$this->init_shortcode( $tag );
109
		}
110
	}
111
112
	/**
113
	 * Initialize a single shortcode.
114
	 *
115
	 * @since 0.1.0
116
	 *
117
	 * @param string $tag The tag of the shortcode to register.
118
	 */
119
	protected function init_shortcode( $tag ) {
120
		$shortcode_class       = $this->get_shortcode_class( $tag );
121
		$shortcode_atts_parser = $this->get_shortcode_atts_parser_class( $tag );
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $shortcode_atts_parser exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
122
123
		$atts_parser        = new $shortcode_atts_parser(
124
			$this->config->getSubConfig( $tag )
125
		);
126
		$this->shortcodes[] = new $shortcode_class(
127
			$tag,
128
			$this->config->getSubConfig( $tag ),
129
			$atts_parser,
130
			$this->dependencies
131
		);
132
133
		if ( $this->hasConfigKey( $tag, self::KEY_UI ) ) {
134
			$this->init_shortcode_ui( $tag );
135
		}
136
	}
137
138
	/**
139
	 * Get the class name of an implementation of the ShortcodeInterface.
140
	 *
141
	 * @since 0.1.0
142
	 *
143
	 * @param string $tag Shortcode tag to get the class for.
144
	 * @return string Class name of the Shortcode.
145
	 */
146
	protected function get_shortcode_class( $tag ) {
147
		$shortcode_class = $this->hasConfigKey( $tag, self::KEY_CUSTOM_CLASS )
148
			? $this->getConfigKey( $tag, self::KEY_CUSTOM_CLASS )
149
			: self::DEFAULT_SHORTCODE;
150
		return $shortcode_class;
151
	}
152
153
	/**
154
	 * Get the class name of an implementation of the
155
	 * ShortcodeAttsParsersInterface.
156
	 *
157
	 * @since 0.1.0
158
	 *
159
	 * @param string $tag Shortcode tag to get the class for.
160
	 * @return string Class name of the ShortcodeAttsParser.
161
	 */
162
	protected function get_shortcode_atts_parser_class( $tag ) {
163
		$atts_parser = $this->hasConfigKey( $tag, self::KEY_CUSTOM_ATTS_PARSER )
164
			? $this->getConfigKey( $tag, self::KEY_CUSTOM_ATTS_PARSER )
165
			: self::DEFAULT_SHORTCODE_ATTS_PARSER;
166
		return $atts_parser;
167
	}
168
169
	/**
170
	 * Initialize the Shortcode UI for a single shortcode.
171
	 *
172
	 * @since 0.1.0
173
	 *
174
	 * @param string $tag The tag of the shortcode to register the UI for.
175
	 */
176
	protected function init_shortcode_ui( $tag ) {
177
		$shortcode_ui_class = $this->get_shortcode_ui_class( $tag );
178
179
		$this->shortcode_uis[] = new $shortcode_ui_class(
180
			$tag,
181
			$this->config->getSubConfig( $tag, self::KEY_UI ),
0 ignored issues
show
Unused Code introduced by
The call to ConfigInterface::getSubConfig() has too many arguments starting with self::KEY_UI.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
182
			$this->dependencies
183
		);
184
	}
185
186
	/**
187
	 * Get the class name of an implementation of the ShortcodeUIInterface.
188
	 *
189
	 * @since 0.1.0
190
	 *
191
	 * @param string $tag Configuration settings.
192
	 * @return string Class name of the ShortcodeUI.
193
	 */
194
	protected function get_shortcode_ui_class( $tag ) {
195
		$ui_class = $this->hasConfigKey( $tag, self::KEY_CUSTOM_UI )
196
			? $this->getConfigKey( $tag, self::KEY_CUSTOM_UI )
197
			: self::DEFAULT_SHORTCODE_UI;
198
		return $ui_class;
199
	}
200
201
	/**
202
	 * Register all of the shortcode handlers.
203
	 *
204
	 * @since 0.1.0
205
	 *
206
	 * @param mixed $context Optional. Context information to pass to shortcode.
207
	 * @return void
208
	 */
209
	public function register( $context = null ) {
210
		$context                  = $this->validate_context( $context );
211
		$context['page_template'] = $this->get_page_template();
212
213
		array_walk( $this->shortcodes,
214
			function ( $shortcode ) use ( $context ) {
215
				/** @var ShortcodeInterface $shortcode */
216
				$shortcode->register( $context );
217
			} );
218
219
		// This hook only gets fired when Shortcode UI plugin is active.
220
		\add_action(
221
			'register_shortcode_ui',
222
			[ $this, 'register_shortcode_ui', ]
223
		);
224
	}
225
226
	/**
227
	 * Validate the context to make sure it is an array.
228
	 *
229
	 * @since 0.2.3
230
	 *
231
	 * @param mixed $context The context as passed in by WordPress.
232
	 * @return array Validated context.
233
	 */
234
	protected function validate_context( $context ) {
235
		if ( is_string( $context ) ) {
236
			return [ 'wp_context' => $context ];
237
		}
238
		return (array) $context;
239
	}
240
241
	/**
242
	 * Get the name of the page template.
243
	 *
244
	 * @since 0.1.0
245
	 *
246
	 * @return string Name of the page template.
247
	 */
248
	protected function get_page_template() {
249
		$template = str_replace(
250
			\trailingslashit( \get_stylesheet_directory() ),
251
			'',
252
			\get_page_template()
253
		);
254
		return $template;
255
	}
256
257
	/**
258
	 * Register the shortcode UI handlers.
259
	 *
260
	 * @since 0.1.0
261
	 */
262
	public function register_shortcode_ui() {
263
		$template = $this->get_page_template();
264
		$context  = [ 'page_template' => $template ];
265
266
		array_walk( $this->shortcode_uis,
267
			function ( $shortcode_ui ) use ( $context ) {
268
				/** @var ShortcodeUIInterface $shortcode_ui */
269
				$shortcode_ui->register( $context );
270
			}
271
		);
272
	}
273
274
	/**
275
	 * Execute a specific shortcode directly from code.
276
	 *
277
	 * @since 0.2.4
278
	 *
279
	 * @param string $tag     Tag of the shortcode to execute.
280
	 * @param array  $atts    Array of attributes to pass to the shortcode.
281
	 * @param null   $content Inner content to pass to the shortcode.
282
	 * @return string Rendered HTML.
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
283
	 */
284
	public function do_tag( $tag, array $atts = [ ], $content = null ) {
285
		\BrightNucleus\Shortcode\do_tag( $tag, $atts, $content );
286
	}
287
}
288