FireDI Release 2.1.0

FireDI 2.1.0 was just released. Here’s what’s new:

  • Implemented PSR-11 Container Interface. Here’s what that means. FireDI is compliant with an agreed upon standard for how you should implement a container of objects.
  • Updated code base to follow PSR-2 Coding Standards. In a previous work situation, we followed Zend 1.* standards for coding. I adopted many of those not working on a modern framework. I had to update the code base to be compliant with PSR-2.
  • Implemented Di::has(). As part of the PSR-11 standard, I had to add a method to determine if this particular container “has” an object in it. So what that means under the hood is can this container resolve what you are asking for.
  • Updated the Di::set() interface to take in a callable and updated the logic of dependency resolution to invoke the callable as part of resolving the dependency. That means you can set factories to return objects for specific dependencies.
  • Included an MIT license! This way you can trust that the code you are using cannot be taken back from you.

FireDI – Advanced Use Cases

FireDI can do a lot more than just resolve your dependencies for you. This tutorial, we are going to cover other scenarios where FireDI can become helpful.

For Mocking In Test Cases

When working on testing your code, you will find that from time to time, you will need to mock a dependency for the class you are testing.

<?php
class TheClassImTesting {
    public function __construct(MyDependentClass $myDependentClass) {}
}

Now to mock MyDependentClass!

<?php
$fireDi = new \UA1Labs\Fire\Di();
$myDependentClassMock = Mock::get('MyDependentClass');
$fireDi->set('MyDependentClass', $myDependentClassMock);

$theClassImTesting = $fireDi->get('TheClassImTesting');

For Injecting New Objects Every Time

By default, FireDI will cache all objects (including object dependencies) within the container object cache. This is so the next time you need the object, it is already ready for you and is just simply returns. From time to time, however, you may need for a specific object to return a new instance every time it is needed.

<?php
// to prevent an object from caching, you may pass a factory into the Di::set() method.

$fireDi = new \UA1Labs\Fire\Di();
$fireDi->set('MyNewObjectEveryTime', function() {
    new MyNewObjectEveryTime();
});

For Interface Dependencies

In many cases, you might have a class that has a constructor dependency on an interface.

<?php
inteface MyInterface {}

class MyClass implements MyInterface {}

class InterfaceDependentClass 
{
    public function __construct(MyInterface $MyInterface) {}
}

In the example above, you will notice that InterfaceDependentClass depends on an object that implements MyInterface. If you want to resolve this, you would need to new MyClass() and pass the resulting instance object into the InterfaceDependentClass constructor. Or you can simply have FireDI do that for you!

<?php
$fireDi = new \UA1Labs\Fire\Di();
$myClass = $fireDi->get('MyClass');
$fireDi->set('MyInterface', $myClass);

$interfaceDependentClass = $fireDi->get('InterfaceDependentClass');

FireTest – API Documentation

UA1Labs\Fire\Test\Suite

__construct($dir, $fileExt = ‘.TestCase.php’)

The class constructor.

  • @param string $dir The directory you would like to run this test suite for
  • @param string $fileExt The file extension that will indicate FireTest cases
  • @return void
<?php
$testSuite = new UA1Labs\Fire\Test\Suite(__DIR__ . '/.', '.TestCase.php');

run()

The run logic used to run the suite of test cases and tests.

  • @return void
<?php
$testSuite = new UA1Labs\Fire\Test\Suite(__DIR__ . '/.', '.TestCase.php');
$testSuite->run();

[static] log($text)

This method is responsible for logging messages to the command line. It is a static method and will allow you to log without having to have an instance of the class.

  • @param string $text The text you would like to have logged.
  • @return void
<?php
// calling it statically
UA1Labs\Fire\Test\Suite::log('Log message here');

// calling it from an instance object
$testSuite = new UA1Labs\Fire\Test\Suite('/.', '.TestCase.php');
$testSuite->log('Log message here');

[abstract] UA1Labs\Fire\Test\TestCase

should($statement)

Method used to set the current test’s should statement. This method call should be paired with ::assert().

  • @param string $statement The statement you want to test against
  • @return UA1Labs\Fire\Test\TestCase
<?php
use UA1Labs\Fire\Test\TestCase;

class MyTestCase extends TestCase 
{
    testMyFirstTest()
    {
        $this->should('Return true when called');
        $this->assert(true);
    }
} 

assert($true)

Method used to determine if a test passes or fails.

  • @param boolean $true The statement you want to test
  • @return UA1Labs\Fire\Test\TestCase
