{"id":19017436,"url":"https://github.com/jakewhiteley/hodl","last_synced_at":"2025-04-23T02:51:10.601Z","repository":{"id":44623574,"uuid":"131292829","full_name":"jakewhiteley/hodl","owner":"jakewhiteley","description":"Super light PHP DI service container with autowiring capabilities to facilitate dependency injection/inversion of control in your project.","archived":false,"fork":false,"pushed_at":"2024-11-06T20:26:46.000Z","size":146,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-29T21:22:23.962Z","etag":null,"topics":["arrayaccess","dependency-injection","inversion","php7","resolving","service-manager"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jakewhiteley.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-27T12:20:26.000Z","updated_at":"2022-07-31T08:15:50.000Z","dependencies_parsed_at":"2022-09-11T15:20:48.489Z","dependency_job_id":null,"html_url":"https://github.com/jakewhiteley/hodl","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakewhiteley%2Fhodl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakewhiteley%2Fhodl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakewhiteley%2Fhodl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakewhiteley%2Fhodl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakewhiteley","download_url":"https://codeload.github.com/jakewhiteley/hodl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249318971,"owners_count":21250444,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arrayaccess","dependency-injection","inversion","php7","resolving","service-manager"],"created_at":"2024-11-08T19:47:00.727Z","updated_at":"2025-04-17T05:32:01.330Z","avatar_url":"https://github.com/jakewhiteley.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hodl DI\r\n\r\n[![Travis](https://img.shields.io/travis/jakewhiteley/hodl.svg?style=for-the-badge)](https://travis-ci.org/jakewhiteley/hodl) [![PHP from Packagist](https://img.shields.io/packagist/php-v/jakewhiteley/hodl.svg?style=for-the-badge)](https://packagist.org/packages/jakewhiteley/hodl)\r\n\r\n\r\n\r\nHodl provides full autowiring capabilities for Inversion of Control, but is simple enough to use as a standalone service container without any faff.\r\n\r\nIt's a simple `ArrayAccess` Service container which takes inspiration from both Laravel and Pimple (Symfony), but sits comfortably in the middle.\r\n\r\n## Basic Usage\r\n\r\nA service can be added to the container by providing the class name, and a definition for the service.\r\n\r\n```` php\r\n$hodl-\u003eadd('Some\\Namespace\\Foo', function() {\r\n\treturn new Foo();\r\n});\r\n````\r\n\r\nYou should always register a service using it's full class name. This is so that the autowiring can work and classes can have their dependencies injected with no fuss.\r\n\r\n### Retrieving a service\r\n\r\nAs simple as it gets:\r\n\r\n```` php\r\n$foo = $hodl-\u003eget('Some\\Namespace\\Foo');\r\n````\r\n\r\n### Checking if a service exists\r\n\r\nAs all services are referenced by the key you defined it with, you can use `has()` to check if that key has been defined previously:\r\n\r\n```` php\r\nuse Namespace\\Foo;\r\n\r\n// using the ::class shorthand\r\n$hodl-\u003eadd(Foo::class, function() {\r\n    return new Foo();\r\n});\r\n\r\n$hodl-\u003ehas(Foo::class); // true\r\n$hodl-\u003ehas('some\\other\\class'); // false\r\n````\r\n\r\n### Removing services\r\n\r\nAs the Container implements ArrayAccess you can use `unset()` or the `remove()` method to remove a class:\r\n\r\n```` php\r\n$hodl['foo'] = function(){\r\n    return new Foo();\r\n};\r\n\r\n$hodl-\u003ehas('foo'); // true\r\n\r\n$hodl-\u003eremove('foo');\r\n\r\n$hodl-\u003ehas('foo'); // false\r\n````\r\n\r\nRemoving a service will also remove any aliases or bound interfaces as well (more on that below).\r\n\r\n### ArrayAccess style\r\n\r\nAs Hodl implements `ArrayAccess`, you can achieve the above like this instead:\r\n\r\n````php\r\n// add\r\n$hodl['Some\\Namespace\\Foo'] = function(){\r\n    return new Foo();\r\n};\r\n\r\n// get\r\n$foo = $hodl['Some\\Namespace\\Foo'];\r\n\r\n// check\r\nif (isset($hodl['Some\\Namespace\\Foo')) // ...\r\n\r\n// remove\r\nunset($hodl['Some\\Namespace\\Foo']);\r\n````\r\n\r\n### Service definitions\r\n\r\nWhen adding a new service definition, the `callable` which returns the class is passed an instance of `Hodl`, which can be used for passing arguments derived from services already within the container. **Note** You should not pass services directly into the constructor of your service. For that we have the magic of [autowiring](#autowiring-resolving-dependencies).\r\n\r\n```` php\r\n$hodl-\u003eget('Baz', function($hodl) {\r\n\treturn new Baz($hodl-\u003eget('Some\\Namespace\\Foo')-\u003esomeProp);\r\n});\r\n````\r\n\r\n## Singletons\r\n\r\nThe above examplse will return a new instance of the service no matter when it is fetched.\r\nYou can also specify that the same instance should be returned each time by using the `addFactory()` method:\r\n\r\n```` php\r\n$hodl-\u003eaddSingleton(Bar::class, function() {\r\n\treturn new Bar();\r\n});\r\n````\r\n\r\n## Instances\r\nYou can also add a specific instance as a service. As this is already booted, `Hodl` can derrive the class name pretty easily so there is no need to supply that.\r\n\r\n````php\r\n$instance = new Foo\\Bar();\r\n$instance-\u003eprop = 'foobar';\r\n\r\n$hodl-\u003eaddInstance($instance);\r\n\r\n// ...\r\n\r\n$hodl-\u003eget('Foo\\Bar')-\u003eprop // equals 'foobar'\r\n````\r\n\r\n## Aliases\r\n\r\nSometimes it is tiresome typing in fully qualified class names to get access to a service. Luckily you can also define an **alias** to a service for quick retrieval:\r\n\r\n```` php\r\n// using the ::class shorthand\r\n$hodl-\u003eadd(Foo::class, function() {\r\n    return new Foo();\r\n});\r\n\r\n// Adda alias.\r\n$hodl-\u003ealias(Foo:class, 'myAlias');\r\n\r\n$hodl-\u003ehas(Foo::class); // true\r\n$hodl-\u003ehas('myAlias'); // true\r\n````\r\n\r\n#### Removing aliases\r\nIf at somepoint you need to remove an alias, or binding (see below) then you can use the `removeAlias($alias)` method.\r\n\r\n## Autowiring (resolving dependencies)\r\n\r\nAside as using it as a container for passing objects around, it can also be used to auotmatically resolve objects using the Reflection API and achieve Inversion of Control.\r\n\r\nConsider the following object:\r\n\r\n```` php\r\nnamespace Foo;\r\n\r\nclass Foo\r\n{\r\n    function __construct( Bar $bar )\r\n    {\r\n       \t$this-\u003ebar = $bar;\r\n    }\r\n}\r\n````\r\n\r\nWhen this object is created, it needs to be passed an instance of `Foo\\Bar` as the first argument.\r\n\r\nUsing the `resolve()` method this is super easy:\r\n\r\n```` php\r\n$foo = $hodl-\u003eresolve('Foo\\Foo');\r\n````\r\n\r\nThe object will be created an an instance of `Foo\\Bar` will be initialized and passed through automatically. \r\n\r\nThis works recursively so any dependencies of `Foo\\Bar` will be magically resolved as well.\r\n\r\n### Passing arguments\r\n\r\nThe resolve method also accepts a second argument, which is an array of extra parameters you want to pass to the object constructors.\r\n\r\nThe keys of this array must be the variable name of the parameter.\r\n\r\n```` php\r\n// using the Foo class in the previous example, but with a following constructor:\r\n// function __construct( Bar $bar, int $someInt, string $someString = 'string' ) { ...\r\n\r\n// by not passing someString as a key, the default of 'string' will be used\r\n$foo = $hodl-\u003eresolve('Foo\\Foo', [\r\n\t'someInt' =\u003e 42\r\n]);\r\n````\r\n\r\n### Resolving using services\r\n\r\nThe above examples have an empty container, so all services are injected as new generic instances of that class. But if a service exists within the container, that service will be used instead - allowing your specific instance or a persistent object to be passed to any object which needs it.\r\n\r\n````php\r\nclass Foo\r\n{\r\n\tpublic $var = 'foo';\r\n}\r\n\r\nclass Bar\r\n{\r\n\tpublic $foo;\r\n\t\r\n\tpublic function __construct(Foo $foo)\r\n\t{\r\n\t\t$this-\u003efoo = $foo;\r\n\t}\r\n}\r\n\r\n// Add Foo as a singleton\r\n$hodl-\u003eaddSingleton('Foo', function() {\r\n\treturn new Foo();\r\n});\r\n\r\n$hodl['Foo']-\u003evar = 'changed!';\r\n\r\n\r\n$var = $hodl-\u003eresolve('Bar')-\u003efoo-\u003evar; // equals 'changed!'\r\n````\r\n\r\nDon't be afraid to use `resolve()` when adding a service definition to the container either!\r\n\r\n```` php\r\n$hodl-\u003eadd('Bar', function($hodl) {\r\n\treturn $hodl-\u003eresolve('Bar'); // All of Bar's dependencies will be injected as soon as it is fetched\r\n});\r\n\r\n````\r\n\r\n### Binding implementations to interfaces\r\n\r\nA really useful feature when using the autowiring functionality is to be able to specify in a constructor an interface, and have Hodl deal with passing the correct implementation to the resolved class.\r\n\r\nConsider the following:\r\n\r\n```` php\r\n// Basic interface\r\ninterface HelloWorld\r\n{\r\n\tpublic function output();\r\n}\r\n\r\n// Service\r\nclass NeedsResolving\r\n{\r\n\tpublic function __construct(HelloWorld $writer)\r\n\t{\r\n\t\t$this-\u003ewriter = $writer;\r\n\t}\r\n\t\r\n\tpublic function output()\r\n\t{\r\n\t\t$this-\u003ewriter-\u003eoutput();\r\n\t}\r\n}\r\n````\r\n\r\nWe know the `NeedsResolving` class needs some kind of `HelloWorld` implementation to actually work. We can let Hodl know which one using the `bind()` method:\r\n\r\n```` php\r\nclass MyPrinter implements HelloWorld\r\n{\r\n\tpublic function output()\r\n\t{\r\n\t\techo 'Hello world!';\r\n\t}\r\n}\r\n\r\n$hodl-\u003ebind(MyPrinter::class, HellowWorld::class);\r\n\r\n// Correctly gets an instance of MyPrinter\r\n$foo = $hodl-\u003eresolve(NeedsResolving::class);\r\n\r\n$foo-\u003eoutput(); // Outputs 'Hello world!'\r\n````\r\n\r\n#### Removing bindings\r\nAs under the hood `bind()` as an alias for `alias()`, the `removeAlias($interface)` method will remove a binding. Useful if for whatever reason you had to hot-swap an implementation out for another.\r\n\r\n## Resolving methods\r\nThe `resolveMethod($class, $methodName, $args)` method allows autowiring of class members the same way that `resolve()` works on classes.\r\n\r\n````php\r\n$hodl-\u003eresolveMethod(Foo::class, 'someMethod');\r\n````\r\n\r\n`resolveMethod` will call the supplied method, recursively inject dependencies and allow you to pass extra non-object parameters as per the `resolve` examples above. This works on static methods as well as public ones.\r\n\r\n### Resolving instance methods\r\n\r\nThe example above shows `someMethod` being execcuted and returned on a new instance of `Foo`, but you can also pass a specific instance instead of the class name:\r\n\r\n````php\r\n$foo = new Foo();\r\n$return = $hodl-\u003eresolveMethod($foo, 'someMethod', ['amount_of_awesome' =\u003e 100]);\r\n````\r\n\r\nBoth `resolve` and `resolveMethod` could therefore be used together to create a new fully resolved object and execute a method.\r\n\r\n````php\r\nclass Bar\r\n{\r\n\tpublic $foo;\r\n\t\r\n\tpublic function __construct(Foo $foo)\r\n\t{\r\n\t\t$this-\u003efoo = $foo;\r\n\t}\r\n\t\r\n\tpublic function methodName(Foo\\Baz $baz)\r\n\t{\r\n\t\treturn $this-\u003efoo-\u003evar * $baz-\u003evar;\r\n\t}\r\n}\r\n\r\n// Fully resolves methodName and returns an instance of Foo\\Baz\r\n$resolvedBaz = $hodl-\u003eresolveMethod(\r\n\t$hodl-\u003eresolve('Bar'),\r\n\t'methodName'\r\n);\r\n````\r\n\r\n## Conclusion\r\n\r\nBy adding services to Hodl, your code can achieve complete inversion of control and manage classes application-wide with no need for a single `new` keyword or singleton in sight.\r\n\r\n## Contributing\r\n\r\nIf you have any improvements, bugs, or feature requests; feel free to open up an issue or PR.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakewhiteley%2Fhodl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakewhiteley%2Fhodl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakewhiteley%2Fhodl/lists"}