Download the PHP package jayankaghosh/om without Composer
On this page you can find all versions of the php package jayankaghosh/om. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download jayankaghosh/om
More information about jayankaghosh/om
Files in jayankaghosh/om
Package om
Short Description The Advanced Object Manager with dependency injection, plugins, preferences, and much more... NO MORE new Obj()!
License OSL-3.0 AFL-3.0
Informations about the package om
OM (ObjectManager)
The Advanced Object Manager with dependency injection, plugins, preferences, and much more... NO MORE new Obj()!
No more cluttering your project with unnecessary object creation. Now inject whatever object you want in your class in the constructor, and let OM take care of it. It's that simple!
This project was heavily inspired by Magento 2's use of Object Manager, and dependency injection.
Installation
composer require jayankaghosh/om
Features
OM (ObjectManager) helps developers organize and maintain their code easily with powerful features like
- Dependency Injection
- Constructor parameter injection (Magento-like)
- Plugins (Maento-like,)
- Preferences, (Magento-like)
- Parameter injection (Magento-like)
How To Use
1. Simple usage
// Get object manager instance from object manager factory Like this
$objectManagerFactory = new \Om\OmFactory();
$objectManager = $objectManagerFactory->getInstance();
// And then create the initial object of your project
// using $objectManager instead of "new"
// $objectManager::get method takes 2 parameters
// 1. the class name as string
// 2. an associative array of constructor arguments of the class
$app = $objectManager->get(\My\Project\App::class, []);
$app->run();
// After that we are open to a new world of possibilities
// that will change the way programming was done forever!!!
// Too much? Okay too much 😐
2. Constructor arguments
Using OM we don't have to worry about constructor arguments. If the argument type is an object, it'll automatically be passed (as a singleton) to the constructor
class A {...}
class B {...}
class C {
public function __construct(A $a, B $b) {...}
...
}
// $c = new C(); // Will throw error. Since C required A and B to run
$c = $objectManager->get(C::class); // Works like a charm!
3. The DI config
Remember when we created an object of OmFactory?
$objectManagerFactory = new \Om\OmFactory();
Well it turns out we can pass a config object to it as well as a parameter. This config holds information about how the ObjectManager should handle the dependency injection. We can pass configurations regarding preferences, type arguments, and plugins using the config object.
The config is an object of Om\DiConfig\Config
. We can either use the functions
provided inside the class to make the config. Or we can use an associative
array to make it. In this example, we'll use the latter
$config = \Om\DiConfig\Config::fromArray([
'preferences' => [
[
'for' => 'MyCalculator',
'type' => Calculator::class
]
],
'types' => [
[
'name' => Calculator::class,
'arguments' => [
[
'name' => 'logger',
'type' => 'object',
'value' => \Monolog\Logger::class
]
],
'plugins' => [
[
'name' => 'divide-by-zero-check',
'type' => CalculatorPlugin::class
]
]
]
]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();
You can also create the DI Config from a XML string using the
\Om\DiConfig\Config::fromXml
method, like this,
$config = Om\DiConfig\Config::fromXml(
\file_get_contents(__DIR__ . '/di.xml')
);
Here is a sample of how the di.xml
file should look like
<?xml version="1.0" encoding="utf-8" ?>
<config>
<preference for="MyCalculator" type="Calculator" />
<type name="Calculator">
<arguments>
<argument name="logger" xsi:type="object">Monolog\Logger</argument>
</arguments>
<plugin name="divide-by-zero-check" type="CalculatorPlugin" />
</type>
</config>
4. Preferences
Preferences are a way of making aliases or proxies for objects. Keeping 'B' as a preference to 'A' is like saying "Whenever I want to create an object of class 'A', please instead create an object of class 'B' instead"
Preferences are defined using the DI Config
$config = \Om\DiConfig\Config::fromArray([
'preferences' => [
[
'for' => 'MyCalculatorAlias',
'type' => Calculator::class
]
]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();
// There is not class with the name 'MyCalculatorAlias',
// but still it doesn't throw any errors because we had already
// mentioned that Calculator::class is the preference for 'MyCalculatorAlias'
$calculator = $objectManager->get('MyCalculatorAlias');
echo get_class($calculator); // Calculator::class
5. Constructor arguments
Let's say we have a class A which has 2 constructor arguments
class A {
public function __constructor(
Logger $logger,
string $message
) {
var_dump($message);
}
}
Now if we want to create an object of class A using object manager
$objectManager->get('A');
It will throw an error saying too few arguments passed. But why? I thought OM took care of all constructor arguments :(
Well, not exactly. OM can only pass parameters which have type as object. If the type is something else (in this case $message is a string). Hence we would need to pass the argument "message" ourselves. How?
$a = $objectManager->get('A', ['message' => 'Hello OM']);
By passing it as a part of an associative array in the second parameter of get
.
We can pass as many constructor arguments as we want like this. We can even
pass arguments of type object, and it'll override the default argument.
But isn't it frustrating having to pass the value of message
every time
we want to create an object of A? Hence there is also another way of
passing global arguments to classes. That is through the DI Config
$config = \Om\DiConfig\Config::fromArray([
'types' => [
[
'name' => 'A',
'arguments' => [
[
'name' => 'message',
'type' => 'string',
'value' => 'Hello Global OM'
]
]
]
]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();
$objectManager->get('A'); // doesn't throw error anymore
6. Singletons
By default all objects created using the \Om\ObjectManager\ObjectManager::get
methods are singletons. Which means, if an object of the class is already created, it'll
return that same object, or create a new one if it's the first time.
There might be some times when we don't want this behaviour and want a new Object
to be created. In cases like these, we use the \Om\ObjectManager\ObjectManager::create
method.
$aSingleton = $objectManager->get('A'); // singleton
$aNew = $objectManager->create('A'); // new object
7. Generated classes
In order to provide certain supercalifragilisticexpialidocious features, OM might want to generate a few files in your system (not a worm. pinky swear!). For that we need to provide the absolute path of a directory where OM can generate the files.
$config = [...];
$writableDirectoryPath = __DIR__ . '/var/generated/';
$objectManagerFactory = new \Om\OmFactory($config, $writableDirectoryPath);
$objectManager = $objectManagerFactory->getInstance();
8. Factory Pattern
Requires Generated Classes
Once we gave OM the writable directory to generate files in, we can use the
factory pattern of OM to generate new classes instead of singleton.
For that simply suffix the word Factory
after the class name.
class A {
public function __construct(
\My\Useful\HelperClass $helper,
\My\Useful\HelperClassFactory $helperFactory
) {
$this->helper = $helper; // singleton
$this->helper = $helperFactory->create(); // new object
}
}
9. Plugins
Requires Generated Classes
Plugins are an elegant way of achieving complete extendability of an application. Plugins give us the ability to intercept the flow of any public method to do something before, after, or around the method execution.
Plugins are also defined in the DI Config
$config = \Om\DiConfig\Config::fromArray([
'types' => [
[
'name' => 'Calculator',
'plugins' => [
[
'name' => 'divide-by-zero-check',
'type' => 'CalculatorPlugin'
]
]
]
]
]);
$objectManagerFactory = new \Om\OmFactory($config);
$objectManager = $objectManagerFactory->getInstance();
class Calculator {
public function divide($a, $b)
{
return $a / $b;
}
}
class CalculatorPlugin {
public function beforeDivide(Calculator $subject, $a, $b)
{
if ($b === 0) {
$b = 1;
echo '$b changed to 1 because it was 0';
}
return [$a, $b];
}
}
$calculator = $objectManager->get('Calculator');
As you can see, we modified the behaviour of the divide
method in the
Calculator
class without even touching the Calculator class. How cool
is that?!
Here's a handy guide for plugins
Before
Used to modify parameters. Prefix before
to the method name
in the plugin. For example, beforeDivide
.
Arguments
- Subject (an object of the class it is a plugin for)
- Parameter 1 of the actual method
- Parameter 2 of the actual method
- ...
After
Used to modify the return value. Prefix after
to the method name
in the plugin. For example, afterDivide
.
Arguments
- Subject (an object of the class it is a plugin for)
- The return value of the actual function
Around
Used to change the entire flow of the method. Here the method itself
is not executed automatically. Rather it is converted into a callable and
passed to the plugin as an argument. Prefix around
to the method name
in the plugin. For example, aroundDivide
.
Arguments
- Subject (an object of the class it is a plugin for)
- callable (a callable method pointing to the actual method)
- Parameter 1 of the actual method
- Parameter 2 of the actual method
- ...