├── PULL_REQUEST_TEMPLATE.md
├── src
├── Bowerphp
│ ├── Util
│ │ ├── ZipArchive.php
│ │ ├── PackageNameVersionExtractor.php
│ │ ├── Filesystem.php
│ │ └── ErrorHandler.php
│ ├── Command
│ │ ├── Helper
│ │ │ └── QuestionHelper.php
│ │ ├── LookupCommand.php
│ │ ├── ListCommand.php
│ │ ├── SearchCommand.php
│ │ ├── HelpCommand.php
│ │ ├── UninstallCommand.php
│ │ ├── CommandListCommand.php
│ │ ├── UpdateCommand.php
│ │ ├── InitCommand.php
│ │ ├── InfoCommand.php
│ │ ├── HomeCommand.php
│ │ ├── Command.php
│ │ └── InstallCommand.php
│ ├── Installer
│ │ ├── InstallerInterface.php
│ │ └── Installer.php
│ ├── Repository
│ │ ├── RepositoryInterface.php
│ │ └── GithubRepository.php
│ ├── Package
│ │ ├── PackageInterface.php
│ │ └── Package.php
│ ├── Config
│ │ ├── ConfigInterface.php
│ │ └── Config.php
│ ├── Output
│ │ └── BowerphpConsoleOutput.php
│ ├── Console
│ │ └── Application.php
│ ├── Compiler.php
│ └── Bowerphp.php
└── bootstrap.php
├── LICENSE
├── bin
└── bowerphp
└── composer.json
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/Bowerphp/Util/ZipArchive.php:
--------------------------------------------------------------------------------
1 | numFiles;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/bootstrap.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | $includeIfExists = function ($file) {
13 | return file_exists($file) ? include $file : false;
14 | };
15 |
16 | if ((!$loader = $includeIfExists(__DIR__ . '/../vendor/autoload.php')) && (!$loader = $includeIfExists(__DIR__ . '/../../../autoload.php'))) {
17 | $error = 'You must set up the project dependencies, run the following commands:' . PHP_EOL .
18 | 'curl -sS https://getcomposer.org/installer | php' . PHP_EOL .
19 | 'php composer.phar install' . PHP_EOL;
20 |
21 | throw new \Exception($error);
22 | }
23 |
24 | return $loader;
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Massimiliano Arione
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/bin/bowerphp:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | run();
45 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/Helper/QuestionHelper.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command\Helper;
13 |
14 | use Symfony\Component\Console\Helper\QuestionHelper as BaseQuestionHelper;
15 | use Symfony\Component\Console\Question\Question;
16 |
17 | /**
18 | * Copied by Composer https://github.com/composer/composer
19 | */
20 | class QuestionHelper extends BaseQuestionHelper
21 | {
22 | /**
23 | * Build text for asking a question. For example:
24 | *
25 | * "Do you want to continue [yes]:"
26 | *
27 | * @param string $question The question you want to ask
28 | * @param mixed $default Default value to add to message, if false no default will be shown
29 | * @param string $sep Separation char for between message and user input
30 | *
31 | * @return string
32 | */
33 | public function getQuestion($question, $default = null, $sep = ':')
34 | {
35 | return null !== $default ?
36 | new Question(sprintf('%s [%s]%s ', $question, $default, $sep)) :
37 | new Question(sprintf('%s%s ', $question, $sep));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Bowerphp/Installer/InstallerInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Util;
13 |
14 | /**
15 | * PackageNameVersionExtractor
16 | */
17 | class PackageNameVersionExtractor
18 | {
19 | /**
20 | * @var string
21 | */
22 | public $name;
23 |
24 | /**
25 | * @var string
26 | */
27 | public $version;
28 |
29 | /**
30 | * @param string $name
31 | * @param string $version
32 | */
33 | public function __construct($name, $version)
34 | {
35 | $this->name = $name;
36 | $this->version = $version;
37 | }
38 |
39 | /**
40 | * @param string $endpoint
41 | * @return PackageNameVersionExtractor
42 | */
43 | public static function fromString($endpoint)
44 | {
45 | $map = explode('#', $endpoint);
46 | $name = isset($map[0]) ? $map[0] : $endpoint;
47 | $version = isset($map[1]) ? $map[1] : '*';
48 |
49 | // Convert user/package shorthand to GitHub url
50 | if (preg_match('/^([-_a-z0-9]+)\/([-_a-z0-9]+)$/i', $name)) {
51 | $name = 'https://github.com/' . $name . '.git';
52 | }
53 |
54 | return new self($name, $version);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Bowerphp/Util/Filesystem.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Util;
13 |
14 | use Symfony\Component\Filesystem\Exception\FileNotFoundException;
15 | use Symfony\Component\Filesystem\Filesystem as BaseFilesystem;
16 |
17 | /**
18 | * Filesystem
19 | */
20 | class Filesystem extends BaseFilesystem
21 | {
22 | /**
23 | * Read a file
24 | *
25 | * @param string $filename
26 | *
27 | * @return string
28 | */
29 | public function read($filename)
30 | {
31 | if (!is_readable($filename)) {
32 | throw new FileNotFoundException(sprintf('File "%s" does not exist.', $filename), 0, null, $filename);
33 | }
34 |
35 | return file_get_contents($filename);
36 | }
37 |
38 | /**
39 | * Write a file
40 | *
41 | * @param string $filename The file to be written to
42 | * @param string $content The data to write into the file
43 | * @param int $mode The file mode (octal)
44 | *
45 | * @throws \Symfony\Component\Filesystem\Exception\IOException If the file cannot be written to
46 | */
47 | public function write($filename, $content, $mode = 0644)
48 | {
49 | $this->dumpFile($filename, $content);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Bowerphp/Repository/RepositoryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Util;
13 |
14 | use ErrorException;
15 |
16 | /**
17 | * Convert PHP errors into exceptions
18 | * Copied by Composer https://github.com/composer/composer
19 | */
20 | class ErrorHandler
21 | {
22 | /**
23 | * Error handler
24 | *
25 | * @param int $level Level of the error raised
26 | * @param string $message Error message
27 | * @param string $file Filename that the error was raised in
28 | * @param int $line Line number the error was raised at
29 | *
30 | * @static
31 | * @throws \ErrorException
32 | */
33 | public static function handle($level, $message, $file, $line)
34 | {
35 | // respect error_reporting being disabled
36 | if (0 === error_reporting()) {
37 | return;
38 | }
39 |
40 | if (ini_get('xdebug.scream')) {
41 | $message .= "\n\nWarning: You have xdebug.scream enabled, the warning above may be" .
42 | "\na legitimately suppressed error that you were not supposed to see.";
43 | }
44 |
45 | throw new ErrorException($message, 0, $level, $file, $line);
46 | }
47 |
48 | /**
49 | * Register error handler
50 | *
51 | * @static
52 | */
53 | public static function register()
54 | {
55 | set_error_handler([__CLASS__, 'handle']);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/LookupCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Util\PackageNameVersionExtractor;
15 | use Symfony\Component\Console\Input\InputArgument;
16 | use Symfony\Component\Console\Input\InputInterface;
17 | use Symfony\Component\Console\Output\OutputInterface;
18 |
19 | /**
20 | * Lookup
21 | */
22 | class LookupCommand extends Command
23 | {
24 | /**
25 | * {@inheritdoc}
26 | */
27 | protected function configure()
28 | {
29 | $this
30 | ->setName('lookup')
31 | ->setDescription('Look up a package URL by name')
32 | ->addArgument('package', InputArgument::REQUIRED, 'Choose a package.')
33 | ->setHelp(<<<'EOT'
34 | The %command.name% command is used for search with exact match the repository URL package
35 |
36 | php %command.full_name% packageName
37 | EOT
38 | );
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | protected function execute(InputInterface $input, OutputInterface $output)
45 | {
46 | $this->setGithubToken($output);
47 |
48 | $name = $input->getArgument('package');
49 |
50 | $packageNameVersion = PackageNameVersionExtractor::fromString($name);
51 |
52 | $bowerphp = $this->getBowerphp($output);
53 |
54 | $package = $bowerphp->lookupPackage($packageNameVersion->name);
55 |
56 | $this->consoleOutput->writelnSearchOrLookup($package['name'], $package['url']);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/ListCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Installer\Installer;
15 | use Bowerphp\Util\ZipArchive;
16 | use Symfony\Component\Console\Input\InputInterface;
17 | use Symfony\Component\Console\Output\OutputInterface;
18 | use Symfony\Component\Finder\Finder;
19 |
20 | /**
21 | * This command shows a list of installed packages.
22 | * Not to be confused with original "list" command of Symfony, that has been
23 | * renamed to "list-commands" (see CommandListCommand.php)
24 | */
25 | class ListCommand extends Command
26 | {
27 | /**
28 | * {@inheritdoc}
29 | */
30 | protected function configure()
31 | {
32 | $this
33 | ->setName('list')
34 | ->setDescription('Lists installed packages')
35 | ->setHelp(<<<'EOT'
36 | The %command.name% lists installed packages.
37 |
38 | %command.full_name%
39 | EOT
40 | )
41 | ;
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | protected function execute(InputInterface $input, OutputInterface $output)
48 | {
49 | $this->setGithubToken($output);
50 |
51 | $installer = new Installer($this->filesystem, new ZipArchive(), $this->config);
52 | $bowerphp = $this->getBowerphp($output);
53 | $packages = $bowerphp->getInstalledPackages($installer, new Finder());
54 |
55 | foreach ($packages as $package) {
56 | $this->consoleOutput->writelnListPackage($package, $bowerphp);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "beelab/bowerphp",
3 | "description": "An implementation of bower in PHP",
4 | "keywords": ["bower", "package", "dependency", "frontend"],
5 | "homepage": "https://bowerphp.github.io/",
6 | "type": "library",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Massimiliano Arione",
11 | "email": "massimiliano.arione@bee-lab.net"
12 | },
13 | {
14 | "name": "Mauro D'Alatri",
15 | "email": "mauro.dalatri@bee-lab.net"
16 | },
17 | {
18 | "name": "The Community",
19 | "homepage": "https://github.com/Bee-Lab/bowerphp/graphs/contributors"
20 | }
21 | ],
22 | "support": {
23 | "issues": "https://github.com/bee-lab/bowserphp/issues"
24 | },
25 | "require": {
26 | "php": "^5.6|^7.0",
27 | "ext-fileinfo": "*",
28 | "ext-zip": "*",
29 | "knplabs/github-api": "^1.4",
30 | "samsonasik/package-versions": "^1.1",
31 | "symfony/console": "^3.4|^4.1",
32 | "symfony/filesystem": "^3.4|^4.1",
33 | "symfony/finder": "^3.4|^4.1",
34 | "symfony/process": "^3.4|^4.1",
35 | "vierbergenlars/php-semver": "^3.0"
36 | },
37 | "require-dev": {
38 | "ext-phar": "*",
39 | "friendsofphp/php-cs-fixer": "^2.14",
40 | "mockery/mockery": "^1.0",
41 | "phpunit/phpunit": "^5.7|^6.0|^7.0"
42 | },
43 | "autoload": {
44 | "psr-4": {
45 | "Bowerphp\\": "src/Bowerphp"
46 | }
47 | },
48 | "bin": ["bin/bowerphp"],
49 | "extra": {
50 | "branch-alias": {
51 | "dev-master": "1.0-dev"
52 | }
53 | },
54 | "config": {
55 | "bin-dir": "bin",
56 | "sort-packages": true
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/SearchCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Symfony\Component\Console\Input\InputArgument;
15 | use Symfony\Component\Console\Input\InputInterface;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 |
18 | /**
19 | * Search
20 | */
21 | class SearchCommand extends Command
22 | {
23 | /**
24 | * {@inheritdoc}
25 | */
26 | protected function configure()
27 | {
28 | $this
29 | ->setName('search')
30 | ->setDescription('Search for a package by name')
31 | ->addArgument('name', InputArgument::REQUIRED, 'Name to search for.')
32 | ->setHelp(<<<'EOT'
33 | The %command.name% command searches for a package by name.
34 |
35 | php %command.full_name% name
36 | EOT
37 | );
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | protected function execute(InputInterface $input, OutputInterface $output)
44 | {
45 | $this->setGithubToken($output);
46 |
47 | $name = $input->getArgument('name');
48 |
49 | $bowerphp = $this->getBowerphp($output);
50 | $packages = $bowerphp->searchPackages($name);
51 |
52 | if (empty($packages)) {
53 | $output->writeln('No results.');
54 | } else {
55 | $output->writeln('Search results:' . PHP_EOL);
56 | $consoleOutput = $this->consoleOutput;
57 | array_walk($packages, function ($package) use ($consoleOutput) {
58 | $consoleOutput->writelnSearchOrLookup($package['name'], $package['url'], 4);
59 | });
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/HelpCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Symfony\Component\Console\Command\HelpCommand as SymfonyHelpCommand;
15 | use Symfony\Component\Console\Input\InputArgument;
16 | use Symfony\Component\Console\Input\InputOption;
17 |
18 | /**
19 | * This is just an extension of original HelpCommand, needed to remove the last line from help.
20 | * See also {@link https://github.com/Bee-Lab/bowerphp/issues/108}.
21 | */
22 | class HelpCommand extends SymfonyHelpCommand
23 | {
24 | /**
25 | * {@inheritdoc}
26 | */
27 | protected function configure()
28 | {
29 | $this->ignoreValidationErrors();
30 |
31 | $this
32 | ->setName('help')
33 | ->setDefinition([
34 | new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
35 | new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'),
36 | new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
37 | new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
38 | ])
39 | ->setDescription('Displays help for a command')
40 | ->setHelp(<<<'EOF'
41 | The %command.name% command displays help for a given command:
42 |
43 | php %command.full_name% list
44 |
45 | You can also output the help in other formats by using the --format option:
46 |
47 | php %command.full_name% --format=xml list
48 | EOF
49 | )
50 | ;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/UninstallCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Installer\Installer;
15 | use Bowerphp\Package\Package;
16 | use Bowerphp\Util\PackageNameVersionExtractor;
17 | use Bowerphp\Util\ZipArchive;
18 | use Symfony\Component\Console\Input\InputArgument;
19 | use Symfony\Component\Console\Input\InputInterface;
20 | use Symfony\Component\Console\Output\OutputInterface;
21 |
22 | /**
23 | * Uninstall
24 | */
25 | class UninstallCommand extends Command
26 | {
27 | /**
28 | * {@inheritdoc}
29 | */
30 | protected function configure()
31 | {
32 | $this
33 | ->setName('uninstall')
34 | ->setDescription('Uninstalls a single specified package')
35 | ->addArgument('package', InputArgument::REQUIRED, 'Choose a package.')
36 | ->setHelp(<<<'EOT'
37 | The %command.name% command uninstall a package.
38 |
39 | php %command.full_name% packageName
40 | EOT
41 | )
42 | ;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | protected function execute(InputInterface $input, OutputInterface $output)
49 | {
50 | $this->setGithubToken($output);
51 |
52 | $packageName = $input->getArgument('package');
53 |
54 | $bowerphp = $this->getBowerphp($output);
55 |
56 | try {
57 | $installer = new Installer($this->filesystem, new ZipArchive(), $this->config);
58 |
59 | $packageNameVersion = PackageNameVersionExtractor::fromString($packageName);
60 |
61 | $package = new Package($packageNameVersion->name, $packageNameVersion->version);
62 | $bowerphp->uninstallPackage($package, $installer);
63 | } catch (\RuntimeException $e) {
64 | $output->writeln(sprintf('%s', $e->getMessage()));
65 |
66 | return 1;
67 | }
68 |
69 | $output->writeln('');
70 |
71 | return 0;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/CommandListCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Symfony\Component\Console\Command\ListCommand;
15 | use Symfony\Component\Console\Input\InputArgument;
16 | use Symfony\Component\Console\Input\InputDefinition;
17 | use Symfony\Component\Console\Input\InputOption;
18 |
19 | /**
20 | * CommandList: this command shows a list of available commands.
21 | * It's just an extension of original ListCommand, needed to change "list" name
22 | */
23 | class CommandListCommand extends ListCommand
24 | {
25 | /**
26 | * {@inheritdoc}
27 | */
28 | protected function configure()
29 | {
30 | $this
31 | ->setName('list-commands')
32 | ->setDefinition($this->createDefinition())
33 | ->setDescription('Lists commands')
34 | ->setHelp(<<<'EOF'
35 | The %command.name% command lists all commands:
36 |
37 | php %command.full_name%
38 |
39 | You can also display the commands for a specific namespace:
40 |
41 | php %command.full_name% test
42 |
43 | You can also output the information in other formats by using the --format option:
44 |
45 | php %command.full_name% --format=xml
46 |
47 | It's also possible to get raw list of commands (useful for embedding command runner):
48 |
49 | php %command.full_name% --raw
50 | EOF
51 | )
52 | ;
53 | }
54 |
55 | /**
56 | * {@inheritdoc}
57 | */
58 | private function createDefinition()
59 | {
60 | return new InputDefinition([
61 | new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
62 | new InputOption('xml', null, InputOption::VALUE_NONE, 'To output list as XML'),
63 | new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
64 | new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output list in other formats', 'txt'),
65 | ]);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Bowerphp/Package/PackageInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Installer\Installer;
15 | use Bowerphp\Package\Package;
16 | use Bowerphp\Util\ZipArchive;
17 | use RuntimeException;
18 | use Symfony\Component\Console\Input\InputArgument;
19 | use Symfony\Component\Console\Input\InputInterface;
20 | use Symfony\Component\Console\Output\OutputInterface;
21 |
22 | /**
23 | * Inspired by Composer https://github.com/composer/composer
24 | */
25 | class UpdateCommand extends Command
26 | {
27 | /**
28 | * {@inheritdoc}
29 | */
30 | protected function configure()
31 | {
32 | $this
33 | ->setName('update')
34 | ->setDescription('Update the project dependencies from the bower.json file or a single specified package')
35 | ->addArgument('package', InputArgument::OPTIONAL, 'Choose a package.')
36 | ->setHelp(<<<'EOT'
37 | The %command.name% command reads the bower.json file from
38 | the current directory, processes it, and downloads and installs all the
39 | libraries and dependencies outlined in that file.
40 |
41 | php %command.full_name%
42 |
43 | If an optional package name is passed, only that package is updated.
44 |
45 | php %command.full_name% packageName
46 |
47 | EOT
48 | );
49 | }
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | protected function execute(InputInterface $input, OutputInterface $output)
55 | {
56 | $this->setGithubToken($output);
57 |
58 | $packageName = $input->getArgument('package');
59 | $installer = new Installer($this->filesystem, new ZipArchive(), $this->config);
60 |
61 | try {
62 | $bowerphp = $this->getBowerphp($output);
63 | if (is_null($packageName)) {
64 | $bowerphp->updatePackages($installer);
65 | } else {
66 | $bowerphp->updatePackage(new Package($packageName), $installer);
67 | }
68 | } catch (RuntimeException $e) {
69 | throw new RuntimeException($e->getMessage());
70 | }
71 | $output->writeln('');
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/InitCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Symfony\Component\Console\Input\InputInterface;
15 | use Symfony\Component\Console\Output\OutputInterface;
16 |
17 | /**
18 | * Init
19 | */
20 | class InitCommand extends Command
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | protected function configure()
26 | {
27 | $this
28 | ->setName('init')
29 | ->setDescription('Initializes a bower.json file')
30 | ->setHelp(<<<'EOT'
31 | The %command.name% command initializes a bower.json file in
32 | the current directory.
33 |
34 | php %command.full_name%
35 | EOT
36 | )
37 | ;
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | protected function execute(InputInterface $input, OutputInterface $output)
44 | {
45 | $this->setGithubToken($output);
46 |
47 | $author = sprintf('%s <%s>', $this->getGitInfo(), $this->getGitInfo('user.email'));
48 |
49 | $params = ['name' => get_current_user(), 'author' => $author];
50 |
51 | // @codeCoverageIgnoreStart
52 | if ($input->isInteractive()) {
53 | $dialog = $this->getHelperSet()->get('question');
54 |
55 | $params['name'] = $dialog->ask(
56 | $input, $output, $dialog->getQuestion('Please specify a name for project', $params['name'])
57 | );
58 |
59 | $params['author'] = $dialog->ask(
60 | $input, $output, $dialog->getQuestion('Please specify an author', $params['author'])
61 | );
62 | }
63 | // @codeCoverageIgnoreEnd
64 | $bowerphp = $this->getBowerphp($output);
65 | $bowerphp->init($params);
66 |
67 | $output->writeln('');
68 | }
69 |
70 | /**
71 | * Get some info from local git
72 | *
73 | * @param string $info info type
74 | * @return string|null
75 | */
76 | private function getGitInfo($info = 'user.name')
77 | {
78 | $output = [];
79 | $return = 0;
80 | $info = exec("git config --get $info", $output, $return);
81 |
82 | if (0 === $return) {
83 | return $info;
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/InfoCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Package\Package;
15 | use Bowerphp\Util\PackageNameVersionExtractor;
16 | use Symfony\Component\Console\Input\InputArgument;
17 | use Symfony\Component\Console\Input\InputInterface;
18 | use Symfony\Component\Console\Output\OutputInterface;
19 |
20 | /**
21 | * Info
22 | */
23 | class InfoCommand extends Command
24 | {
25 | /**
26 | * {@inheritdoc}
27 | */
28 | protected function configure()
29 | {
30 | $this
31 | ->setName('info')
32 | ->setDescription('Displays overall information of a package or of a particular version')
33 | ->addArgument('package', InputArgument::REQUIRED, 'Choose a package.')
34 | ->addArgument('property', InputArgument::OPTIONAL, 'A property present in bower.json.')
35 | ->setHelp(<<<'EOT'
36 | The %command.name% command displays overall information of a package or of a particular version.
37 | If you pass a property present in bower.json, you can get the correspondent value.
38 |
39 | php %command.full_name% package
40 | EOT
41 | )
42 | ;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | protected function execute(InputInterface $input, OutputInterface $output)
49 | {
50 | $this->setGithubToken($output);
51 |
52 | $packageName = $input->getArgument('package');
53 | $property = $input->getArgument('property');
54 |
55 | $packageNameVersion = PackageNameVersionExtractor::fromString($packageName);
56 |
57 | $package = new Package($packageNameVersion->name, $packageNameVersion->version);
58 | $bowerphp = $this->getBowerphp($output);
59 |
60 | $bowerJsonFile = $bowerphp->getPackageBowerFile($package);
61 | if ('*' == $packageNameVersion->version) {
62 | $versions = $bowerphp->getPackageInfo($package, 'versions');
63 | }
64 | if (!is_null($property)) {
65 | $bowerArray = json_decode($bowerJsonFile, true);
66 | $propertyValue = isset($bowerArray[$property]) ? $bowerArray[$property] : '';
67 | $this->consoleOutput->writelnJsonText($propertyValue);
68 |
69 | return;
70 | }
71 | $this->consoleOutput->writelnJson($bowerJsonFile);
72 | if ('*' != $packageNameVersion->version) {
73 | return;
74 | }
75 | $output->writeln('');
76 | if (empty($versions)) {
77 | $output->writeln('No versions available.');
78 | } else {
79 | $output->writeln('Available versions:');
80 | foreach ($versions as $vrs) {
81 | $output->writeln("- $vrs");
82 | }
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/HomeCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Package\Package;
15 | use Bowerphp\Util\PackageNameVersionExtractor;
16 | use Symfony\Component\Console\Input\InputArgument;
17 | use Symfony\Component\Console\Input\InputInterface;
18 | use Symfony\Component\Console\Output\OutputInterface;
19 | use Symfony\Component\Process\Process;
20 |
21 | /**
22 | * Home
23 | */
24 | class HomeCommand extends Command
25 | {
26 | /**
27 | * {@inheritdoc}
28 | */
29 | protected function configure()
30 | {
31 | $this
32 | ->setName('home')
33 | ->setDescription('Opens a package homepage into your favorite browser')
34 | ->addArgument('package', InputArgument::REQUIRED, 'Choose a package.')
35 | ->setHelp(<<<'EOT'
36 | The %command.name% command opens a package homepage into your favorite browser.
37 |
38 | php %command.full_name% name
39 | EOT
40 | )
41 | ;
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | protected function execute(InputInterface $input, OutputInterface $output)
48 | {
49 | $this->setGithubToken($output);
50 |
51 | $packageName = $input->getArgument('package');
52 |
53 | $packageNameVersion = PackageNameVersionExtractor::fromString($packageName);
54 |
55 | $package = new Package($packageNameVersion->name, $packageNameVersion->version);
56 | $bowerphp = $this->getBowerphp($output);
57 |
58 | $url = $bowerphp->getPackageInfo($package);
59 |
60 | $default = $this->getDefaultBrowser();
61 |
62 | $arg = "$default \"$url\"";
63 |
64 | if (OutputInterface::VERBOSITY_DEBUG <= $output->getVerbosity()) {
65 | $output->writeln($arg);
66 | } else {
67 | $output->writeln('');
68 | }
69 | // @codeCoverageIgnoreStart
70 | if (!defined('PHPUNIT_BOWER_TESTSUITE')) {
71 | $browser = new Process($arg);
72 | $browser->start();
73 | while ($browser->isRunning()) {
74 | // do nothing...
75 | }
76 | }
77 | // @codeCoverageIgnoreEnd
78 | }
79 |
80 | /**
81 | * @return string
82 | * @codeCoverageIgnore
83 | */
84 | private function getDefaultBrowser()
85 | {
86 | $xdgOpen = new Process('which xdg-open');
87 | $xdgOpen->run();
88 | if (!$xdgOpen->isSuccessful()) {
89 | $open = new Process('which open');
90 | $open->run();
91 | if (!$open->isSuccessful()) {
92 | throw new \RuntimeException('Could not open default browser.');
93 | }
94 |
95 | return trim($open->getOutput());
96 | }
97 |
98 | return trim($xdgOpen->getOutput());
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/Command.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Bowerphp;
15 | use Bowerphp\Config\Config;
16 | use Bowerphp\Output\BowerphpConsoleOutput;
17 | use Bowerphp\Repository\GithubRepository;
18 | use Bowerphp\Util\Filesystem;
19 | use Github\Client;
20 | use Guzzle\Log\ClosureLogAdapter;
21 | use Guzzle\Log\MessageFormatter;
22 | use Guzzle\Plugin\Log\LogPlugin;
23 | use Symfony\Component\Console\Command\Command as BaseCommand;
24 | use Symfony\Component\Console\Output\OutputInterface;
25 |
26 | /**
27 | * Base class for Bowerphp commands
28 | * Inspired by Composer https://github.com/composer/composer
29 | */
30 | abstract class Command extends BaseCommand
31 | {
32 | /**
33 | * @var Filesystem
34 | */
35 | protected $filesystem;
36 |
37 | /**
38 | * @var Config
39 | */
40 | protected $config;
41 |
42 | /**
43 | * @var Client
44 | */
45 | protected $githubClient;
46 |
47 | /**
48 | * @var BowerphpConsoleOutput
49 | */
50 | protected $consoleOutput;
51 |
52 | /**
53 | * Debug HTTP interactions
54 | *
55 | * @param Client $client
56 | * @param OutputInterface $output
57 | */
58 | protected function logHttp(Client $client, OutputInterface $output)
59 | {
60 | $guzzle = $client->getHttpClient();
61 | if (OutputInterface::VERBOSITY_DEBUG <= $output->getVerbosity()) {
62 | $logger = function ($message) use ($output) {
63 | $finfo = new \finfo(FILEINFO_MIME);
64 | $msg = ('text' == substr($finfo->buffer($message), 0, 4)) ? $message : '(binary string)';
65 | $output->writeln('Guzzle ' . $msg);
66 | };
67 | $logAdapter = new ClosureLogAdapter($logger);
68 | $logPlugin = new LogPlugin($logAdapter, MessageFormatter::DEBUG_FORMAT);
69 | $guzzle->addSubscriber($logPlugin);
70 | }
71 | }
72 |
73 | /**
74 | * Set oauth token (to increase API limit to 5000 per hour, instead of default 60)
75 | *
76 | * @param Client $client
77 | */
78 | protected function setToken(Client $client)
79 | {
80 | $token = getenv('BOWERPHP_TOKEN');
81 | if (!empty($token)) {
82 | $client->authenticate($token, null, Client::AUTH_HTTP_TOKEN);
83 | }
84 | }
85 |
86 | /**
87 | * @param OutputInterface $output
88 | */
89 | protected function setGithubToken(OutputInterface $output)
90 | {
91 | $this->filesystem = new Filesystem();
92 | $this->githubClient = new Client();
93 | $this->config = new Config($this->filesystem);
94 | $this->logHttp($this->githubClient, $output);
95 | $this->setToken($this->githubClient);
96 | }
97 |
98 | /**
99 | * @param OutputInterface $output
100 | * @return Bowerphp
101 | */
102 | protected function getBowerphp(OutputInterface $output)
103 | {
104 | $this->consoleOutput = new BowerphpConsoleOutput($output);
105 |
106 | return new Bowerphp($this->config, $this->filesystem, $this->githubClient, new GithubRepository(), $this->consoleOutput);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/Bowerphp/Config/ConfigInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Config;
13 |
14 | use Bowerphp\Package\PackageInterface;
15 |
16 | /**
17 | * ConfigInterface
18 | */
19 | interface ConfigInterface
20 | {
21 | /**
22 | * @return string
23 | */
24 | public function getBasePackagesUrl();
25 |
26 | /**
27 | * @return string
28 | */
29 | public function getCacheDir();
30 |
31 | /**
32 | * @return string
33 | */
34 | public function getInstallDir();
35 |
36 | /**
37 | * @return bool
38 | */
39 | public function isSaveToBowerJsonFile();
40 |
41 | /**
42 | * Set true|false for decide if add package reference on bower.json file during install procedure
43 | *
44 | * @param bool $flag default true
45 | */
46 | public function setSaveToBowerJsonFile($flag = true);
47 |
48 | /**
49 | * Init project's bower.json file
50 | *
51 | * @param array $params
52 | *
53 | * @return int
54 | */
55 | public function initBowerJsonFile(array $params);
56 |
57 | /**
58 | * Update project's bower.json with a new added package
59 | *
60 | * @param PackageInterface $package
61 | *
62 | * @return int
63 | */
64 | public function updateBowerJsonFile(PackageInterface $package);
65 |
66 | /**
67 | * Update project's bower.json from a previous existing one
68 | *
69 | * @param array $old values of previous bower.json
70 | * @param array $new new values
71 | *
72 | * @return int
73 | */
74 | public function updateBowerJsonFile2(array $old, array $new);
75 |
76 | /**
77 | * Get content from project's bower.json file
78 | *
79 | * @return array
80 | *
81 | * @throws \Exception if bower.json does not exist
82 | */
83 | public function getBowerFileContent();
84 |
85 | /**
86 | * Retrieve the array of overrides optionally defined in the bower.json file.
87 | * Each element's key is a package name, and contains an array of other package names
88 | * and versions that should replace the dependencies found in that package's canonical bower.json
89 | *
90 | * @return array The overrides section from the bower.json file, or an empty array if no overrides section is defined
91 | */
92 | public function getOverridesSection();
93 |
94 | /**
95 | * Get the array of overrides defined for the specified package
96 | *
97 | * @param string $packageName The name of the package for which dependencies are being overridden
98 | *
99 | * @return array A list of dependency name => override versions to be used instead of the target package's normal dependencies. An empty array if none are defined
100 | */
101 | public function getOverrideFor($packageName);
102 |
103 | /**
104 | * Get content from a packages' bower.json file
105 | *
106 | * @param PackageInterface $package
107 | *
108 | * @return array
109 | *
110 | * @throws \Exception if bower.json or package.json does not exist in a dir of installed package
111 | */
112 | public function getPackageBowerFileContent(PackageInterface $package);
113 |
114 | /**
115 | * Check if project's bower.json file exists
116 | *
117 | * @return bool
118 | */
119 | public function bowerFileExists();
120 | }
121 |
--------------------------------------------------------------------------------
/src/Bowerphp/Package/Package.php:
--------------------------------------------------------------------------------
1 | name = $name;
36 | $this->requiredVersion = 'master' === $requiredVersion ? '*' : $requiredVersion;
37 | $this->version = $version;
38 | if (!empty($requires)) {
39 | $this->requires = $requires;
40 | }
41 | if (!empty($info)) {
42 | $this->info = $info;
43 | }
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | public function getName()
50 | {
51 | return $this->name;
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | public function getVersion()
58 | {
59 | return $this->version;
60 | }
61 |
62 | /**
63 | * {@inheritdoc}
64 | */
65 | public function setVersion($version)
66 | {
67 | return $this->version = $version;
68 | }
69 |
70 | /**
71 | * {@inheritdoc}
72 | */
73 | public function getRequiredVersion()
74 | {
75 | return $this->requiredVersion;
76 | }
77 |
78 | /**
79 | * {@inheritdoc}
80 | */
81 | public function setRequiredVersion($version)
82 | {
83 | return $this->requiredVersion = $version;
84 | }
85 |
86 | /**
87 | * {@inheritdoc}
88 | */
89 | public function setRepository(RepositoryInterface $repository)
90 | {
91 | if ($this->repository && $repository !== $this->repository) {
92 | throw new \LogicException('A package can only be added to one repository');
93 | }
94 | $this->repository = $repository;
95 | }
96 |
97 | /**
98 | * Returns package unique name, constructed from name, version and release type.
99 | *
100 | * @return string
101 | */
102 | public function getUniqueName()
103 | {
104 | return $this->getName() . '-' . $this->getVersion();
105 | }
106 |
107 | /**
108 | * {@inheritdoc}
109 | */
110 | public function setRequires(array $requires = null)
111 | {
112 | $this->requires = $requires;
113 | }
114 |
115 | /**
116 | * {@inheritdoc}
117 | */
118 | public function getRequires()
119 | {
120 | // see if there is some inside $this->info (e.g. from bower.json)
121 | if (empty($this->requires) && isset($this->info['dependencies'])) {
122 | $this->requires = $this->info['dependencies'];
123 | }
124 |
125 | return $this->requires;
126 | }
127 |
128 | /**
129 | * {@inheritdoc}
130 | */
131 | public function setInfo(array $info)
132 | {
133 | $this->info = $info;
134 | }
135 |
136 | /**
137 | * {@inheritdoc}
138 | */
139 | public function getInfo()
140 | {
141 | return $this->info;
142 | }
143 |
144 | /**
145 | * Converts the package into a readable and unique string
146 | *
147 | * @return string
148 | */
149 | public function __toString()
150 | {
151 | return $this->getUniqueName();
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/Bowerphp/Output/BowerphpConsoleOutput.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Output;
13 |
14 | use Bowerphp\Bowerphp;
15 | use Bowerphp\Package\PackageInterface;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 |
18 | class BowerphpConsoleOutput
19 | {
20 | protected $output;
21 |
22 | /**
23 | * @param OutputInterface $output
24 | */
25 | public function __construct(OutputInterface $output)
26 | {
27 | $this->output = $output;
28 | }
29 |
30 | /**
31 | * writelnInfoPackage
32 | *
33 | * @param PackageInterface $package
34 | * @param string $action
35 | * @param string $message
36 | */
37 | public function writelnInfoPackage(PackageInterface $package, $action = '', $message = '')
38 | {
39 | $this->output->writeln(sprintf('bower %s %s %s',
40 | str_pad($package->getName() . '#' . $package->getRequiredVersion(), 21, ' ', STR_PAD_RIGHT),
41 | str_pad($action, 10, ' ', STR_PAD_LEFT),
42 | $message
43 | ));
44 | }
45 |
46 | /**
47 | * writelnInstalledPackage
48 | *
49 | * @param PackageInterface $package
50 | */
51 | public function writelnInstalledPackage(PackageInterface $package)
52 | {
53 | $this->output->writeln(sprintf('bower %s %s',
54 | str_pad($package->getName() . '#' . $package->getVersion(), 21, ' ', STR_PAD_RIGHT),
55 | str_pad('install', 10, ' ', STR_PAD_LEFT)
56 | ));
57 | }
58 |
59 | /**
60 | * writelnNoBowerJsonFile
61 | */
62 | public function writelnNoBowerJsonFile()
63 | {
64 | $this->output->writeln(sprintf(
65 | 'bower %s %s %s',
66 | str_pad('', 21, ' ', STR_PAD_RIGHT),
67 | str_pad('no-json', 10, ' ', STR_PAD_LEFT),
68 | 'No bower.json file to save to, use bower init to create one'
69 | ));
70 | }
71 |
72 | /**
73 | * Rewrite json with colors and unescaped slashes
74 | *
75 | * @param string $jsonString
76 | */
77 | public function writelnJson($jsonString)
78 | {
79 | $keyColor = preg_replace('/"(\w+)": /', '$1: ', $jsonString);
80 | $valColor = preg_replace('/"([^"]+)"/', "'$1'", $keyColor);
81 |
82 | $this->output->writeln(stripslashes($valColor));
83 | }
84 |
85 | /**
86 | * writelnJson
87 | *
88 | * @param mixed $jsonPart
89 | */
90 | public function writelnJsonText($jsonPart)
91 | {
92 | $this->output->writeln(sprintf('%s', json_encode($jsonPart, JSON_PRETTY_PRINT)));
93 | }
94 |
95 | /**
96 | * writelnSearchOrLookup
97 | *
98 | * @param string $name
99 | * @param string $homepage
100 | * @param int $pad
101 | */
102 | public function writelnSearchOrLookup($name, $homepage, $pad = 0)
103 | {
104 | $this->output->writeln(sprintf('%s%s %s', str_repeat(' ', $pad), $name, $homepage));
105 | }
106 |
107 | /**
108 | * writelnListPackage
109 | *
110 | * @param PackageInterface $package
111 | * @param Bowerphp $bowerphp
112 | */
113 | public function writelnListPackage(PackageInterface $package, Bowerphp $bowerphp)
114 | {
115 | $this->output->writeln(sprintf('%s#%s%s',
116 | $package->getName(),
117 | $package->getVersion(),
118 | $bowerphp->isPackageExtraneous($package) ? ' extraneous' : ''
119 | ));
120 | }
121 |
122 | public function writelnUpdatingPackage(PackageInterface $package)
123 | {
124 | $this->output->writeln(sprintf('bower %s %s',
125 | str_pad($package->getName() . '#' . $package->getRequiredVersion(), 21, ' ', STR_PAD_RIGHT),
126 | str_pad('install', 10, ' ', STR_PAD_LEFT)
127 | ));
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/Bowerphp/Command/InstallCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Command;
13 |
14 | use Bowerphp\Installer\Installer;
15 | use Bowerphp\Package\Package;
16 | use Bowerphp\Repository\GithubRepository;
17 | use Bowerphp\Util\PackageNameVersionExtractor;
18 | use Bowerphp\Util\ZipArchive;
19 | use Symfony\Component\Console\Input\InputArgument;
20 | use Symfony\Component\Console\Input\InputInterface;
21 | use Symfony\Component\Console\Input\InputOption;
22 | use Symfony\Component\Console\Output\OutputInterface;
23 |
24 | /**
25 | * Inspired by Composer https://github.com/composer/composer
26 | */
27 | class InstallCommand extends Command
28 | {
29 | /**
30 | * {@inheritdoc}
31 | */
32 | protected function configure()
33 | {
34 | $this
35 | ->setName('install')
36 | ->setDescription('Installs the project dependencies from the bower.json file or a single specified package')
37 | ->addOption('save', 'S', InputOption::VALUE_NONE, 'Add installed package to bower.json file.')
38 | ->addArgument('package', InputArgument::OPTIONAL, 'Choose a package.')
39 | ->setHelp(<<<'EOT'
40 | The %command.name% command reads the bower.json file from
41 | the current directory, processes it, and downloads and installs all the
42 | libraries and dependencies outlined in that file.
43 |
44 | php %command.full_name%
45 |
46 | If an optional package name is passed, that package is installed.
47 |
48 | php %command.full_name% packageName[#version]
49 |
50 | If an optional flag -S is passed, installed package is added
51 | to bower.json file (only if bower.json file already exists).
52 |
53 | EOT
54 | )
55 | ;
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | protected function execute(InputInterface $input, OutputInterface $output)
62 | {
63 | $this->setGithubToken($output);
64 | $this->config->setSaveToBowerJsonFile($input->getOption('save'));
65 |
66 | $packageName = $input->getArgument('package');
67 |
68 | $bowerphp = $this->getBowerphp($output);
69 |
70 | try {
71 | $installer = new Installer($this->filesystem, new ZipArchive(), $this->config);
72 |
73 | if (is_null($packageName)) {
74 | $bowerphp->installDependencies($installer);
75 | } else {
76 | if ('bower.json' === substr($packageName, -10)) {
77 | if (!is_readable($packageName)) {
78 | $output->writeln(sprintf('Cannot read file %s', $packageName));
79 |
80 | return 1;
81 | }
82 | $json = json_decode($this->filesystem->read($packageName), true);
83 | if (empty($json['dependencies'])) {
84 | $output->writeln(sprintf('Nothing to install in %s', $packageName));
85 |
86 | return 1;
87 | }
88 | foreach ($json['dependencies'] as $name => $version) {
89 | $package = new Package($name, $version);
90 | $bowerphp->installPackage($package, $installer);
91 | }
92 | } else {
93 | $packageNameVersion = PackageNameVersionExtractor::fromString($packageName);
94 | $package = new Package($packageNameVersion->name, $packageNameVersion->version);
95 | $bowerphp->installPackage($package, $installer);
96 | }
97 | }
98 | } catch (\RuntimeException $e) {
99 | $output->writeln(sprintf('%s', $e->getMessage()));
100 | if (GithubRepository::VERSION_NOT_FOUND == $e->getCode() && !empty($package)) {
101 | $output->writeln(sprintf('Available versions: %s', implode(', ', $bowerphp->getPackageInfo($package, 'versions'))));
102 | }
103 |
104 | return 1;
105 | }
106 |
107 | $output->writeln('');
108 |
109 | return 0;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Bowerphp/Console/Application.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Console;
13 |
14 | use Bowerphp\Command;
15 | use Bowerphp\Command\Helper\QuestionHelper;
16 | use Bowerphp\Util\ErrorHandler;
17 | use PackageVersions\Versions;
18 | use Symfony\Component\Console\Application as BaseApplication;
19 | use Symfony\Component\Console\Input\ArrayInput;
20 | use Symfony\Component\Console\Input\InputInterface;
21 | use Symfony\Component\Console\Input\InputOption;
22 | use Symfony\Component\Console\Output\OutputInterface;
23 |
24 | /**
25 | * The console application that handles the commands
26 | * Inspired by Composer https://github.com/composer/composer
27 | */
28 | class Application extends BaseApplication
29 | {
30 | /**
31 | * @var \Bowerphp\Bowerphp
32 | */
33 | protected $bowerphp;
34 |
35 | private static $logo = ' ____ __
36 | / __ )____ _ _____ _________ / /_ ____
37 | / __ / __ \ | /| / / _ \/ ___/ __ \/ __ \/ __ \
38 | / /_/ / /_/ / |/ |/ / __/ / / /_/ / / / / /_/ /
39 | /_____/\____/|__/|__/\___/_/ / .___/_/ /_/ .___/
40 | /_/ /_/
41 | ';
42 |
43 | /**
44 | * Constructor
45 | */
46 | public function __construct()
47 | {
48 | ErrorHandler::register();
49 | $version = Versions::getVersion('beelab/bowerphp');
50 | parent::__construct('Bowerphp', $version . ' Powered by BeeLab (github.com/bee-lab)');
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function doRun(InputInterface $input, OutputInterface $output)
57 | {
58 | if ($input->hasParameterOption('--profile')) {
59 | $startTime = microtime(true);
60 | }
61 |
62 | if ($newWorkDir = $this->getNewWorkingDir($input)) {
63 | $oldWorkingDir = getcwd();
64 | chdir($newWorkDir);
65 | }
66 |
67 | $result = $this->symfonyDoRun($input, $output);
68 |
69 | if (isset($oldWorkingDir)) {
70 | chdir($oldWorkingDir);
71 | }
72 |
73 | if (isset($startTime)) {
74 | $output->writeln('Memory usage: ' . round(memory_get_usage() / 1024 / 1024, 2) . 'MB (peak: ' . round(memory_get_peak_usage() / 1024 / 1024, 2) . 'MB), time: ' . round(microtime(true) - $startTime, 2) . 's');
75 | }
76 |
77 | return $result;
78 | }
79 |
80 | /**
81 | * {@inheritdoc}
82 | */
83 | public function getHelp()
84 | {
85 | return self::$logo . parent::getHelp();
86 | }
87 |
88 | /**
89 | * {@inheritdoc}
90 | */
91 | protected function getDefaultCommands()
92 | {
93 | return [
94 | new Command\HelpCommand(),
95 | new Command\CommandListCommand(),
96 | new Command\HomeCommand(),
97 | new Command\InfoCommand(),
98 | new Command\InitCommand(),
99 | new Command\InstallCommand(),
100 | new Command\ListCommand(),
101 | new Command\LookupCommand(),
102 | new Command\SearchCommand(),
103 | new Command\UpdateCommand(),
104 | new Command\UninstallCommand(),
105 | ];
106 | }
107 |
108 | /**
109 | * {@inheritdoc}
110 | */
111 | protected function getDefaultInputDefinition()
112 | {
113 | $definition = parent::getDefaultInputDefinition();
114 | $definition->addOption(new InputOption('--profile', null, InputOption::VALUE_NONE, 'Display timing and memory usage information'));
115 | $definition->addOption(new InputOption('--working-dir', '-d', InputOption::VALUE_REQUIRED, 'If specified, use the given directory as working directory.'));
116 |
117 | return $definition;
118 | }
119 |
120 | /**
121 | * {@inheritdoc}
122 | */
123 | protected function getDefaultHelperSet()
124 | {
125 | $helperSet = parent::getDefaultHelperSet();
126 | $helperSet->set(new QuestionHelper());
127 |
128 | return $helperSet;
129 | }
130 |
131 | /**
132 | * @param InputInterface $input
133 | * @return string
134 | * @throws \RuntimeException
135 | */
136 | private function getNewWorkingDir(InputInterface $input)
137 | {
138 | $workingDir = $input->getParameterOption(['--working-dir', '-d']);
139 | if (false !== $workingDir && !is_dir($workingDir)) {
140 | throw new \RuntimeException('Invalid working directory specified.');
141 | }
142 |
143 | return $workingDir;
144 | }
145 |
146 | /**
147 | * Copy of original Symfony doRun, to allow a default command
148 | *
149 | * @param InputInterface $input An Input instance
150 | * @param OutputInterface $output An Output instance
151 | * @param string $default Default command to execute
152 | *
153 | * @return int 0 if everything went fine, or an error code
154 | * @codeCoverageIgnore
155 | */
156 | private function symfonyDoRun(InputInterface $input, OutputInterface $output, $default = 'list-commands')
157 | {
158 | if (true === $input->hasParameterOption(['--version', '-V'])) {
159 | $output->writeln($this->getLongVersion());
160 |
161 | return 0;
162 | }
163 |
164 | $name = $this->getCommandName($input);
165 |
166 | if (true === $input->hasParameterOption(['--help', '-h'])) {
167 | if (!$name) {
168 | $name = 'help';
169 | $input = new ArrayInput(['command' => 'help']);
170 | } else {
171 | // TODO $wantHelps is private in parent class
172 | $this->wantHelps = true;
173 | }
174 | }
175 |
176 | if (!$name) {
177 | $name = $default;
178 | $input = new ArrayInput(['command' => $default]);
179 | }
180 |
181 | // the command name MUST be the first element of the input
182 | $command = $this->find($name);
183 |
184 | $this->runningCommand = $command;
185 | $exitCode = $this->doRunCommand($command, $input, $output);
186 | $this->runningCommand = null;
187 |
188 | return $exitCode;
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/Bowerphp/Compiler.php:
--------------------------------------------------------------------------------
1 |
7 | * (c) Mauro D'Alatri
8 | *
9 | * For the full copyright and license information, please view the LICENSE
10 | * file that was distributed with this source code.
11 | */
12 |
13 | namespace Bowerphp;
14 |
15 | use Symfony\Component\Finder\Finder;
16 |
17 | /**
18 | * The Compiler class compiles Bower into a phar.
19 | */
20 | class Compiler
21 | {
22 | /**
23 | * @var bool
24 | */
25 | private $gz;
26 |
27 | /**
28 | * @param bool $gz
29 | */
30 | public function __construct($gz = false)
31 | {
32 | $this->gz = $gz;
33 | }
34 |
35 | /**
36 | * Compiles bower into a single phar file
37 | *
38 | * @throws \RuntimeException
39 | * @param string $pharFile The full path to the file to create
40 | */
41 | public function compile($pharFile = 'bowerphp.phar')
42 | {
43 | if (file_exists($pharFile)) {
44 | unlink($pharFile);
45 | }
46 |
47 | $phar = new \Phar($pharFile, 0, 'bowerphp.phar');
48 | $phar->setSignatureAlgorithm(\Phar::SHA1);
49 |
50 | $phar->startBuffering();
51 |
52 | $finder = new Finder();
53 | $finder->files()
54 | ->ignoreVCS(true)
55 | ->name('*.php')
56 | ->notName('Compiler.php')
57 | ->notName('ClassLoader.php')
58 | ->in(__DIR__ . '/..')
59 | ;
60 |
61 | foreach ($finder as $file) {
62 | $this->addFile($phar, $file);
63 | }
64 |
65 | $finder = new Finder();
66 | $finder->files()
67 | ->ignoreVCS(true)
68 | ->name('*.php')
69 | ->name('*.pem')
70 | ->name('*.pem.md5')
71 | ->exclude('Tests')
72 | ->in(__DIR__ . '/../../vendor/symfony/')
73 | ->in(__DIR__ . '/../../vendor/guzzle/guzzle/src/')
74 | ->in(__DIR__ . '/../../vendor/ircmaxell/password-compat/lib/')
75 | ->in(__DIR__ . '/../../vendor/paragonie/random_compat/lib/')
76 | ->in(__DIR__ . '/../../vendor/knplabs/github-api/lib/')
77 | ->in(__DIR__ . '/../../vendor/samsonasik/package-versions/src/PackageVersions/')
78 | ->in(__DIR__ . '/../../vendor/vierbergenlars/php-semver/src/vierbergenlars/LibJs/')
79 | ->in(__DIR__ . '/../../vendor/vierbergenlars/php-semver/src/vierbergenlars/SemVer/')
80 | ->in(__DIR__ . '/../../vendor/hamcrest/hamcrest-php/hamcrest/')
81 | ;
82 |
83 | foreach ($finder as $file) {
84 | $this->addFile($phar, $file);
85 | }
86 |
87 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/autoload.php'));
88 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/autoload_psr4.php'));
89 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/autoload_files.php'));
90 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/autoload_namespaces.php'));
91 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/autoload_classmap.php'));
92 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/autoload_real.php'));
93 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/autoload_static.php'));
94 | if (file_exists(__DIR__ . '/../../vendor/composer/include_paths.php')) {
95 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/include_paths.php'));
96 | }
97 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../vendor/composer/ClassLoader.php'));
98 | $this->addBin($phar);
99 |
100 | // Stubs
101 | $phar->setStub($this->getStub());
102 |
103 | $phar->stopBuffering();
104 |
105 | if ($this->gz) {
106 | $phar->compressFiles(\Phar::GZ);
107 | }
108 |
109 | $this->addFile($phar, new \SplFileInfo(__DIR__ . '/../../LICENSE'), false);
110 |
111 | unset($phar);
112 | chmod('bowerphp.phar', 0700);
113 | }
114 |
115 | /**
116 | * @param \Phar $phar
117 | * @param \SplFileInfo $file
118 | * @param bool $strip
119 | */
120 | private function addFile(\Phar $phar, \SplFileInfo $file, $strip = true)
121 | {
122 | $path = strtr(str_replace(dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR, '', $file->getRealPath()), '\\', '/');
123 |
124 | $content = file_get_contents($file);
125 | if ($strip) {
126 | $content = $this->stripWhitespace($content);
127 | } elseif ('LICENSE' === basename($file)) {
128 | $content = "\n" . $content . "\n";
129 | }
130 |
131 | $phar->addFromString($path, $content);
132 | }
133 |
134 | /**
135 | * @param \Phar $phar
136 | */
137 | private function addBin(\Phar $phar)
138 | {
139 | $content = file_get_contents(__DIR__ . '/../../bin/bowerphp');
140 | $content = preg_replace('{^#!/usr/bin/env php\s*}', '', $content);
141 | $phar->addFromString('bin/bowerphp', $content);
142 | }
143 |
144 | /**
145 | * Removes whitespace from a PHP source string while preserving line numbers.
146 | *
147 | * @param string $source A PHP string
148 | * @return string The PHP string with the whitespace removed
149 | */
150 | private function stripWhitespace($source)
151 | {
152 | if (!function_exists('token_get_all')) {
153 | return $source;
154 | }
155 |
156 | $output = '';
157 | foreach (token_get_all($source) as $token) {
158 | if (is_string($token)) {
159 | $output .= $token;
160 | } elseif (in_array($token[0], [T_COMMENT, T_DOC_COMMENT], true)) {
161 | $output .= str_repeat("\n", substr_count($token[1], "\n"));
162 | } elseif (T_WHITESPACE === $token[0]) {
163 | // reduce wide spaces
164 | $whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
165 | // normalize newlines to \n
166 | $whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
167 | // trim leading spaces
168 | $whitespace = preg_replace('{\n +}', "\n", $whitespace);
169 | $output .= $whitespace;
170 | } else {
171 | $output .= $token[1];
172 | }
173 | }
174 |
175 | return $output;
176 | }
177 |
178 | /**
179 | * @return string
180 | */
181 | private function getStub()
182 | {
183 | $stub = <<<'EOF'
184 | #!/usr/bin/env php
185 |
190 | * Mauro D'Alatri
191 | *
192 | * For the full copyright and license information, please view
193 | * the license that is located at the bottom of this file.
194 | */
195 |
196 | Phar::mapPhar('bowerphp.phar');
197 |
198 | EOF;
199 |
200 | return $stub . <<<'EOF'
201 | require 'phar://bowerphp.phar/bin/bowerphp';
202 |
203 | __HALT_COMPILER();
204 | EOF;
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/src/Bowerphp/Config/Config.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp\Config;
13 |
14 | use Bowerphp\Package\PackageInterface;
15 | use Bowerphp\Util\Filesystem;
16 | use RuntimeException;
17 |
18 | /**
19 | * Config
20 | */
21 | class Config implements ConfigInterface
22 | {
23 | protected $cacheDir;
24 |
25 | protected $installDir;
26 |
27 | protected $filesystem;
28 |
29 | protected $basePackagesUrl = 'http://registry.bower.io/packages/';
30 |
31 | protected $saveToBowerJsonFile = false;
32 |
33 | protected $bowerFileNames = ['bower.json', 'package.json'];
34 |
35 | protected $stdBowerFileName = 'bower.json';
36 |
37 | /**
38 | * @param Filesystem $filesystem
39 | */
40 | public function __construct(Filesystem $filesystem)
41 | {
42 | $this->filesystem = $filesystem;
43 | $this->cacheDir = $this->getHomeDir() . '/.cache/bowerphp';
44 | $this->installDir = getcwd() . '/bower_components';
45 | $rc = getcwd() . '/.bowerrc';
46 |
47 | if ($this->filesystem->exists($rc)) {
48 | $json = json_decode($this->filesystem->read($rc), true);
49 | if (is_null($json)) {
50 | throw new RuntimeException('Invalid .bowerrc file.');
51 | }
52 | if (isset($json['directory'])) {
53 | $this->installDir = getcwd() . '/' . $json['directory'];
54 | }
55 | if (isset($json['storage']) && isset($json['storage']['packages'])) {
56 | $this->cacheDir = $json['storage']['packages'];
57 | }
58 | }
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | */
64 | public function getBasePackagesUrl()
65 | {
66 | return $this->basePackagesUrl;
67 | }
68 |
69 | /**
70 | * {@inheritdoc}
71 | */
72 | public function getCacheDir()
73 | {
74 | return $this->cacheDir;
75 | }
76 |
77 | /**
78 | * {@inheritdoc}
79 | */
80 | public function getInstallDir()
81 | {
82 | return $this->installDir;
83 | }
84 |
85 | /**
86 | * {@inheritdoc}
87 | */
88 | public function isSaveToBowerJsonFile()
89 | {
90 | return $this->saveToBowerJsonFile;
91 | }
92 |
93 | /**
94 | * {@inheritdoc}
95 | */
96 | public function setSaveToBowerJsonFile($flag = true)
97 | {
98 | $this->saveToBowerJsonFile = $flag;
99 | }
100 |
101 | /**
102 | * {@inheritdoc}
103 | */
104 | public function initBowerJsonFile(array $params)
105 | {
106 | $file = getcwd() . '/' . $this->stdBowerFileName;
107 | $json = json_encode($this->createAClearBowerFile($params), JSON_PRETTY_PRINT);
108 |
109 | return $this->filesystem->write($file, $json);
110 | }
111 |
112 | /**
113 | * {@inheritdoc}
114 | */
115 | public function updateBowerJsonFile(PackageInterface $package)
116 | {
117 | if (!$this->isSaveToBowerJsonFile()) {
118 | return 0;
119 | }
120 |
121 | $decode = $this->getBowerFileContent();
122 | $decode['dependencies'][$package->getName()] = $package->getRequiredVersion();
123 | $file = getcwd() . '/' . $this->stdBowerFileName;
124 | $json = json_encode($decode, JSON_PRETTY_PRINT);
125 |
126 | return $this->filesystem->write($file, $json);
127 | }
128 |
129 | /**
130 | * {@inheritdoc}
131 | */
132 | public function updateBowerJsonFile2(array $old, array $new)
133 | {
134 | $json = json_encode(array_merge($old, $new), JSON_PRETTY_PRINT);
135 | $file = getcwd() . '/' . $this->stdBowerFileName;
136 |
137 | return $this->filesystem->write($file, $json);
138 | }
139 |
140 | /**
141 | * {@inheritdoc}
142 | */
143 | public function getBowerFileContent()
144 | {
145 | if (!$this->bowerFileExists()) {
146 | throw new RuntimeException('No ' . $this->stdBowerFileName . ' found. You can run "init" command to create it.');
147 | }
148 | $bowerJson = $this->filesystem->read(getcwd() . '/' . $this->stdBowerFileName);
149 | if (empty($bowerJson) || !is_array($decode = json_decode($bowerJson, true))) {
150 | throw new RuntimeException(sprintf('Malformed JSON in %s: %s.', $this->stdBowerFileName, $bowerJson));
151 | }
152 |
153 | return $decode;
154 | }
155 |
156 | /**
157 | * {@inheritdoc}
158 | */
159 | public function getOverridesSection()
160 | {
161 | if ($this->bowerFileExists()) {
162 | $bowerData = $this->getBowerFileContent();
163 | if ($bowerData && array_key_exists('overrides', $bowerData)) {
164 | return $bowerData['overrides'];
165 | }
166 | }
167 |
168 | return [];
169 | }
170 |
171 | /**
172 | * {@inheritdoc}
173 | */
174 | public function getOverrideFor($packageName)
175 | {
176 | $overrides = $this->getOverridesSection();
177 | if (array_key_exists($packageName, $overrides)) {
178 | return $overrides[$packageName];
179 | }
180 |
181 | return [];
182 | }
183 |
184 | /**
185 | * {@inheritdoc}
186 | */
187 | public function getPackageBowerFileContent(PackageInterface $package)
188 | {
189 | $file = $this->getInstallDir() . '/' . $package->getName() . '/.bower.json';
190 | if (!$this->filesystem->exists($file)) {
191 | throw new RuntimeException(sprintf('Could not find .bower.json file for package %s.', $package->getName()));
192 | }
193 | $bowerJson = $this->filesystem->read($file);
194 | $bower = json_decode($bowerJson, true);
195 | if (is_null($bower)) {
196 | throw new RuntimeException(sprintf('Invalid content in .bower.json for package %s.', $package->getName()));
197 | }
198 |
199 | return $bower;
200 | }
201 |
202 | /**
203 | * {@inheritdoc}
204 | */
205 | public function bowerFileExists()
206 | {
207 | return $this->filesystem->exists(getcwd() . '/' . $this->stdBowerFileName);
208 | }
209 |
210 | /**
211 | * @param array $params
212 | * @return array
213 | */
214 | protected function createAClearBowerFile(array $params)
215 | {
216 | $structure = [
217 | 'name' => $params['name'],
218 | 'authors' => [
219 | 0 => 'Beelab ',
220 | 1 => $params['author'],
221 | ],
222 | 'private' => true,
223 | 'dependencies' => new \StdClass(),
224 | ];
225 |
226 | return $structure;
227 | }
228 |
229 | /**
230 | * @return string
231 | */
232 | protected function getHomeDir()
233 | {
234 | if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
235 | $appData = getenv('APPDATA');
236 | if (empty($appData)) {
237 | throw new \RuntimeException('The APPDATA environment variable must be set for bowerphp to run correctly');
238 | }
239 |
240 | return strtr($appData, '\\', '/');
241 | }
242 | $home = getenv('HOME');
243 | if (empty($home)) {
244 | throw new \RuntimeException('The HOME environment variable must be set for bowerphp to run correctly');
245 | }
246 |
247 | return rtrim($home, '/');
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/src/Bowerphp/Installer/Installer.php:
--------------------------------------------------------------------------------
1 | filesystem = $filesystem;
43 | $this->zipArchive = $zipArchive;
44 | $this->config = $config;
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function install(PackageInterface $package)
51 | {
52 | $tmpFileName = $this->config->getCacheDir() . '/tmp/' . $package->getName();
53 | if (true !== $this->zipArchive->open($tmpFileName)) {
54 | throw new RuntimeException(sprintf('Unable to open zip file %s.', $tmpFileName));
55 | }
56 | $dirName = trim($this->zipArchive->getNameIndex(0), '/');
57 | $info = $package->getInfo();
58 | $files = $this->filterZipFiles($this->zipArchive, isset($info['ignore']) ? $info['ignore'] : [], isset($info['main']) ? (array) $info['main'] : []);
59 | foreach ($files as $i => $file) {
60 | $stat = $this->zipArchive->statIndex($i);
61 | $fileName = $this->config->getInstallDir() . '/' . str_replace($dirName, $package->getName(), $file);
62 | if ('/' != substr($fileName, -1)) {
63 | $fileContent = $this->zipArchive->getStream($file);
64 | $this->filesystem->write($fileName, $fileContent);
65 | $this->filesystem->touch($fileName, $stat['mtime']);
66 | }
67 | }
68 | // adjust timestamp for directories
69 | foreach ($files as $i => $file) {
70 | $stat = $this->zipArchive->statIndex($i);
71 | $fileName = $this->config->getInstallDir() . '/' . str_replace($dirName, $package->getName(), $file);
72 | if (is_dir($fileName) && '/' == substr($fileName, -1)) {
73 | $this->filesystem->touch($fileName, $stat['mtime']);
74 | }
75 | }
76 | $this->zipArchive->close();
77 |
78 | // merge package info with bower.json contents
79 | $bowerJsonPath = $this->config->getInstallDir() . '/' . $package->getName() . '/bower.json';
80 | if ($this->filesystem->exists($bowerJsonPath)) {
81 | $bowerJson = $this->filesystem->read($bowerJsonPath);
82 | $bower = json_decode($bowerJson, true);
83 | $package->setInfo(array_merge($bower, $package->getInfo()));
84 | }
85 |
86 | // create .bower.json metadata file
87 | // XXX we still need to add some other info
88 | $dotBowerContent = array_merge($package->getInfo(), ['version' => $package->getVersion()]);
89 | $dotBowerJson = str_replace('\/', '/', json_encode($dotBowerContent, JSON_PRETTY_PRINT));
90 | $this->filesystem->write($this->config->getInstallDir() . '/' . $package->getName() . '/.bower.json', $dotBowerJson);
91 | }
92 |
93 | /**
94 | * {@inheritdoc}
95 | */
96 | public function update(PackageInterface $package)
97 | {
98 | $this->install($package);
99 | }
100 |
101 | /**
102 | * {@inheritdoc}
103 | */
104 | public function uninstall(PackageInterface $package)
105 | {
106 | $this->removeDir($this->config->getInstallDir() . '/' . $package->getName());
107 | }
108 |
109 | /**
110 | * {@inheritdoc}
111 | */
112 | public function getInstalled(Finder $finder)
113 | {
114 | $packages = [];
115 | if (!$this->filesystem->exists($this->config->getInstallDir())) {
116 | return $packages;
117 | }
118 |
119 | $directories = $finder->directories()->in($this->config->getInstallDir());
120 |
121 | foreach ($directories as $packageDirectory) {
122 | if ($this->filesystem->exists($packageDirectory . '/.bower.json')) {
123 | $bowerJson = $this->filesystem->read($packageDirectory . '/.bower.json');
124 | $bower = json_decode($bowerJson, true);
125 | if (is_null($bower)) {
126 | throw new RuntimeException(sprintf('Invalid content in .bower.json for package %s.', $packageDirectory));
127 | }
128 | $packages[] = new Package($bower['name'], null, $bower['version'], isset($bower['dependencies']) ? $bower['dependencies'] : null, $bower);
129 | }
130 | }
131 |
132 | return $packages;
133 | }
134 |
135 | /**
136 | * {@inheritdoc}
137 | */
138 | public function findDependentPackages(PackageInterface $package, Finder $finder)
139 | {
140 | $return = [];
141 | $packages = $this->getInstalled($finder);
142 | foreach ($packages as $installedPackage) {
143 | $requires = $installedPackage->getRequires();
144 | if (isset($requires[$package->getName()])) {
145 | $return[$requires[$package->getName()]] = $installedPackage;
146 | }
147 | }
148 |
149 | return $return;
150 | }
151 |
152 | /**
153 | * Filter archive files based on an "ignore" list.
154 | *
155 | * @param ZipArchive $archive
156 | * @param array $ignore
157 | * @param array $force
158 | * @return array
159 | */
160 | protected function filterZipFiles(ZipArchive $archive, array $ignore = [], array $force = [])
161 | {
162 | $dirName = $archive->getNameIndex(0);
163 | $return = [];
164 | $numFiles = $archive->getNumFiles();
165 | for ($i = 0; $i < $numFiles; ++$i) {
166 | $stat = $archive->statIndex($i);
167 | $return[] = $stat['name'];
168 | }
169 | $that = $this;
170 | $filter = array_filter($return, function ($var) use ($ignore, $force, $dirName, $that) {
171 | return !$that->isIgnored($var, $ignore, $force, $dirName);
172 | });
173 |
174 | return array_values($filter);
175 | }
176 |
177 | /**
178 | * @param string $dir
179 | */
180 | protected function removeDir($dir)
181 | {
182 | $this->filesystem->remove($dir);
183 | }
184 |
185 | /**
186 | * Check if a file should be ignored
187 | *
188 | * @param string $name file's name
189 | * @param array $ignore list of ignores
190 | * @param array $force list of files to force (do not ignore)
191 | * @param string $dirName dir's name (to be removed from file's name)
192 | * @return bool
193 | */
194 | public function isIgnored($name, array $ignore, array $force, $dirName)
195 | {
196 | $vName = substr($name, strlen($dirName));
197 | if (in_array($vName, $force, true)) {
198 | return false;
199 | }
200 | // first check if there is line that overrides other lines
201 | foreach ($ignore as $pattern) {
202 | if (0 !== strpos($pattern, '!')) {
203 | continue;
204 | }
205 | $pattern = ltrim($pattern, '!');
206 | // the ! negates the line, otherwise the syntax is the same
207 | if ($this->isIgnored($name, [$pattern], $force, $dirName)) {
208 | return false;
209 | }
210 | }
211 | foreach ($ignore as $pattern) {
212 | if (false !== strpos($pattern, '**')) {
213 | $pattern = str_replace('**', '*', $pattern);
214 | //$pattern = str_replace('*/*', '*', $pattern);
215 | if ('/' == substr($pattern, 0, 1)) {
216 | $vName = '/' . $vName;
217 | }
218 | if ('.' == substr($vName, 0, 1)) {
219 | $vName = '/' . $vName;
220 | }
221 | if (fnmatch($pattern, $vName, FNM_PATHNAME)) {
222 | return true;
223 | } elseif ('*/*' === $pattern && fnmatch('*', $vName, FNM_PATHNAME)) {
224 | // this a special case, where a double asterisk must match also files in the root dir
225 | return true;
226 | }
227 | } elseif ('/' == substr($pattern, -1)) { // trailing slash
228 | if ('/' == substr($pattern, 0, 1)) {
229 | $pattern = substr($pattern, 1); // remove possible starting slash
230 | }
231 | $escPattern = str_replace(['.', '*'], ['\.', '.*'], $pattern);
232 | if (preg_match('#^' . $escPattern . '#', $vName) > 0) {
233 | return true;
234 | }
235 | } elseif (false === strpos($pattern, '/')) { // no slash
236 | $escPattern = str_replace(['.', '*'], ['\.', '.*'], $pattern);
237 | if (preg_match('#^' . $escPattern . '#', $vName) > 0) {
238 | return true;
239 | }
240 | } elseif ('/' == substr($pattern, 0, 1)) { // starting slash
241 | $escPattern = str_replace(['.', '*'], ['\.', '.*'], $pattern);
242 | if (preg_match('#^' . $escPattern . '#', '/' . $vName) > 0) {
243 | return true;
244 | }
245 | } else {
246 | $escPattern = str_replace(['.', '*'], ['\.', '.*'], $pattern);
247 | if (preg_match('#^' . $escPattern . '#', $vName) > 0) {
248 | return true;
249 | }
250 | }
251 | }
252 |
253 | return false;
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/src/Bowerphp/Repository/GithubRepository.php:
--------------------------------------------------------------------------------
1 | null];
26 |
27 | /**
28 | * @var Client
29 | */
30 | protected $githubClient;
31 |
32 | /**
33 | * {@inheritdoc}
34 | *
35 | * @return GithubRepository
36 | */
37 | public function setUrl($url, $raw = true)
38 | {
39 | $url = preg_replace('/\.git$/', '', str_replace('git://', 'https://' . ($raw ? 'raw.' : ''), $url));
40 | $this->url = str_replace('raw.github.com', 'raw.githubusercontent.com', $url);
41 |
42 | return $this;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function getUrl()
49 | {
50 | return $this->url;
51 | }
52 |
53 | /**
54 | * @param Client $githubClient
55 | * @return GithubRepository
56 | */
57 | public function setHttpClient(Client $githubClient)
58 | {
59 | $this->githubClient = $githubClient;
60 | // see https://developer.github.com/changes/2015-04-17-preview-repository-redirects/
61 | $this->githubClient->getHttpClient()->setHeaders(['Accept' => 'application/vnd.github.quicksilver-preview+json']);
62 |
63 | return $this;
64 | }
65 |
66 | /**
67 | * {@inheritdoc}
68 | */
69 | public function getBower($version = 'master', $includeHomepage = false, $url = '')
70 | {
71 | if ('*' == $version) {
72 | $version = 'master';
73 | }
74 | if (!empty($url)) {
75 | // we need to save current $this->url
76 | $oldUrl = $this->url;
77 | // then, we call setUrl(), to get the http url
78 | $this->setUrl($url);
79 | }
80 | $json = $this->getDepBowerJson($version);
81 | if ($includeHomepage) {
82 | $array = json_decode($json, true);
83 | if (!empty($url)) {
84 | // here, we set again original $this->url, to pass it in bower.json
85 | $this->setUrl($oldUrl);
86 | }
87 | $array['homepage'] = $this->url;
88 | $json = json_encode($array, JSON_PRETTY_PRINT);
89 | }
90 |
91 | return $json;
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function findPackage($rawCriteria = '*')
98 | {
99 | list($repoUser, $repoName) = explode('/', $this->clearGitURL($this->url));
100 | $paginator = new ResultPager($this->githubClient);
101 | $tags = $paginator->fetchAll($this->githubClient->api('repo'), 'tags', [$repoUser, $repoName]);
102 |
103 | // edge case: package has no tags
104 | if (0 === count($tags)) {
105 | $this->tag['name'] = 'master';
106 |
107 | return $this->tag['name'];
108 | }
109 |
110 | // edge case: user asked for latest package
111 | if ('latest' == $rawCriteria || '*' == $rawCriteria || empty($rawCriteria)) {
112 | $sortedTags = $this->sortTags($tags);
113 | $this->tag = end($sortedTags);
114 |
115 | return $this->tag['name'];
116 | }
117 |
118 | // edge case for versions with slash (like ckeditor). See also issue #120
119 | if (strpos($rawCriteria, '/') > 0) {
120 | $tagNames = array_column($tags, 'name');
121 | if (false !== $tag = array_search($rawCriteria, $tagNames, true)) {
122 | $this->tag = $tag;
123 |
124 | return $rawCriteria;
125 | }
126 | }
127 |
128 | try {
129 | $criteria = new expression($rawCriteria);
130 | } catch (SemVerException $sve) {
131 | throw new RuntimeException(sprintf('Criteria %s is not valid.', $rawCriteria), self::INVALID_CRITERIA, $sve);
132 | }
133 | $sortedTags = $this->sortTags($tags);
134 |
135 | // Yes, the php-semver lib does offer a maxSatisfying() method similar the code below.
136 | // We're not using it because it will throw an exception on what it considers to be an
137 | // "invalid" candidate version, and not continue checking the rest of the candidates.
138 | // So, even if it's faster than this code, it's not a complete solution.
139 | $matches = array_filter($sortedTags, function ($tag) use ($criteria) {
140 | $candidate = $tag['parsed_version'];
141 |
142 | return $criteria->satisfiedBy($candidate) ? $tag : false;
143 | });
144 |
145 | // If the array has elements, the LAST element is the best (highest numbered) version.
146 | if (count($matches) > 0) {
147 | // @todo Get rid of this side effect?
148 | $this->tag = array_pop($matches);
149 |
150 | return $this->tag['name'];
151 | }
152 |
153 | throw new RuntimeException(sprintf('%s: No suitable version for %s was found.', $repoName, $rawCriteria), self::VERSION_NOT_FOUND);
154 | }
155 |
156 | /**
157 | * {@inheritdoc}
158 | */
159 | public function getRelease($type = 'zip')
160 | {
161 | list($repoUser, $repoName) = explode('/', $this->clearGitURL($this->url));
162 |
163 | return $this->githubClient->api('repo')->contents()->archive($repoUser, $repoName, $type . 'ball', $this->tag['name']);
164 | }
165 |
166 | /**
167 | * {@inheritdoc}
168 | */
169 | public function getTags()
170 | {
171 | list($repoUser, $repoName) = explode('/', $this->clearGitURL($this->url));
172 | $paginator = new ResultPager($this->githubClient);
173 | $tags = $paginator->fetchAll($this->githubClient->api('repo'), 'tags', [$repoUser, $repoName]);
174 | // edge case: no tags
175 | if (0 === count($tags)) {
176 | return [];
177 | }
178 |
179 | $sortedTags = $this->sortTags($tags); // Filters out bad tag specs
180 |
181 | return array_keys($sortedTags);
182 | }
183 |
184 | /**
185 | * Get remote bower.json file (or package.json file)
186 | *
187 | * @param string $version
188 | * @return string
189 | */
190 | private function getDepBowerJson($version)
191 | {
192 | list($repoUser, $repoName) = explode('/', $this->clearGitURL($this->url));
193 | $contents = $this->githubClient->api('repo')->contents();
194 | if ($contents->exists($repoUser, $repoName, 'bower.json', $version)) {
195 | $json = $contents->download($repoUser, $repoName, 'bower.json', $version);
196 | } else {
197 | $isPackageJson = true;
198 | if ($contents->exists($repoUser, $repoName, 'package.json', $version)) {
199 | $json = $contents->download($repoUser, $repoName, 'package.json', $version);
200 | } elseif ('master' != $version) {
201 | return $this->getDepBowerJson('master');
202 | }
203 | // try anyway. E.g. exists() return false for Modernizr, but then it downloads :-|
204 | $json = $contents->download($repoUser, $repoName, 'package.json', $version);
205 | }
206 |
207 | if ("\xef\xbb\xbf" == substr($json, 0, 3)) {
208 | $json = substr($json, 3);
209 | }
210 |
211 | // for package.json, remove dependencies (see the case of Modernizr)
212 | if (isset($isPackageJson)) {
213 | $array = json_decode($json, true);
214 | if (isset($array['dependencies'])) {
215 | unset($array['dependencies']);
216 | }
217 | $json = json_encode($array, JSON_PRETTY_PRINT);
218 | }
219 |
220 | return $json;
221 | }
222 |
223 | /**
224 | * @param string $url
225 | *
226 | * @return string
227 | */
228 | private function clearGitURL($url)
229 | {
230 | $partsToClean = [
231 | 'git://',
232 | 'git@github.com:',
233 | 'https://',
234 | 'github.com/',
235 | 'raw.githubusercontent.com/',
236 | ];
237 | foreach ($partsToClean as $part) {
238 | $url = str_replace($part, '', $url);
239 | }
240 |
241 | if ('.git' == substr($url, -4)) {
242 | $url = substr($url, 0, -4);
243 | }
244 |
245 | return $url;
246 | }
247 |
248 | /**
249 | * Why do we have to do this? Your guess is as good as mine.
250 | * The only flaw I've seen in the semver lib we're using,
251 | * and the regex's in there are too complicated to mess with.
252 | *
253 | * @param string $rawValue
254 | *
255 | * @return string
256 | */
257 | private function fixupRawTag($rawValue)
258 | {
259 | if (0 === strpos($rawValue, 'v')) {
260 | $rawValue = substr($rawValue, 1);
261 | }
262 | // WHY NOT SCRUB OUT PLUS SIGNS, RIGHT?
263 | $foundIt = strpos($rawValue, '+');
264 | if (false !== $foundIt) {
265 | $rawValue = substr($rawValue, 0, $foundIt);
266 | }
267 | $rawValue = strtr($rawValue, ['.alpha' => '-alpha', '.beta' => '-beta', '.dev' => '-dev']);
268 | $pieces = explode('.', $rawValue);
269 | $count = count($pieces);
270 | if (0 == $count) {
271 | $pieces[] = '0';
272 | $count = 1;
273 | }
274 | for ($add = $count; $add < 3; ++$add) {
275 | $pieces[] = '0';
276 | }
277 | $return = implode('.', array_slice($pieces, 0, 3));
278 |
279 | return $return;
280 | }
281 |
282 | /**
283 | * @param array $tags
284 | * @param bool $excludeUnstables
285 | *
286 | * @return array
287 | */
288 | private function sortTags(array $tags, $excludeUnstables = true)
289 | {
290 | $return = [];
291 |
292 | // Don't include invalid tags
293 | foreach ($tags as $tag) {
294 | try {
295 | $fixedName = $this->fixupRawTag($tag['name']);
296 | $v = new version($fixedName);
297 | if ($v->valid()) {
298 | $version = $v->getVersion();
299 | if ($excludeUnstables && $this->isNotStable($v)) {
300 | continue;
301 | }
302 | $tag['parsed_version'] = $v;
303 | $return[$version] = $tag;
304 | }
305 | } catch (\Exception $ex) {
306 | // Skip
307 | }
308 | }
309 |
310 | uasort($return, function ($a, $b) {
311 | return version::compare($a['parsed_version'], $b['parsed_version']);
312 | });
313 |
314 | return $return;
315 | }
316 |
317 | /**
318 | * @param version $version
319 | *
320 | * @return bool
321 | */
322 | private function isNotStable(version $version)
323 | {
324 | return count($version->getPrerelease()) > 0;
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/src/Bowerphp/Bowerphp.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Bowerphp;
13 |
14 | use Bowerphp\Config\ConfigInterface;
15 | use Bowerphp\Installer\InstallerInterface;
16 | use Bowerphp\Output\BowerphpConsoleOutput;
17 | use Bowerphp\Package\Package;
18 | use Bowerphp\Package\PackageInterface;
19 | use Bowerphp\Repository\RepositoryInterface;
20 | use Bowerphp\Util\Filesystem;
21 | use Github\Client;
22 | use Guzzle\Http\Exception\RequestException;
23 | use InvalidArgumentException;
24 | use RuntimeException;
25 | use Symfony\Component\Finder\Finder;
26 | use vierbergenlars\SemVer\expression;
27 | use vierbergenlars\SemVer\version;
28 |
29 | /**
30 | * Main class
31 | */
32 | class Bowerphp
33 | {
34 | protected $config;
35 |
36 | protected $filesystem;
37 |
38 | protected $githubClient;
39 |
40 | protected $repository;
41 |
42 | protected $output;
43 |
44 | /**
45 | * @param ConfigInterface $config
46 | * @param Filesystem $filesystem
47 | * @param Client $githubClient
48 | * @param RepositoryInterface $repository
49 | * @param BowerphpConsoleOutput $output
50 | */
51 | public function __construct(
52 | ConfigInterface $config,
53 | Filesystem $filesystem,
54 | Client $githubClient,
55 | RepositoryInterface $repository,
56 | BowerphpConsoleOutput $output
57 | ) {
58 | $this->config = $config;
59 | $this->filesystem = $filesystem;
60 | $this->githubClient = $githubClient;
61 | $this->repository = $repository;
62 | $this->output = $output;
63 | }
64 |
65 | /**
66 | * Init bower.json
67 | *
68 | * @param array $params
69 | */
70 | public function init(array $params)
71 | {
72 | if ($this->config->bowerFileExists()) {
73 | $bowerJson = $this->config->getBowerFileContent();
74 | $this->config->setSaveToBowerJsonFile();
75 | $this->config->updateBowerJsonFile2($bowerJson, $params);
76 | } else {
77 | $this->config->initBowerJsonFile($params);
78 | }
79 | }
80 |
81 | /**
82 | * Install a single package
83 | *
84 | * @param PackageInterface $package
85 | * @param InstallerInterface $installer
86 | * @param bool $isDependency
87 | */
88 | public function installPackage(PackageInterface $package, InstallerInterface $installer, $isDependency = false)
89 | {
90 | if (false !== strpos($package->getName(), 'github')) {
91 | // install from a github endpoint
92 | $name = basename($package->getName(), '.git');
93 | $repoUrl = $package->getName();
94 | $package = new Package($name, $package->getRequiredVersion());
95 | $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
96 | $package->setRepository($this->repository);
97 | $packageTag = $this->repository->findPackage($package->getRequiredVersion());
98 | if (is_null($packageTag)) {
99 | throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
100 | }
101 | } else {
102 | $packageTag = $this->getPackageTag($package, true);
103 | $package->setRepository($this->repository);
104 | }
105 |
106 | $package->setVersion($packageTag);
107 |
108 | $this->updateBowerFile($package, $isDependency);
109 |
110 | // if package is already installed, match current version with latest available version
111 | if ($this->isPackageInstalled($package)) {
112 | $this->output->writelnInfoPackage($package, 'validate', sprintf('%s against %s#%s', $packageTag, $package->getName(), $package->getRequiredVersion()));
113 | $packageBower = $this->config->getPackageBowerFileContent($package);
114 | if ($packageTag == $packageBower['version']) {
115 | // if version is fully matching, there's no need to install
116 | return;
117 | }
118 | }
119 |
120 | $this->cachePackage($package);
121 |
122 | $this->output->writelnInstalledPackage($package);
123 | $installer->install($package);
124 |
125 | $overrides = $this->config->getOverrideFor($package->getName());
126 | if (array_key_exists('dependencies', $overrides)) {
127 | $dependencies = $overrides['dependencies'];
128 | } else {
129 | $dependencies = $package->getRequires();
130 | }
131 | if (!empty($dependencies)) {
132 | foreach ($dependencies as $name => $version) {
133 | $depPackage = new Package($name, $version);
134 | if (!$this->isPackageInstalled($depPackage)) {
135 | $this->installPackage($depPackage, $installer, true);
136 | } elseif ($this->isNeedUpdate($depPackage)) {
137 | $this->updatePackage($depPackage, $installer);
138 | }
139 | }
140 | }
141 | }
142 |
143 | /**
144 | * Install all dependencies
145 | *
146 | * @param InstallerInterface $installer
147 | */
148 | public function installDependencies(InstallerInterface $installer)
149 | {
150 | $decode = $this->config->getBowerFileContent();
151 | if (!empty($decode['dependencies'])) {
152 | foreach ($decode['dependencies'] as $name => $requiredVersion) {
153 | if (false !== strpos($requiredVersion, 'github')) {
154 | list($name, $requiredVersion) = explode('#', $requiredVersion);
155 | }
156 | $package = new Package($name, $requiredVersion);
157 | $this->installPackage($package, $installer, true);
158 | }
159 | }
160 | }
161 |
162 | /**
163 | * Update a single package
164 | *
165 | * @param PackageInterface $package
166 | * @param InstallerInterface $installer
167 | */
168 | public function updatePackage(PackageInterface $package, InstallerInterface $installer)
169 | {
170 | if (!$this->isPackageInstalled($package)) {
171 | throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
172 | }
173 | if (is_null($package->getRequiredVersion())) {
174 | $decode = $this->config->getBowerFileContent();
175 | if (empty($decode['dependencies']) || empty($decode['dependencies'][$package->getName()])) {
176 | throw new InvalidArgumentException(sprintf('Package %s not found in bower.json', $package->getName()));
177 | }
178 | $package->setRequiredVersion($decode['dependencies'][$package->getName()]);
179 | }
180 |
181 | $bower = $this->config->getPackageBowerFileContent($package);
182 | $package->setInfo($bower);
183 | $package->setVersion($bower['version']);
184 | $package->setRequires(isset($bower['dependencies']) ? $bower['dependencies'] : null);
185 |
186 | $packageTag = $this->getPackageTag($package);
187 | $this->output->writelnInfoPackage($package, 'validate', sprintf('%s against %s#%s', $packageTag, $package->getName(), $package->getRequiredVersion()));
188 |
189 | $package->setRepository($this->repository);
190 | if ($packageTag == $package->getVersion()) {
191 | // if version is fully matching, there's no need to update
192 | return;
193 | }
194 | $package->setVersion($packageTag);
195 |
196 | $this->cachePackage($package);
197 |
198 | $this->output->writelnUpdatingPackage($package);
199 | $installer->update($package);
200 |
201 | $overrides = $this->config->getOverrideFor($package->getName());
202 | if (array_key_exists('dependencies', $overrides)) {
203 | $dependencies = $overrides['dependencies'];
204 | } else {
205 | $dependencies = $package->getRequires();
206 | }
207 | if (!empty($dependencies)) {
208 | foreach ($dependencies as $name => $requiredVersion) {
209 | $depPackage = new Package($name, $requiredVersion);
210 | if (!$this->isPackageInstalled($depPackage)) {
211 | $this->installPackage($depPackage, $installer, true);
212 | } elseif ($this->isNeedUpdate($depPackage)) {
213 | $this->updatePackage($depPackage, $installer);
214 | }
215 | }
216 | }
217 | }
218 |
219 | /**
220 | * Update all dependencies
221 | *
222 | * @param InstallerInterface $installer
223 | */
224 | public function updatePackages(InstallerInterface $installer)
225 | {
226 | $decode = $this->config->getBowerFileContent();
227 | if (!empty($decode['dependencies'])) {
228 | foreach ($decode['dependencies'] as $packageName => $requiredVersion) {
229 | $this->updatePackage(new Package($packageName, $requiredVersion), $installer);
230 | }
231 | }
232 | }
233 |
234 | /**
235 | * @param PackageInterface $package
236 | * @param string $info
237 | * @return mixed
238 | */
239 | public function getPackageInfo(PackageInterface $package, $info = 'url')
240 | {
241 | $decode = $this->lookupPackage($package->getName());
242 |
243 | $this->repository->setHttpClient($this->githubClient);
244 |
245 | if ('url' == $info) {
246 | $this->repository->setUrl($decode['url'], false);
247 |
248 | return $this->repository->getUrl();
249 | }
250 |
251 | if ('versions' == $info) {
252 | $tags = $this->repository->getTags();
253 | usort($tags, function ($a, $b) {
254 | return version_compare($b, $a);
255 | });
256 |
257 | return $tags;
258 | }
259 |
260 | throw new RuntimeException(sprintf('Unsupported info option "%s".', $info));
261 | }
262 |
263 | /**
264 | * @param string $name
265 | * @return array
266 | */
267 | public function lookupPackage($name)
268 | {
269 | return $this->findPackage($name);
270 | }
271 |
272 | /**
273 | * @param PackageInterface $package
274 | * @return string
275 | */
276 | public function getPackageBowerFile(PackageInterface $package)
277 | {
278 | $this->repository->setHttpClient($this->githubClient);
279 | $lookupPackage = $this->lookupPackage($package->getName());
280 | $this->repository->setUrl($lookupPackage['url'], false);
281 | $tag = $this->repository->findPackage($package->getRequiredVersion());
282 |
283 | return $this->repository->getBower($tag, true, $lookupPackage['url']);
284 | }
285 |
286 | /**
287 | * Uninstall a single package
288 | *
289 | * @param PackageInterface $package
290 | * @param InstallerInterface $installer
291 | */
292 | public function uninstallPackage(PackageInterface $package, InstallerInterface $installer)
293 | {
294 | if (!$this->isPackageInstalled($package)) {
295 | throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
296 | }
297 | $installer->uninstall($package);
298 | }
299 |
300 | /**
301 | * Search packages by name
302 | *
303 | * @param string $name
304 | * @return array
305 | */
306 | public function searchPackages($name)
307 | {
308 | try {
309 | $url = $this->config->getBasePackagesUrl() . 'search/' . $name;
310 | $response = $this->githubClient->getHttpClient()->get($url);
311 |
312 | return json_decode($response->getBody(true), true);
313 | } catch (RequestException $e) {
314 | throw new RuntimeException(sprintf('Cannot get package list from %s.', str_replace('/packages/', '', $this->config->getBasePackagesUrl())));
315 | }
316 | }
317 |
318 | /**
319 | * Get a list of installed packages
320 | *
321 | * @param InstallerInterface $installer
322 | * @param Finder $finder
323 | * @return array
324 | */
325 | public function getInstalledPackages(InstallerInterface $installer, Finder $finder)
326 | {
327 | return $installer->getInstalled($finder);
328 | }
329 |
330 | /**
331 | * Check if package is installed
332 | *
333 | * @param PackageInterface $package
334 | * @return bool
335 | */
336 | public function isPackageInstalled(PackageInterface $package)
337 | {
338 | return $this->filesystem->exists($this->config->getInstallDir() . '/' . $package->getName() . '/.bower.json');
339 | }
340 |
341 | /**
342 | * @param PackageInterface $package
343 | * @param bool $checkInstall
344 | *
345 | * @return bool
346 | */
347 | public function isPackageExtraneous(PackageInterface $package, $checkInstall = false)
348 | {
349 | if ($checkInstall && !$this->isPackageInstalled($package)) {
350 | return false;
351 | }
352 |
353 | try {
354 | $bower = $this->config->getBowerFileContent();
355 | } catch (RuntimeException $e) { // no bower.json file, package is extraneous
356 | return true;
357 | }
358 | if (!isset($bower['dependencies'])) {
359 | return true;
360 | }
361 | // package is a direct dependencies
362 | if (isset($bower['dependencies'][$package->getName()])) {
363 | return false;
364 | }
365 | // look for dependencies of dependencies
366 | foreach ($bower['dependencies'] as $name => $version) {
367 | $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name . '/.bower.json');
368 | $depBower = json_decode($dotBowerJson, true);
369 | if (isset($depBower['dependencies'][$package->getName()])) {
370 | return false;
371 | }
372 | // look for dependencies of dependencies of dependencies
373 | if (isset($depBower['dependencies'])) {
374 | foreach ($depBower['dependencies'] as $name1 => $version1) {
375 | $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name1 . '/.bower.json');
376 | $depDepBower = json_decode($dotBowerJson, true);
377 | if (isset($depDepBower['dependencies'][$package->getName()])) {
378 | return false;
379 | }
380 | }
381 | }
382 | }
383 |
384 | return true;
385 | }
386 |
387 | /**
388 | * @param array $params
389 | * @return array
390 | */
391 | protected function createAClearBowerFile(array $params)
392 | {
393 | $authors = ['Beelab '];
394 | if (!empty($params['author'])) {
395 | $authors[] = $params['author'];
396 | }
397 | $structure = [
398 | 'name' => $params['name'],
399 | 'authors' => $authors,
400 | 'private' => true,
401 | 'dependencies' => new \StdClass(),
402 | ];
403 |
404 | return $structure;
405 | }
406 |
407 | /**
408 | * @param PackageInterface $package
409 | * @param bool $setInfo
410 | * @return string
411 | */
412 | protected function getPackageTag(PackageInterface $package, $setInfo = false)
413 | {
414 | $decode = $this->findPackage($package->getName());
415 | // open package repository
416 | $repoUrl = $decode['url'];
417 | $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
418 | $packageTag = $this->repository->findPackage($package->getRequiredVersion());
419 | if (is_null($packageTag)) {
420 | throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
421 | }
422 | $bowerJson = $this->repository->getBower($packageTag);
423 | $bower = json_decode($bowerJson, true);
424 | if (!is_array($bower)) {
425 | throw new RuntimeException(sprintf('Invalid bower.json found in package %s: %s.', $package->getName(), $bowerJson));
426 | }
427 | if ($setInfo) {
428 | $package->setInfo($bower);
429 | }
430 |
431 | return $packageTag;
432 | }
433 |
434 | /**
435 | * @param string $name
436 | * @return array
437 | */
438 | protected function findPackage($name)
439 | {
440 | try {
441 | $response = $this->githubClient->getHttpClient()->get($this->config->getBasePackagesUrl() . urlencode($name));
442 | } catch (RuntimeException $e) {
443 | throw new RuntimeException(sprintf('Cannot fetch registry info for package %s from search registry (%s).', $name, $e->getMessage()));
444 | }
445 | $packageInfo = json_decode($response->getBody(true), true);
446 | if (!is_array($packageInfo) || empty($packageInfo['url'])) {
447 | throw new RuntimeException(sprintf('Registry info for package %s has malformed json or is missing "url".', $name));
448 | }
449 |
450 | return $packageInfo;
451 | }
452 |
453 | /**
454 | * @param PackageInterface $package
455 | */
456 | private function cachePackage(PackageInterface $package)
457 | {
458 | $this->output->writelnInfoPackage($package, 'download');
459 |
460 | // get release archive from repository
461 | $file = $this->repository->getRelease();
462 |
463 | $tmpFileName = $this->config->getCacheDir() . '/tmp/' . $package->getName();
464 | $this->filesystem->write($tmpFileName, $file);
465 | }
466 |
467 | /**
468 | * @param PackageInterface $package
469 | * @param bool $isDependency
470 | */
471 | private function updateBowerFile(PackageInterface $package, $isDependency = false)
472 | {
473 | if ($this->config->isSaveToBowerJsonFile() && !$isDependency) {
474 | try {
475 | $this->config->updateBowerJsonFile($package);
476 | } catch (RuntimeException $e) {
477 | $this->output->writelnNoBowerJsonFile();
478 | }
479 | }
480 | }
481 |
482 | /**
483 | * Update only if needed is greater version
484 | *
485 | * @param PackageInterface $package
486 | * @return bool
487 | */
488 | public function isNeedUpdate($package)
489 | {
490 | $packageBower = $this->config->getPackageBowerFileContent($package);
491 | $semver = new version($packageBower['version']);
492 |
493 | return !$semver->satisfies(new expression($package->getRequiredVersion()));
494 | }
495 | }
496 |
--------------------------------------------------------------------------------