Skip to content

Commit c96e472

Browse files
authored
Merge pull request #19 from php-school/improve-error-messages
Parse output from composer and list missing extensions
2 parents b889f40 + e12c95a commit c96e472

20 files changed

+837
-223
lines changed

app/config.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
use PhpSchool\WorkshopManager\Command\UninstallWorkshop;
1818
use PhpSchool\WorkshopManager\Command\UpdateWorkshop;
1919
use PhpSchool\WorkshopManager\Command\VerifyInstall;
20+
use PhpSchool\WorkshopManager\ComposerInstaller;
2021
use PhpSchool\WorkshopManager\ComposerInstallerFactory;
2122
use PhpSchool\WorkshopManager\Downloader;
2223
use PhpSchool\WorkshopManager\Filesystem;
2324
use PhpSchool\WorkshopManager\Installer\Installer;
2425
use PhpSchool\WorkshopManager\Installer\Uninstaller;
2526
use PhpSchool\WorkshopManager\Installer\Updater;
26-
use PhpSchool\WorkshopManager\IOFactory;
2727
use PhpSchool\WorkshopManager\Linker;
2828
use PhpSchool\WorkshopManager\ManagerState;
2929
use PhpSchool\WorkshopManager\Repository\InstalledWorkshopRepository;
@@ -125,14 +125,13 @@
125125
);
126126
}),
127127
Installer::class => \DI\factory(function (ContainerInterface $c) {
128-
$io = $c->get(IOFactory::class)->getNullableIO($c->get(InputInterface::class), $c->get(OutputInterface::class));
129128
return new Installer(
130129
$c->get(InstalledWorkshopRepository::class),
131130
$c->get(RemoteWorkshopRepository::class),
132131
$c->get(Linker::class),
133132
$c->get(Filesystem::class),
134133
$c->get('appDir'),
135-
new ComposerInstallerFactory($c->get(Factory::class), $io),
134+
$c->get(ComposerInstaller::class),
136135
$c->get(Client::class),
137136
$c->get(VersionChecker::class)
138137
);
@@ -159,14 +158,14 @@
159158
Client::class => \DI\factory(function (ContainerInterface $c) {
160159
return new Client;
161160
}),
162-
Factory::class => \DI\object(),
163-
IOFactory::class => \DI\object(),
164-
IOInterface::class => \DI\factory(function (ContainerInterface $c) {
165-
return $c->get(IOFactory::class)->getIO(
161+
ComposerInstaller::class => function (ContainerInterface $c) {
162+
return new ComposerInstaller(
166163
$c->get(InputInterface::class),
167-
$c->get(OutputInterface::class)
164+
$c->get(OutputInterface::class),
165+
new Factory
168166
);
169-
}),
167+
},
168+
Factory::class => \DI\object(),
170169
InputInterface::class => \Di\factory(function () {
171170
return new \Symfony\Component\Console\Input\ArgvInput($_SERVER['argv']);
172171
}),

composer.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
"symfony/filesystem": "^3.1",
2525
"tm/tooly-composer-script": "^1.0",
2626
"padraic/phar-updater": "^1.0",
27-
"samsonasik/package-versions": "^1.0"
27+
"samsonasik/package-versions": "^1.0",
28+
"ext-curl": "*",
29+
"ext-json": "*",
30+
"ext-mbstring": "*",
31+
"ext-zip": "*"
2832
},
2933
"require-dev": {
3034
"phpunit/phpunit": "~5.4",

src/Command/InstallWorkshop.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,17 @@ public function __invoke(OutputInterface $output, $workshopName)
6868
''
6969
]);
7070
} catch (ComposerFailureException $e) {
71-
$message = " <error> There was a problem installing dependencies for \"%s\". Try running in verbose";
72-
$message .= " mode to see the composer error: %s </error>\n";
71+
$message = " <error> There was a problem installing dependencies for \"%s\".%s Try running in verbose";
72+
$message .= " mode to see more details: %s </error>\n";
7373

74-
$output->writeln(sprintf($message, $workshopName, $this->getCommand()));
74+
$output->writeln(
75+
sprintf(
76+
$message,
77+
$workshopName,
78+
$e->getMessage() ? sprintf(' %s', $e->getMessage()) : '',
79+
$this->getCommand()
80+
)
81+
);
7582
} catch (\Exception $e) {
7683
$output->writeln(
7784
sprintf(" <error> An unknown error occurred: \"%s\" </error>\n", $e->getMessage())

src/Command/VerifyInstall.php

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
*/
1212
class VerifyInstall
1313
{
14+
/**
15+
* @var array
16+
*/
17+
private static $requiredExtensions = ['json', 'zip', 'mbstring', 'curl'];
18+
1419
/**
1520
* @var InputInterface
1621
*/
@@ -26,6 +31,7 @@ class VerifyInstall
2631
*/
2732
private $workshopHomeDirectory;
2833

34+
2935
/**
3036
* @param InputInterface $input
3137
* @param OutputInterface $output
@@ -44,9 +50,9 @@ public function __invoke()
4450

4551
$style->title("Verifying your installation");
4652

47-
53+
4854
if (strpos(getenv('PATH'), sprintf('%s/bin', $this->workshopHomeDirectory)) !== false) {
49-
$style->success('Your $PATH environment variable is configured correctly');
55+
$style->success('Your $PATH environment variable is configured correctly.');
5056
} else {
5157
$style->error('The PHP School bin directory is not in your PATH variable.');
5258

@@ -68,13 +74,33 @@ public function __invoke()
6874
''
6975
]);
7076
}
71-
77+
7278
if (version_compare(PHP_VERSION, '5.6')) {
73-
$message = 'Your PHP version is at least 5.6, which is required by this tool. Be aware that some ';
74-
$message .= 'workshops may require a higher version of PHP, so you may not be able to install them.';
75-
$style->success($message);
79+
$message = 'Your PHP version is %s, PHP 5.6 is the minimum supported version for this tool. Please note ';
80+
$message .= 'that some workshops may require a higher version of PHP, so you may not be able to install ';
81+
$message .= 'them without upgrading PHP.';
82+
$style->success(sprintf($message, PHP_VERSION));
7683
} else {
7784
$style->error('You need a PHP version of at least 5.6 to use PHP School.');
7885
}
86+
87+
$missingExtensions = array_filter(static::$requiredExtensions, function ($extension) {
88+
return !extension_loaded($extension);
89+
});
90+
91+
array_walk($missingExtensions, function ($missingExtension) use ($style) {
92+
$style->error(
93+
sprintf(
94+
'The %s extension is missing - use your preferred package manager to install it.',
95+
$missingExtension
96+
)
97+
);
98+
});
99+
100+
if (empty($missingExtensions)) {
101+
$message = 'All required PHP extensions are installed. Please note that some workshops may require ';
102+
$message .= 'additional PHP extensions.';
103+
$style->success($message);
104+
}
79105
}
80106
}

src/ComposerInstaller.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace PhpSchool\WorkshopManager;
4+
5+
use Composer\Factory;
6+
use Composer\Installer;
7+
use Composer\IO\ConsoleIO;
8+
use Symfony\Component\Console\Helper\HelperSet;
9+
use Symfony\Component\Console\Input\InputInterface;
10+
use Symfony\Component\Console\Output\ConsoleOutput;
11+
use Symfony\Component\Console\Output\NullOutput;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
use Symfony\Component\Console\Output\StreamOutput;
14+
15+
/**
16+
* @author Aydin Hassan <[email protected]>
17+
*/
18+
class ComposerInstaller
19+
{
20+
/**
21+
* @var InputInterface
22+
*/
23+
private $input;
24+
25+
/**
26+
* @var OutputInterface
27+
*/
28+
private $output;
29+
30+
/**
31+
* @var Factory
32+
*/
33+
private $composerFactory;
34+
35+
/**
36+
* @param InputInterface $input
37+
* @param OutputInterface $output
38+
* @param Factory $composerFactory
39+
*/
40+
public function __construct(InputInterface $input, OutputInterface $output, Factory $composerFactory)
41+
{
42+
$this->input = $input;
43+
$this->output = $output;
44+
$this->composerFactory = $composerFactory;
45+
}
46+
47+
/**
48+
* @param string $pathToComposerProject
49+
* @return InstallResult
50+
*/
51+
public function install($pathToComposerProject)
52+
{
53+
if ($this->output->isVerbose()) {
54+
$output = $this->output;
55+
} else {
56+
//write all output in verbose mode to a temp stream
57+
//so we don't write it out when not in verbose mode
58+
$output = new StreamOutput(
59+
fopen('php://memory', 'w'),
60+
OutputInterface::VERBOSITY_VERY_VERBOSE,
61+
$this->output->isDecorated(),
62+
$this->output->getFormatter()
63+
);
64+
}
65+
66+
$wrappedOutput = new RecordingOutput($output);
67+
$io = new ConsoleIO($this->input, $wrappedOutput, new HelperSet);
68+
69+
$composer = $this->composerFactory->createComposer(
70+
$io,
71+
sprintf('%s/composer.json', rtrim($pathToComposerProject, '/')),
72+
false,
73+
$pathToComposerProject
74+
);
75+
76+
return new InstallResult(
77+
Installer::create($io, $composer)->run(),
78+
$wrappedOutput->getOutput()
79+
);
80+
}
81+
}

src/ComposerInstallerFactory.php

Lines changed: 0 additions & 51 deletions
This file was deleted.

src/Exception/ComposerFailureException.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,26 @@
66
* Class ComposerFailureException
77
* @author Michael Woodward <[email protected]>
88
*/
9-
final class ComposerFailureException extends \RuntimeException
9+
class ComposerFailureException extends \RuntimeException
1010
{
1111
/**
1212
* @param \Exception $e
13-
* @return ComposerFailureException
13+
* @return self
1414
*/
1515
public static function fromException(\Exception $e)
1616
{
1717
return new self($e->getMessage());
1818
}
19+
20+
/**
21+
* @param array $missingExtensions
22+
* @return self
23+
*/
24+
public static function fromMissingExtensions(array $missingExtensions)
25+
{
26+
$message = 'This workshop requires some extra PHP extensions. Please install them';
27+
$message .= ' and try again. Required extensions are %s.';
28+
29+
return new self(sprintf($message, implode(', ', $missingExtensions)));
30+
}
1931
}

src/IOFactory.php

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)