Ready for the next step?
Use the PHP School workshop documentation to build you own workshop and help teach others PHP!
Open Menu
As we have seen in the previous articles, you can build your own custom checks. Checks can be used in as many exercises as you want - you could even create a package which consists of common checks you might want to use in your workshops. The check we built in the Creating Simple Checks article is a good example of a reusable check; you might want to include this in all your exercises.
But what if you want to perform a check that you don't think you will use again? You don't really want to create a class to encompass this logic when it is only to be used in one exercise.
Enter the Self Checking feature!
The Self Checking feature allows your exercise to implement an interface which contains one method -
check()
during the verification process of the student's solution, your method will be called and
passed the input arguments passed to our workshop, which will contain the file name of the student's solution.
In this method you can do whatever you want: parse the code
into an AST using the PhpParser\Parser
service, lint it using a third party tool or whatever else you
can think of.
To give you an example of how you might use it - we use it here in Learn You PHP! to check that a submission contains an include
/require
statement as the exercise is teaching how to separate code over multiple files. We want to enforce the student to include a separate file.
Creating a self checking exercise requires implementing the interface
PhpSchool\PhpWorkshop\ExerciseCheck\SelfCheck
, adding your check logic and returning a
result. Depending on whether you want a success or a failure
to be recorded it will be an instance of PhpSchool\PhpWorkshop\Result\SuccessInterface
or
\PhpSchool\PhpWorkshop\Result\FailureInterface
.
The interface looks like this:
<?php
namespace PhpSchool\PhpWorkshop\ExerciseCheck;
use PhpSchool\PhpWorkshop\Result\ResultInterface;
use PhpSchool\PhpWorkshop\Input\Input;
interface SelfCheck
{
/**
* The method is passed the absolute file path to the student's solution and should return a result
* object which indicates the success or not of the check.
*
* @param Input $input The command line arguments passed to the command.
* @return ResultInterface The result of the check.
*/
public function check(Input $input);
}
You can implement like so:
<?php
class Mean extends AbstractExercise implements ExerciseInterface, CliExercise, SelfCheck
{
...omitting methods described in ExerciseInterface
/**
* @param Input $input
* @return ResultInterface
*/
public function check(Input $input)
{
//do some checking with $input
if ($someResult) {
return new Success('My Check');
}
return new Failure('My Check', "Something didn't go well!");
}
}
As you can see, you do the checking logic and then return a result object. The result object is used to render the
results to the student. In this case the first argument to PhpSchool\PhpWorkshop\Result\Success
is
the name of the check being performed. The same is true for the failure
PhpSchool\PhpWorkshop\Result\Failure
, however, it takes an optional second argument which should
describe what went wrong.
Learn more about results here.
Contrary to what we said earlier (a PSR2 check would be a good candidate for a re-usable check), let's build that
as a self check. We will use the already built example workshop as a base - the finished code is available on the
self-checking-exercise
branch of the
tutorial repository.
We will start fresh from the master
branch for this tutorial, so if you haven't already got it, git
clone it and install the dependencies:
cd projects
git clone git@github.com:php-school/simple-math.git
cd simple-math
composer install
Our check will run the PHP_CodeSniffer tool against the student's solution and report a success or failure based on the result.
composer require squizlabs/php_codesniffer
Our exercise should look like the following:
<?php
namespace PhpSchool\SimpleMath\Exercise;
use PhpSchool\PhpWorkshop\Exercise\AbstractExercise;
use PhpSchool\PhpWorkshop\Exercise\CliExercise;
use PhpSchool\PhpWorkshop\Exercise\ExerciseInterface;
use PhpSchool\PhpWorkshop\Exercise\ExerciseType;
use PhpSchool\PhpWorkshop\ExerciseCheck\SelfCheck;
use PhpSchool\PhpWorkshop\Input\Input;
use PhpSchool\PhpWorkshop\Result\Failure;
use PhpSchool\PhpWorkshop\Result\ResultInterface;
use PhpSchool\PhpWorkshop\Result\Success;
class Mean extends AbstractExercise implements
ExerciseInterface,
CliExercise,
SelfCheck
{
/**
* @return string
*/
public function getName()
{
return 'Mean Average';
}
/**
* @return string
*/
public function getDescription()
{
return 'Simple Math';
}
/**
* @return array
*/
public function getArgs()
{
$numArgs = rand(0, 10);
$args = [];
for ($i = 0; $i < $numArgs; $i ++) {
$args[] = rand(0, 100);
}
return $args;
}
/**
* @return ExerciseType
*/
public function getType()
{
return ExerciseType::CLI();
}
/**
* @param Input $input
* @return ResultInterface
*/
public function check(Input $input)
{
}
}
As you can see, our check does nothing at the minute. Let's add the logic to execute phpcs
on the
student's solution using the PSR2
standard. As we brought in the tool via Composer, we can rest
assured that the binary phpcs
is available in our projects vendor
directory.
Our method might look something like this - nothing new going on:
/**
* @param Input $input
* @return ResultInterface
*/
public function check(Input $input)
{
$phpCsBinary = __DIR__ . '/../../vendor/bin/phpcs';
$cmd = sprintf('%s %s --standard=PSR2', $phpCsBinary, $input->getArgument('program'));
exec($cmd, $output, $exitCode);
if ($exitCode === 0) {
return new Success('PSR2 Code Check');
}
return new Failure('PSR2 Code Check', 'Coding style did not conform to PSR2!');
}
If the phpcs
binary returns a non-zero exit code - a failure occurred: probably the solution did not
pass the coding standard check. So we return a failure with an error message. Otherwise a Success is returned.
Verifying a solution which does not pass the PSR2
coding standard will yield
the output:
And a solution which does pass would yield the output:
Hopefully this feature will help you build your workshops that bit faster!