-
Notifications
You must be signed in to change notification settings - Fork 4
Code patch updates #201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code patch updates #201
Changes from 1 commit
60e154c
e04dd7f
0e0c44a
abb89cd
3342864
33dfe13
fcbf0bf
41e9746
f45b6f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ | |
use DI\Container; | ||
use DI\ContainerBuilder; | ||
use PhpSchool\PhpWorkshop\Check\CheckRepository; | ||
use PhpSchool\PhpWorkshop\Event\Event; | ||
use PhpSchool\PhpWorkshop\Event\EventDispatcher; | ||
use PhpSchool\PhpWorkshop\Exception\InvalidArgumentException; | ||
use PhpSchool\PhpWorkshop\Exception\MissingArgumentException; | ||
use PhpSchool\PhpWorkshop\Factory\ResultRendererFactory; | ||
|
@@ -84,6 +86,8 @@ public function __construct(string $workshopTitle, string $diConfigFile) | |
|
||
$this->workshopTitle = $workshopTitle; | ||
$this->diConfigFile = $diConfigFile; | ||
|
||
set_error_handler([$this, 'handleInternalError']); | ||
} | ||
|
||
/** | ||
|
@@ -192,6 +196,18 @@ public function configure(): ContainerInterface | |
} | ||
} | ||
|
||
$tearDown = function () use ($container): bool { | ||
mikeymike marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// TODO: This works.. but wrong event as PatchListener take ExerciseRunnerEvent | ||
mikeymike marked this conversation as resolved.
Show resolved
Hide resolved
|
||
$container | ||
->get(EventDispatcher::class) | ||
->dispatch(new Event('application.tear-down')); | ||
|
||
// Fallback to default error handler | ||
return false; | ||
}; | ||
|
||
set_error_handler($tearDown); | ||
|
||
return $container; | ||
} | ||
|
||
|
@@ -227,6 +243,10 @@ public function run(): int | |
$message = str_replace($basePath, '', $message); | ||
} | ||
|
||
$container | ||
mikeymike marked this conversation as resolved.
Show resolved
Hide resolved
|
||
->get(EventDispatcher::class) | ||
->dispatch(new Event('application.tear-down')); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I was thinking, if an exception ever happened in a listener then it would bubble up and mask the original exception. It could be the initial exception is something we WANT to show the user, and thus an exception covering this up during some failed cleanup process is not something we want the user to see really. @AydinHassan thoughts? We could move this dispatch into a separate method that tries and catches and either ignores or logs and lets the application carry on the closing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mikeymike I think logging sounds like a good idea |
||
|
||
$container | ||
->get(OutputInterface::class) | ||
->printError( | ||
|
@@ -268,4 +288,16 @@ private function getContainer(): Container | |
|
||
return $containerBuilder->build(); | ||
} | ||
|
||
private function handleInternalError( | ||
mikeymike marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int $errno, | ||
string $errstr, | ||
string $errfile, | ||
int $errline | ||
): bool { | ||
|
||
|
||
|
||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
*/ | ||
abstract class AbstractExercise | ||
{ | ||
protected ?SolutionInterface $solution = null; | ||
|
||
/** | ||
* Get the name of the exercise, like `Hello World!`. | ||
|
@@ -35,15 +36,19 @@ abstract public function getName(): string; | |
*/ | ||
public function getSolution(): SolutionInterface | ||
{ | ||
return SingleFileSolution::fromFile( | ||
(string) realpath( | ||
sprintf( | ||
'%s/../../exercises/%s/solution/solution.php', | ||
dirname((string) (new ReflectionClass(static::class))->getFileName()), | ||
self::normaliseName($this->getName()) | ||
if (null === $this->solution) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so this was required and probs also for dir solution... the problem being that a patched solution in temp was being overwritten due to the solution being copied on construct and we're constructing fresh on each call to getSolution. Probably a better fix here, maybe moving the copy to temp dir to somewhere that isn't on solution construct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AydinHassan I need your input on this one bro, can you think of a cleaner approach that for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm not really off the top of my head. I think it's fine as you have it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess the only problem I foresee here is the implementation of a solution inside the exercise instead of using the Abstract. e.g if you need a dir solution and you use the static factory 🤔 |
||
$this->solution = SingleFileSolution::fromFile( | ||
(string)realpath( | ||
sprintf( | ||
'%s/../../exercises/%s/solution/solution.php', | ||
dirname((string)(new ReflectionClass(static::class))->getFileName()), | ||
self::normaliseName($this->getName()) | ||
) | ||
) | ||
) | ||
); | ||
); | ||
} | ||
|
||
return $this->solution; | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace PhpSchool\PhpWorkshop\Listener; | ||
|
||
use PhpSchool\PhpWorkshop\Event\ExerciseRunnerEvent; | ||
use Symfony\Component\Filesystem\Filesystem; | ||
|
||
/** | ||
* Move solution to temp and replace program arg | ||
*/ | ||
class InMemorySolutionListener | ||
mikeymike marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
/** | ||
* @param ExerciseRunnerEvent $event | ||
*/ | ||
public function __invoke(ExerciseRunnerEvent $event): void | ||
{ | ||
if (!$event->getInput()->hasArgument('program')) { | ||
return; | ||
} | ||
|
||
$program = $event->getInput()->getRequiredArgument('program'); | ||
|
||
if (!file_exists($program)) { | ||
return; | ||
} | ||
|
||
$filesystem = new Filesystem(); | ||
$basedir = basename($program); | ||
$tmp = sprintf('%s/php-school/user-solution', sys_get_temp_dir()); | ||
|
||
if ($filesystem->exists($tmp)) { | ||
$filesystem->remove($tmp); | ||
} | ||
|
||
$filesystem->mkdir($tmp); | ||
|
||
// Iterator with exclusions... TODO: Useful? | ||
$dirIterator = new \RecursiveDirectoryIterator($basedir, \RecursiveDirectoryIterator::SKIP_DOTS); | ||
$filter = new class($dirIterator) extends RecursiveFilterIterator { | ||
private const FILTERS = ['vendor', '.idea', ',git']; | ||
public function accept() { | ||
return !in_array($this->current()->getFilename(), self::FILTERS, true); | ||
} | ||
}; | ||
$iterator = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::SELF_FIRST); | ||
|
||
$composerFile = sprintf('%s/composer.json', $basedir); | ||
if ($filesystem->exists($composerFile)) { | ||
$filesystem->copy($composerFile, $tmp . '/composer.json'); | ||
} | ||
|
||
|
||
// TODO should this event be somewhere we have the exercise | ||
mikeymike marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// TODO: We could then check if it's a single file solutino or dir solution | ||
// TODO: Single file solutions can be just copying entrypoint to temp | ||
// TODO: dir solution we can copy the dirname to temp, however we should take care here | ||
// TODO: e.g. what if it's all just in the root home dir... we can't recursively copy that all into temp... | ||
// TODO: how do we prevent such an issue ?!? | ||
|
||
|
||
$event->getInput()->setArgument('program', (string) realpath($program)); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.