Issues (7)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/EmbeDi.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This software package is licensed under `AGPL, Commercial` license[s].
5
 *
6
 * @package maslosoft/embedi
7
 * @license AGPL, Commercial
8
 *
9
 * @copyright Copyright (c) Peter Maselkowski <[email protected]>
10
 *
11
 */
12
13
namespace Maslosoft\EmbeDi;
14
15
use InvalidArgumentException;
16
use Maslosoft\EmbeDi\Interfaces\AdapterInterface;
17
use Maslosoft\EmbeDi\Managers\SourceManager;
18
use Maslosoft\EmbeDi\Storage\EmbeDiStore;
19
use ReflectionObject;
20
use ReflectionProperty;
21
22
/**
23
 * Embedded dependency injection container
24
 *
25
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
26
 */
27
class EmbeDi
28
{
29
30
	/**
31
	 * This is default instance name, and component name.
32
	 */
33
	const DefaultInstanceId = 'embedi';
0 ignored issues
show
Constant DefaultInstanceId should be defined in uppercase
Loading history...
34
35
	/**
36
	 * Class field in configuration arrays
37
	 * @see apply()
38
	 * @see export()
39
	 * @var string
40
	 */
41
	public $classField = 'class';
42
43
	/**
44
	 * Instance id
45
	 * @var string
46
	 */
47
	private $_instanceId = '';
48
49
	/**
50
	 * Preset ID
51
	 * @var string
52
	 */
53
	private $_presetId = '';
54
55
	/**
56
	 * Storage container
57
	 * @var EmbeDiStore
58
	 */
59
	private $storage = null;
60
61
	/**
62
	 * Configs source manager
63
	 * @var SourceManager
64
	 */
65
	private $sm = null;
66
67
	/**
68
	 * Flyweight instances of EmbeDi
69
	 * @var EmbeDi[]
70
	 */
71
	private static $_instances = [];
72
73
	/**
74
	 * Create container with provided id
75
	 * @param string $instanceId
76
	 * @param string $presetId If set will lookup configuration in depper array level
77
	 * @param array $config Configuration of EmbeDi
78
	 */
79 17
	public function __construct($instanceId = EmbeDi::DefaultInstanceId, $presetId = null, $config = [])
0 ignored issues
show
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
80
	{
81 17
		$this->_instanceId = $instanceId;
82 17
		$this->_presetId = $presetId;
83 17
		if (!empty($config))
84 17
		{
85 1
			$this->apply($config, $this);
86 1
		}
87 17
		$this->storage = new EmbeDiStore(__CLASS__, EmbeDiStore::StoreId);
88
89
		/**
90
		 * TODO Pass $this as second param
91
		 */
92 17
		$this->sm = new SourceManager($instanceId, $presetId);
93 17
		if (!empty($presetId))
94 17
		{
95 1
			$key = $instanceId . '.' . $presetId;
96 1
		}
97
		else
98
		{
99 16
			$key = $instanceId;
100
		}
101
		// Assign flyweight instance
102 17
		if (empty(self::$_instances[$key]))
103 17
		{
104 5
			self::$_instances[$key] = $this;
105 5
		}
106 17
	}
107
108 2
	public function __get($name)
109
	{
110 2
		$methodName = sprintf('get%s', ucfirst($name));
111 2
		return $this->{$methodName}();
112
	}
113
114 3
	public function __set($name, $value)
115
	{
116 3
		$methodName = sprintf('set%s', ucfirst($name));
117 3
		return $this->{$methodName}($value);
118
	}
119
120
	/**
121
	 * Get flyweight instance of embedi.
122
	 * This will create instance only if `$instanceId` insntace id does not exists.
123
	 * If named instance exists, or was ever create - existing instance will be used.
124
	 * Use this function especially when require many `EmbeDi` calls,
125
	 * for instance when creating `EmbeDi` in loops:
126
	 * ```php
127
	 * foreach($configs as $config)
128
	 * {
129
	 * 		(new EmbeDi)->apply($config);
130
	 * }
131
	 * ```
132
	 * In abowe example at each loop iteration new `EmbeDi` instance is created.
133
	 * While it is still lightweight, it's unnessesary overhead.
134
	 *
135
	 * This can be made in slightly more optimized way by using `fly` function:
136
	 * ```php
137
	 * foreach($configs as $config)
138
	 * {
139
	 * 		EmbeDi::fly()->apply($config);
140
	 * }
141
	 * ```
142
	 * In above example only one instance of `EmbeDi` is used.
143
	 * @param string $instanceId
144
	 * @return EmbeDi
145
	 */
146 5
	public static function fly($instanceId = EmbeDi::DefaultInstanceId, $presetId = null)
0 ignored issues
show
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
147
	{
148 5
		if (!empty($presetId))
149 5
		{
150 1
			$key = $instanceId . '.' . $presetId;
151 1
		}
152
		else
153
		{
154 5
			$key = $instanceId;
155
		}
156 5
		if (empty(self::$_instances[$key]))
157 5
		{
158 1
			self::$_instances[$key] = new static($instanceId, $presetId);
159 1
		}
160 5
		return self::$_instances[$key];
161
	}
162
163 2
	public function getAdapters()
164
	{
165 2
		return $this->storage->adapters;
166
	}
167
168
	/**
169
	 * TODO Create AdaptersManager
170
	 */
171 7
	public function setAdapters($adapters)
172
	{
173 7
		$instances = [];
174 7
		foreach ($adapters as $adapter)
175
		{
176
			// Assuming class name
177 7
			if (is_string($adapter))
178 7
			{
179 1
				$instances[] = new $adapter;
180 1
				continue;
181
			}
182
			// Set directly
183 7
			if ($adapter instanceof AdapterInterface)
184 7
			{
185 7
				$instances[] = $adapter;
186 7
				continue;
187
			}
188
			else
189
			{
190 1
				throw new InvalidArgumentException(sprintf('Adapter of `%s->adapters` is of type `%s`, string (class name) or `%s` required', __CLASS__, gettype($adapter) == 'object' ? get_class($adapter) : gettype($adapter), AdapterInterface::class));
191
			}
192 7
		}
193 7
		$this->storage->adapters = $instances;
194 7
		return $this;
195
	}
196
197
	/**
198
	 * Add configuration adapter
199
	 * TODO Create AdaptersManager
200
	 * @param AdapterInterface $adapter
201
	 */
202 4
	public function addAdapter(AdapterInterface $adapter)
203
	{
204 4
		if(null === $this->storage->adapters)
205 4
		{
206 2
			$this->storage->adapters = [];
207 2
		}
208 4
		array_unshift($this->storage->adapters, $adapter);
209 4
	}
210
211
	/**
212
	 * Add configuration source for later use
213
	 * Config should have keys of component id and values of config.
214
	 * Example:
215
	 * ```
216
	 * [
217
	 * 		'logger' => [
218
	 * 			'class' => Monolog\Logger\Logger,
219
	 * 		],
220
	 * 		'mangan' => [
221
	 * 			'@logger' => 'logger'
222
	 * 		]
223
	 * ]
224
	 * ```
225
	 * Attributes starting with `@` denotes that link to other
226
	 * config component should be used. In example above, mangan field `logger`
227
	 * will be configured with monolog logger.
228
	 * @deprecated Use Maslosoft\EmbeDi\Adapters\ArrayAdapter instead
229
	 * @param mixed[] $source
230
	 */
231
	public function addConfig($source)
232
	{
233
		$this->sm->add($source);
234
	}
235
236
	/**
237
	 * Check whenever current configuration is stored.
238
	 * @return bool
239
	 */
240 17
	public function isStored($object)
241
	{
242 17
		return (new DiStore($object, $this->_instanceId, $this->_presetId))->stored;
243
	}
244
245
	/**
246
	 * Configure existing object from previously stored configuration.
247
	 * Typically this will will be called in your class constructor.
248
	 * Will try to find configuration in adapters if it's not stored.
249
	 * TODO Use SourceManager here, before adapters
250
	 * TODO Create AdaptersManager and use here
251
	 * @param object $object
252
	 * @return object
253
	 */
254 17
	public function configure($object)
255
	{
256 17
		$storage = new DiStore($object, $this->_instanceId, $this->_presetId);
257
258
		// Only configure if stored
259 17
		if ($this->isStored($object))
260 17
		{
261
			/**
262
			 * TODO Use apply() here
263
			 */
264 2
			foreach ($storage->data as $name => $value)
265
			{
266 2
				$class = $storage->classes[$name];
267
				if ($class)
268 2
				{
269 1
					$object->$name = new $class;
270 1
					$this->configure($object->$name);
271 1
				}
272
				else
273
				{
274 2
					$object->$name = $value;
275
				}
276 2
			}
277 2
			return;
278
		}
279
280
		// Try to find configuration in adapters
281 17
		foreach ($this->storage->adapters as $adapter)
282
		{
283 9
			$config = $adapter->getConfig(get_class($object), $this->_instanceId, $this->_presetId);
284
			if ($config)
285 9
			{
286 8
				$this->apply($config, $object);
287 8
				return;
288
			}
289 9
		}
290 9
	}
291
292
	/**
293
	 * Apply configuration to object from array.
294
	 *
295
	 * This can also create object if passed configuration array have `class` field.
296
	 *
297
	 * Example of creating object:
298
	 * ```
299
	 * $config = [
300
	 * 		'class' => Vendor\Component::class,
301
	 * 		'title' => 'bar'
302
	 * ];
303
	 * (new Embedi)->apply($config);
304
	 * ```
305
	 *
306
	 * Example of applying config to existing object:
307
	 * ```
308
	 * $config = [
309
	 * 		'title' => 'bar'
310
	 * ];
311
	 * (new Embedi)->apply($config, new Vendor\Component);
312
	 * ```
313
	 *
314
	 * If `$configuration` arguments is string, it will simply instantiate class:
315
	 * ```
316
	 * (new Embedi)->apply('Vendor\Package\Component');
317
	 * ```
318
	 *
319
	 * @param string|mixed[][] $configuration
320
	 * @param object $object Object to configure, set to null to create new one
321
	 * @return object
322
	 */
323 11
	public function apply($configuration, $object = null)
324
	{
325 11
		if (is_string($configuration))
326 11
		{
327
			return new $configuration;
328
		}
329 11
		if (null === $object && array_key_exists($this->classField, $configuration))
330 11
		{
331 11
			$className = $configuration[$this->classField];
332 11
			unset($configuration[$this->classField]);
333 11
			$object = new $className;
334 11
		}
335 11
		foreach ($configuration as $name => $value)
336
		{
337 11
			if ($name === $this->classField)
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $name (integer) and $this->classField (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
338 11
			{
339 8
				continue;
340
			}
341 11
			if (strpos($name, '@') === 0)
342 11
			{
343 1
				$name = substr($name, 1);
344 1
				$object->$name = $this->sm->get($value);
345 1
				continue;
346
			}
347 11
			if (is_array($value) && array_key_exists($this->classField, $value))
348 11
			{
349 10
				$object->$name = $this->apply($value);
350 10
			}
351
			else
352
			{
353 11
				$object->$name = $value;
354
			}
355 11
		}
356 11
		return $object;
357
	}
358
359
	/**
360
	 * Export object configuration to array
361
	 * @param object $object
362
	 * @param string[] $fields
363
	 * @return mixed[][]
364
	 */
365 4
	public function export($object, $fields = [])
366
	{
367 4
		$data = [];
368 4
		foreach ($this->_getFields($object, $fields) as $name)
369
		{
370
			// If object, recurse
371 4
			if (!isset($object->$name))
372 4
			{
373 3
				continue;
374
			}
375 4
			if (is_object($object->$name))
376 4
			{
377 1
				$data[$name] = $this->export($object->$name);
378 1
			}
379
			else
380
			{
381 4
				$data[$name] = $object->$name;
382
			}
383 4
		}
384 4
		$data[$this->classField] = get_class($object);
385 4
		return $data;
386
	}
387
388
	/**
389
	 * Store object configuration.
390
	 *
391
	 * This will be typically called in init method of your component.
392
	 * After storing config, configuration will be available in `configure` method.
393
	 * `configure` method should be called in your class constructor.
394
	 *
395
	 * If you store config and have `configure` method call,
396
	 * after subsequent creations of your component will be configured by EmbeDi.
397
	 *
398
	 * Both methods could be called in constructor, if you don't need additional
399
	 * initialization code after configuring object.
400
	 *
401
	 * Example workflow:
402
	 * ```
403
	 * class Component
404
	 * {
405
	 * 		public $title = '';
406
	 *
407
	 * 		public function __construct()
408
	 * 		{
409
	 * 			(new EmbeDi)->configure($this);
410
	 * 		}
411
	 *
412
	 * 		public function init()
413
	 * 		{
414
	 * 			(new EmbeDi)->store($this);
415
	 * 		}
416
	 * }
417
	 *
418
	 * $c1 = new Component();
419
	 * $c1->title = 'foo';
420
	 * $c1->init();
421
	 *
422
	 * $c2 = new Component();
423
	 *
424
	 * echo $c2->title; // 'foo'
425
	 * ```
426
	 *
427
	 * Parameter `$fields` tell's EmbeDi to store only subset of class fields.
428
	 * Example:
429
	 * ```
430
	 * (new EmbeDi)->store($this, ['title']);
431
	 * ```
432
	 *
433
	 * Parameter `$update` tell's EmbeDi to update existing configuration.
434
	 * By default configuration is not ovveriden on subsequent `store` calls.
435
	 * This is done on purpose, to not mess basic configuration.
436
	 *
437
	 * @param object $object Object to store
438
	 * @param string[] $fields Fields to store
439
	 * @param bool $update Whenever to update existing configuration
440
	 * @return mixed[] Stored data
441
	 */
442 7
	public function store($object, $fields = [], $update = false)
443
	{
444 7
		$storage = new DiStore($object, $this->_instanceId);
445
446
		// Do not modify stored instance
447 7
		if ($this->isStored($object) && !$update)
448 7
		{
449 1
			return $storage;
450
		}
451
452 7
		$data = [];
453 7
		$classes = [];
454 7
		foreach ($this->_getFields($object, $fields) as $name)
455
		{
456
			// If object, recurse
457 7
			if (is_object($object->$name))
458 7
			{
459 1
				$data[$name] = $this->store($object->$name);
460 1
				$classes[$name] = get_class($object->$name);
461 1
			}
462
			else
463
			{
464 7
				$data[$name] = $object->$name;
465 7
				$classes[$name] = '';
466
			}
467 7
		}
468 7
		$storage->stored = true;
469 7
		$storage->data = $data;
470 7
		$storage->classes = $classes;
471 7
		$storage->class = get_class($object);
472 7
		return $data;
473
	}
474
475
	/**
476
	 * Get class fields of object. By default all public and non static fields are returned.
477
	 * This can be overridden by passing `$fields` names of fields. These are not checked for existence.
478
	 * @param object $object
479
	 * @param string[] $fields
480
	 * @return string[]
481
	 */
482 8
	private function _getFields($object, $fields)
483
	{
484 8
		if (empty($fields))
485 8
		{
486 8
			foreach ((new ReflectionObject($object))->getProperties(ReflectionProperty::IS_PUBLIC) as $property)
487
			{
488
				// http://stackoverflow.com/a/15784768/133408
489 8
				if (!$property->isStatic())
490 8
				{
491 8
					$fields[] = $property->name;
492 8
				}
493 8
			}
494 8
		}
495 8
		return $fields;
496
	}
497
498
}
499