<?php
use UA1Labs\Fire\Test\TestCase;

class MyTestCase extends TestCase 
{
    testMyFirstTest()
    {
        $this->should('Return true when called');
        $this->assert(true);
    }
} 

setUp()

A method that is invoked when the when the testcase is first intialized.

  • @return void
<?php
use UA1Labs\Fire\Test\TestCase;

class MyTestCase extends TestCase 
{
    setUp()
    {
        // setup logic here
    }
} 

beforeEach()

A method that is invoked before each test method is invoked.

  • @return void
<?php
use UA1Labs\Fire\Test\TestCase;

class MyTestCase extends TestCase 
{
    beforeEach()
    {
        // before each logic here
    }
}

afterEach()

A method that is invoked after each test method is invoked.

  • @return void
<?php
use UA1Labs\Fire\Test\TestCase;

class MyTestCase extends TestCase 
{
    afterEach()
    {
        // after each logic here
    }
}

tearDown()

A method that is invoked when the test case is finish running all test methods.

  • @return void
<?php
use UA1Labs\Fire\Test\TestCase;

class MyTestCase extends TestCase 
{
    tearDown()
    {
        // tear down logic here
    }
}

FireTest – Getting Started

Getting started with FireTest is easy. Our goal with FireTest was to make it as simple as possible to integrate it into your project and start writing tests for the code you’ve written.

Install FireTest

composer require ua1-labs/firetest

Create Your First Test

Creating test cases in FireTest is as simple as adding a TestCase class and running the FireTest runner.php script. Your first step into creating FireTest Test Cases are to find the class you’d like to test. Create a file in the same directory and call it {classname}.TestCase.php. Copy and paste the boiler plate code into the newly created Test Case file.

<?php

use UA1Labs\Fire\Test\TestCase;

class MyTestCase extends TestCase
{
    //my test suite logic
}

Now you may use event hook methods to bootstrap your tests.

Event Hook Methods In Test Cases

With every test that you write, you will need to setup the environment to setup your test environment to run your code in. For example, almost for every test you write, you will first need to obtain a new instance of the object you are testing. Event Hook Methods are going to give you the ability to do this.

  • setUp() A method that is invoked when the when the testcase is first intialized.
  • beforeEach() A method that is invoked before each test method is invoked.
  • afterEach() A method that is invoked after each test method is invoked.
  • tearDown() A method that is invoked when the test case is finish running all test methods.

Test Methods

Test methods are another kind of event hook. The test method is where you will actually test your code. FireTest will loop through each of these test methods and execute the code. These methods are ran between the beforeEach() and afterEach() method.

Test methods begin with the work “test.” This is how FireTest dictates if it should be running the method during the testing phase of the life cycle. You can name the method whatever you want, however, it will need to begin with the work test (all lowercase).

public function testMyCode1()
{
    // my test code here
}

public function testMyCode2()
{
    // my test code here
}

Testing Your Code

Once you have mastered the event hooks and have setup your testing environment, you will then need to be able to tell what test are you running and how you will prove that it was successful. that is where the should() and assert() methods come in. To write tests start out by telling what this test is going to prove using a should statement. Think of testing like saying “this test should…” and fill in the blank.

$this->should('Return a number when I pass in a number.');
$result = my_number_converter(4);
$this->assert(is_number($result));

Now Run FireTest Runner.php

FireTest has a built in test runner so that you don’t have to bootstrap your own test suites together. Simply run the following command from the root directory of your project and watch FireTest do its thing.

php vendor/ua1-labs/firetest/scripts/runner.php

Test Results

Test results will then provide you with feedback whether your tests have passed or not.

