Open Menu

Results & Renderers#^ TOP

After a student's solution has been verified, the result set is rendered to the console. The result set is made up of several individual results. Verification is deemed to have failed if any one of those results is a failure. Each result represents a different thing, for example, each check will likely inject a result in to the result set. The output verification will be a single result, the parsing of the file will be a single result, and so on.

Each Result class has an associated renderer, the renderers job is to take the information from the result and render in to the console.

Results are what Exercise Checks should return and inject. You will learn more about how to actually use the results in your checks in a later article.

The Result Set#^ TOP

The result set is an instance of PhpSchool\PhpWorkshop\ResultAggregator and results are added to it with add(ResultInterface $result). Note the interface PhpSchool\PhpWorkshop\Result\ResultInterface. Every result must implement this interface.

Success or Failure?#^ TOP

So, how do we know if a result is a success or failure? Well there are two other interfaces, extending from ResultInterface, which are:

  • PhpSchool\PhpWorkshop\Result\SuccessInterface
  • PhpSchool\PhpWorkshop\Result\FailureInterface

Both of these interfaces add no extra methods, they are purely for determining whether a result is considered a success or failure.

Result Interface#^ TOP

The interface for ResultInterface is very simple:

<?php

interface ResultInterface
{
    /**
     * @return string
     */
    public function getCheckName();
}

This method should just return the name of the check associated with this result. This is used when rendering the result to the console.

Implementations#^ TOP

There are default implementations for SuccessInterface & FailureInterface for you to use in your checks. If you need something more bespoke to render the failure of your check, you should create your own.

PhpSchool\PhpWorkshop\Result\Success

As you saw from the interface, the only required piece of information is the check name. So construction would look like the following.

<?php

use PhpSchool\PhpWorkshop\Result\Success;

$success = new Success('My Check');

If you are within a Check (eg $this refers to PhpSchool\PhpWorkshop\Check\CheckInterface) then you can use the static constructor fromCheck(CheckInterface $check) which just pulls the check name from the actual check for convenience.

<?php

namespace PhpSchool\PhpWorkshop\Check;

use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Result\Success;

class MyCheck implements SimpleCheckInterface
{
    ...snip

    public function check(ExerciseInterface $exercise, $fileName)
    {
        return Success::fromCheck($this);
    }
}

PhpSchool\PhpWorkshop\Result\Failure

The default implementation of FailureInterface needs one more piece of information other than the check name: the reason for the failure. Construction is fairly similar:

<?php

use PhpSchool\PhpWorkshop\Result\Failure;

//constructor
$failure = new Failure('My Check', 'Something went wrong!');

//static constructor
$failure = Failure::fromNameAndReason('My Check', 'Something went wrong!');

//static constructor with check
$myCheck = new MyCheck;
$failure = Failure::fromCheckAndReason($check, 'Something went wrong!');

Bundled Failure Implementations#^ TOP

There are a number of FailureInterface implementations bundled with the framework for some of the other checks:

  • PhpSchool\PhpWorkshop\Result\Cgi\GenericFailure
  • PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure
  • PhpSchool\PhpWorkshop\Result\Cli\GenericFailure
  • PhpSchool\PhpWorkshop\Result\Cli\RequestFailure
  • PhpSchool\PhpWorkshop\Result\FunctionRequirementsFailure
  • PhpSchool\PhpWorkshop\Result\ComparisonFailure

Custom Results#^ TOP

When you want to report information that is not simple a message, you will need to create your own result class. If you would build a check that verifies the contents of a database, you may want to provide a list of missing records as an array instead of just a message. You would then write a renderer that may render each row as a new line with a bullet point preceding it. Learn how to create your own checks in a later article.

Result Renderers#^ TOP

Each result renderer must implement the interface PhpSchool\PhpWorkshop\ResultRenderer\ResultRendererInterface which looks like below.

<?php

namespace PhpSchool\PhpWorkshop\ResultRenderer;

use PhpSchool\PhpWorkshop\Result\ResultInterface;

interface ResultRendererInterface
{

    /**
     * @param ResultsRenderer $renderer
     * @return string
     */
    public function render(ResultsRenderer $renderer);
}

PhpSchool\PhpWorkshop\ResultAggregator is rendered by PhpSchool\PhpWorkshop\ResultRenderer\ResultsRenderer. It loops each result, passing the result to PhpSchool\PhpWorkshop\Factory\ResultRendererFactory which returns the correct renderer. The render method is called and then the output of each is written to the console.

When each renderer is created, it is passed the ResultInterface as the first constructor argument. render is called with an instance of PhpSchool\PhpWorkshop\ResultRenderer\ResultsRenderer and it should return a string representation of the ResultInterface instance it was constructed with.

PhpSchool\PhpWorkshop\ResultRenderer\ResultsRenderer has some helper methods on it for rendering styling:

  • style($string, $colourOrStyle) - Use to style a string, eg. bold, green. It accepts an array of styles or one style as a string
  • lineBreak() - Use to render a line break, to separate content.
  • center() - Pad a string according to the terminal width so it displays in the center

Result Renderer Mappings#^ TOP

The workshop framework picks a result renderer based on the mappings in PhpSchool\PhpWorkshop\Factory\ResultRendererFactory.

The default mappings are:

Result Class Result Renderer Class
PhpSchool\PhpWorkshop\Result\Cgi\GenericFailure PhpSchool\PhpWorkshop\ResultRenderer\FailureRenderer
PhpSchool\PhpWorkshop\Result\Cgi\RequestFailure PhpSchool\PhpWorkshop\ResultRenderer\Cgi\RequestFailureRenderer
PhpSchool\PhpWorkshop\Result\Cli\GenericFailure PhpSchool\PhpWorkshop\ResultRenderer\FailureRenderer
PhpSchool\PhpWorkshop\Result\Cli\RequestFailure PhpSchool\PhpWorkshop\ResultRenderer\Cli\RequestFailureRenderer
PhpSchool\PhpWorkshop\Result\ComparisonFailure PhpSchool\PhpWorkshop\ResultRenderer\ComparisonFailureRenderer
PhpSchool\PhpWorkshop\Result\FunctionRequirementsFailure PhpSchool\PhpWorkshop\ResultRenderer\FunctionRequirementsFailureRenderer
PhpSchool\PhpWorkshop\Result\Failure PhpSchool\PhpWorkshop\ResultRenderer\FailureRenderer

If you create a new implementation of FailureInterface you will need to map it to an existing renderer, or most likely you will need to write a custom renderer, and map it to that.

Summary#^ TOP

The whole process may sound complicated, however, this is not true. To summarise, your check should return a result. The result should be mapped to a renderer. The results are rendered by the framework. It will pick the correct renderer based on the mapping.

Create a custom result#^ TOP

In the next set of articles we will learn about and build a check. Once the check is complete, we will build a custom result and result renderer for it, you can jump straight there if you want.