ua1-labs:firedi josjohns$ php vendor/ua1-labs/firetest/scripts/runner.php
FireTest: *************************************************************
FireTest: ███████╗██╗██████╗ ███████╗████████╗███████╗███████╗████████╗
FireTest: ██╔════╝██║██╔══██╗██╔════╝╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝
FireTest: █████╗  ██║██████╔╝█████╗     ██║   █████╗  ███████╗   ██║   
FireTest: ██╔══╝  ██║██╔══██╗██╔══╝     ██║   ██╔══╝  ╚════██║   ██║   
FireTest: ██║     ██║██║  ██║███████╗   ██║   ███████╗███████║   ██║   
FireTest: ╚═╝     ╚═╝╚═╝  ╚═╝╚══════╝   ╚═╝   ╚══════╝╚══════╝   ╚═╝   
FireTest: *************************************************************
FireTest: [STARTING] Test suite is located at "/home/projects/firedi"
FireTest: [STARTING] Finding all files with the extension ".TestCase.php"
FireTest: [LOADING] Test file "/home/projects/firedi/UA1Labs/Fire/Di/Graph.TestCase.php"
FireTest: [LOADING] Test class "Test\UA1Labs\Fire\Di\GraphTestCase"
FireTest: [LOADING] Test file "/home/projects/firedi/UA1Labs/Fire/Di/ClassDefinition.TestCase.php"
FireTest: [LOADING] Test class "Test\UA1Labs\Fire\Di\ClassDefinitionTestCase"
FireTest: [LOADING] Test file "/home/projects/firedi/UA1Labs/Fire/Di.TestCase.php"
FireTest: [LOADING] Test class "Test\UA1Labs\Fire\DiTestCase"
FireTest: [LOADING] Test file "/home/projects/firedi/vendor/ua1-labs/firetest/UA1Labs/Fire/Test/TestCase.TestCase.php"
FireTest: [LOADING] Test class "Test\UA1Labs\Fire\Test\TestCaseTestCase"
FireTest: [LOADING] Test class "Test\UA1Labs\Fire\Test\TestCaseMock"
FireTest: [LOADING] Test file "/home/projects/firedi/vendor/ua1-labs/firetest/UA1Labs/Fire/Test/Suite.TestCase.php"
FireTest: [LOADING] Test class "Test\UA1Labs\Fire\Test\SuiteTestCase"
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\GraphTestCase::testConstructor()
FireTest: [PASSED] Not throw an exception when the class is constructed
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\GraphTestCase::testAddResource()
FireTest: [PASSED] Add a resource to the resource graph.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\GraphTestCase::testAddDependencies()
FireTest: [PASSED] Add dependencies to a resource.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\GraphTestCase::testAddDependency()
FireTest: [PASSED] Add a dependency to a resource.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\GraphTestCase::testRunDependencyCheck()
FireTest: [PASSED] Return an error code of "1" (Resourece Not Found).
FireTest: [PASSED] Contain which resource was missing.
FireTest: [PASSED] Return and error code "2" (Circular Dependnecy).
FireTest: [PASSED] Contain the resource of which caused the circular dependency.
FireTest: [RESULT] (Passed: 4, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\GraphTestCase::testGetDependencyResolveOrder()
FireTest: [PASSED] Resolve dependencies in the order they need to be resolved.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\GraphTestCase::testResetDependencyCheck()
FireTest: [PASSED] Reset a dependency check.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Di\ClassDefinitionTestCase::testConstructor()
FireTest: [PASSED] Not throw an exception when the class is constructed
FireTest: [PASSED] Have set a serviceId of "Test\UA1Labs\Fire\Di\MyTestClass".
FireTest: [PASSED] Have set a classDef as a ReflectionClass object.
FireTest: [PASSED] Have set a dependency of "Test\UA1Labs\Fire\Di\MyDependentClass"
FireTest: [RESULT] (Passed: 4, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\DiTestCase::testConstructor()
FireTest: [PASSED] The constructor should not throw an execption and be an instance of Di.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\DiTestCase::testPutObject()
FireTest: [PASSED] Put an object in the cache without an exception.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\DiTestCase::testGetObject()
FireTest: [PASSED] Resolve all dependencies for TestClassA and return the TestClassA object.
FireTest: [PASSED] Have placed TestClassA, TestClassB, and TestClassC within the object cache.
FireTest: [PASSED] Resolve all dependencies for TestClassD and return it.
FireTest: [PASSED] Have set ::A as TestClassA on TestClassD object.
FireTest: [PASSED] Have set ::B as TestClassB on TestClassD object.
FireTest: [PASSED] Have set ::C as TestClassC on TestClassB.
FireTest: [PASSED] Throw an exception if a the class you are trying to get does not exists.
FireTest: [PASSED] Throw an exception if a circular dependency is detected.
FireTest: [RESULT] (Passed: 8, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\DiTestCase::testGetWithObject()
FireTest: [PASSED] Return a TestClassB object.
FireTest: [PASSED] Prove that TestClassB has a $C variable that is TestClassC.
FireTest: [PASSED] Prove that the object cache does not contain a TestClassB
FireTest: [PASSED] Throw and exception if a the class you are trying to get does not exists.
FireTest: [RESULT] (Passed: 4, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\DiTestCase::testGetObjectCache()
FireTest: [PASSED] Return an object cache array with a key "TestObject".
FireTest: [PASSED] Return an object cache with the object we put into it.
FireTest: [RESULT] (Passed: 2, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\DiTestCase::testClearObjectCache()
FireTest: [PASSED] Remove all objects from the object cache
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Test\TestCaseTestCase::testConstructor()
FireTest: [PASSED] Return an instance object of the TestCase object without throwing an exception.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: [RUNNING] Test\UA1Labs\Fire\Test\SuiteTestCase::testConstructor()
FireTest: [PASSED] Return an instance object of the Suite object without throwing an exception.
FireTest: [RESULT] (Passed: 1, Failed: 0)
FireTest: ***********************************************************
FireTest: ███████╗██╗   ██╗ ██████╗ ██████╗███████╗███████╗███████╗
FireTest: ██╔════╝██║   ██║██╔════╝██╔════╝██╔════╝██╔════╝██╔════╝
FireTest: ███████╗██║   ██║██║     ██║     █████╗  ███████╗███████╗
FireTest: ╚════██║██║   ██║██║     ██║     ██╔══╝  ╚════██║╚════██║
FireTest: ███████║╚██████╔╝╚██████╗╚██████╗███████╗███████║███████║
FireTest: ╚══════╝ ╚═════╝  ╚═════╝ ╚═════╝╚══════╝╚══════╝╚══════╝
FireTest: ***********************************************************
FireTest: [FINAL] (Passed: 33, Failed: 0)

Add The Test Command To Your Composer.json

To make things a lot simpler for you to run all of your test cases, simply add the test runner to the script section of your project’s composer.json.

"scripts": {
    "test": "php vendor/ua1-labs/firetest/scripts/runner.php"
}

The History of FireTest

UA1 Labs has officially released version 2.0.0 of FireTest. But why are we so focused on building yet another testing framework when there are so many already out there?

What Exactly is FireTest?

FireTest is a PHP test automation framework that allows you to automate tests as you write code. It was written in PHP for testing PHP code.

Why Did You Write Your Own Test Automation Framework?

For us the answer was simple, we looked into the many testing frameworks written for PHP and weren’t quite satisfied with what we were seeing. We wanted a light weight and flexible framework that wouldn’t get in the way of our workflow. We wanted to ensure that when we ran our automated tests that the results were valuable to us in catching bugs.

Most of the frameworks we looked at were so focused on code coverage, unit testing, CAP score. They missed out on the important thing. Does the code I’m writing still do what I want it to? And if we find a bug, how can I write a test so that the same bug doesn’t happen again. All other frameworks we worked with didn’t provide the flexibility we were looking for.

So we invented FireTest! We admit, it seems primitive in nature compared to the other popular testing frameworks, but we feel like we needed to get back to basics. Stop bloating our testing to reduce the complexity of the code you are writing. Instead we wanted to focus on simplicity and ease of use.

FireDI – API Documentation

UA1Labs\Fire\Di

get($classname)

Attempts to retrieve an instance object of the given classname by resolving its dependencies and creating an instance of the object.

  • @param $classname string The class you would like to instantiate
  • @throws \UA1Labs\Fire\Di\NotFoundException If the class cannot be resolved
  • @return object The instantiated object based on the $classname
<?php
$fireDi = new UA1Labs\Fire\Di();
$classA = $fireDi->get('ClassA');

set($classname, $entry)

Puts an object into the object cache that is used to resolve dependencies.

  • @param string $classname The classname the instance object should resolve for
  • @param object|callable $entry The object or callable you’d like to place in the object cache
  • @return void
<?php
$fireDi = new UA1Labs\Fire\Di();
$testMock = new Test\Mock();
$fireDi->set('UA1Labs\MyClass', $testMock);

$fireDi->set('MyCallable', function() {
    return new MyClass('awesome');
});

has($classname)

Determines if the class can be resolved.

  • @param string $classname The classname of the instance you would like to resolve
  • @return boolean

getWith($classname, $dependencies)

Returns an instance object for the given classname and dependencies. You may want to use this over Di::get() if you have a need to resolve your own dependencies.

  • @param string $classname The classname you want the instance object of
  • @param array<mixed> $dependencies The dependencies that the class is expecting
  • @return object The instance object of the class you asked for
<?php
$fireDi = new UA1Labs\Fire\Di();
$dependencies = [];
$dependencies[] = 'Toyota';
$dependencies[] = 'Corolla';
$toyotaCorolla = $fireDi->getWith('Framework\Model\Car', $dependencies);

clearObjectCache()

Used to clear the cache of any objects that have been instantiated. FireDI maintains an object cache for the purpose of efficiency. Whenever you use Di::get(), the object you obtained, and all of its dependencies are maintained within the object cache. If you ask for the object again, you will retrieve the one that has already been instantiated.

  • @return void
<?php
$fireDi = new UA1Labs\Fire\Di();
$fireDi->clearObjectCache();

FireDI – Getting Started

In this tutorial, you will learn how to get started with FireDI! A quick recap, FireDI is a PHP dependency injection library that makes it simple to help you manage dependencies within your classes. If you haven’t quite decided whether you should be using FireDI or not, checkout our page about When Should I Use Dependency Injection?.

Installing FireDI

Installing FireDI is so easy by simply using composer.

composer require ua1-labs/firedi

That’s It! It’s included in your project and ready to go!

Using FireDI

FireDI is simple to use and has a simple API.

$fireDi = new UA1Labs\Fire\Di();
$myClass = $fireDi->get('myClass');

FireDI – When Should I Use Dependency Injection?

UA1 Labs has been at this for a while and has made many wonder tools for developers to help keep them focused on the development they are doing. When we interact with other developers and talk with them about dependency injection, there is a bit of confusion on when they should be using it. This post will help answer some of the reasons you may want to consider using dependency injection within you PHP projects.

First off, what is dependency injection anyway?

Well dependency injection is a design pattern that goes way back in the history of software engineering. Its been around and while and has had many different implementations. In its simplest form, dependency injection is a fancy term for making sure that the class you are about to instantiate has the dependencies instantiated for you already.

<?php
class A
{
    public function __construct($dependency)
    {
        $this->dep = $dependency;
    }
}

$dependency = new Dependency();
$classA = new A($dependency);

In the example above class A needs some sort of dependency resolved before it can be instantiated. Dependency injection is that simple! You may have noticed though that this can be a pain in the butt to have to always be resolving dependencies every time you want to get an instance of class A.

<?php
$classA = $firedi->get('A');

In the example above, you will notice that I’m getting the same class A instantiated, but I do not need to first resolve the dependency. FireDI is instead handling that behind the scenes for me! That is what a typical dependency injection library will do for you.

So when do I need to use a dependency injection library?

The answer is simple. When you find it helpful! I know, I know…that isn’t very helpful for me to tell you. You are looking for the solid, “…this is when you should use a dependency injection library.” Why don’t I just tell you the benefits of using one. If these benefits add value to your project, then I would say use a dependency injection library!

Benefit 1. Off the bat, you can see that you will be writing a lot less code for getting instantiated objects. The class A example I shared above, is only the tip of the iceberg with regards to dependency resolution. The more complex a project gets, the more dependencies you will have to resolve in order to get the object you are trying to access. A dependency injection library will track all of these dependencies for you and resolve them as they need to be resolved.

Benefit 2. The second benefit to using a dependency injection library has to do with automated testing. Whether that is unit testing or integrated testing. Having the ability to switch out class definitions for mock definitions makes your life in testing so much easier.

FireDI Migration Started

As announced earlier this week, Ulfberht (a dependency injection container written for PHP) has started to migrate to UA1 Labs. So far, we have moved the repo from my personal GitHub account to UA1 Labs and started the refactor/rebranding we are doing to fit it into our vision for our Fire suite of development tools.

Our very first pull request for FireDI is starting the refactoring. https://github.com/ua1-labs/firedi/pull/2. I wanted to throw this PR out there just in case there were others who wanted to review what we are doing. Please feel free to comment on the PR!

Also note that we have started a project page for FireDI. We will be posting more about how to use it, API reference, etc, there at the project page. That project page can be found https://ua1.us/projects/firedi/

Moving Ulfberht to UA1 Labs and Renaming It To FireDI

Originally started as a learning project back in 2013, I was interested in creating a Dependency Injection Container for PHP. At the time, AngularJS was king and had a really awesome way of resolving dependencies. Originally this project had been named “z20”. I have since, renamed the project and refactored it several times. It has become time to start this process again. But this time, it will be the final time we are moving this code base as UA1 Labs will be maintaining and supporting this library for now on.

Continue reading “Moving Ulfberht to UA1 Labs and Renaming It To FireDI”