1<?php
2
3/*
4 * This file is part of Component Installer.
5 *
6 * (c) Rob Loach (http://robloach.net)
7 *
8 * For the full copyright and license information, please view the LICENSE.md
9 * file that was distributed with this source code.
10 */
11
12namespace ComponentInstaller\Process;
13
14use Composer\IO\IOInterface;
15use Composer\Composer;
16use Composer\IO\NullIO;
17use Composer\Package\Dumper\ArrayDumper;
18use ComponentInstaller\Util\Filesystem;
19use Composer\Package\Loader\ArrayLoader;
20
21/**
22 * The base Process type.
23 *
24 * Processes are initialized, and then run during installation.
25 */
26class Process implements ProcessInterface
27{
28    /**
29     * @var Composer
30     */
31    protected $composer;
32
33    /**
34     * @var IOInterface|NullIO
35     */
36    protected $io;
37
38    /**
39     * @var \Composer\Config
40     */
41    protected $config;
42
43    /**
44     * @var array
45     */
46    protected $packages = array();
47
48    /**
49     * @var string
50     */
51    protected $componentDir = 'components';
52
53    /**
54     * @var Filesystem
55     */
56    protected $fs;
57
58    /**
59     * The Composer installation manager to find Component vendor directories.
60     * @var \Composer\Installer\InstallationManager
61     */
62    protected $installationManager;
63
64    /**
65     * {@inheritdoc}
66     */
67    public function __construct(Composer $composer = null, IOInterface $io = null)
68    {
69        $this->composer = isset($composer) ? $composer : new Composer();
70        $this->io = isset($io) ? $io : new NullIO();
71        $this->fs = new Filesystem();
72        $this->installationManager = $this->composer->getInstallationManager();
73    }
74
75    /**
76     * {@inheritdoc}
77     */
78    public function init()
79    {
80        // Retrieve the configuration variables.
81        $this->config = $this->composer->getConfig();
82        if (isset($this->config)) {
83            if ($this->config->has('component-dir')) {
84                $this->componentDir = $this->config->get('component-dir');
85            }
86        }
87
88        // Get the available packages.
89        $allPackages = array();
90        /** @var \Composer\Package\Locker $locker */
91        $locker = $this->composer->getLocker();
92        if ($locker !== null && $locker->isLocked()) {
93            $lockData = $locker->getLockData();
94            $allPackages = $lockData['packages'];
95
96            // Also merge in any of the development packages.
97            $dev = isset($lockData['packages-dev']) ? $lockData['packages-dev'] : array();
98            foreach ($dev as $package) {
99                $allPackages[] = $package;
100            }
101        }
102
103        // Only add those packages that we can reasonably
104        // assume are components into our packages list
105        /** @var \Composer\Package\RootPackageInterface $rootPackage */
106        $rootPackage = $this->composer->getPackage();
107        $rootExtras = $rootPackage ? $rootPackage->getExtra() : array();
108        $customComponents = isset($rootExtras['component']) ? $rootExtras['component'] : array();
109        foreach ($allPackages as $package) {
110            $name = $package['name'];
111            if (isset($customComponents[$name]) && is_array($customComponents[$name])) {
112                $package['extra'] = array('component' => $customComponents[$name]);
113                $this->packages[] = $package;
114            }
115            else {
116                $extra = isset($package['extra']) ? $package['extra'] : array();
117                if (isset($extra['component']) && is_array($extra['component'])) {
118                    $this->packages[] = $package;
119                }
120            }
121        }
122
123        // Add the root package to the packages list.
124        $root = $this->composer->getPackage();
125        if ($root) {
126            $dumper = new ArrayDumper();
127            $package = $dumper->dump($root);
128            $package['is-root'] = true;
129            $this->packages[] = $package;
130        }
131
132        return true;
133    }
134
135    /**
136     * {@inheritdoc}
137     */
138    public function process()
139    {
140        return false;
141    }
142
143    /**
144     * Retrieves the component name for the component.
145     *
146     * @param string $prettyName
147     *   The Composer package name.
148     * @param array $extra
149     *   The extra config options sent from Composer.
150     *
151     * @return string
152     *   The name of the component, without its vendor name.
153     */
154    public function getComponentName($prettyName, array $extra = array())
155    {
156        // Parse the pretty name for the vendor and name.
157        if (strpos($prettyName, '/') !== false) {
158            list($vendor, $name) = explode('/', $prettyName);
159            unset($vendor);
160        } else {
161            // Vendor wasn't found, so default to the pretty name instead.
162            $name = $prettyName;
163        }
164
165        // Allow the component to define its own name.
166        $component = isset($extra['component']) ? $extra['component'] : array();
167        if (isset($component['name'])) {
168            $name = $component['name'];
169        }
170
171        return $name;
172    }
173
174    /**
175     * Retrieves the component directory.
176     */
177    public function getComponentDir()
178    {
179        return $this->componentDir;
180    }
181
182    /**
183     * Sets the component directory.
184     * @param string $dir
185     * @return string
186     */
187    public function setComponentDir($dir)
188    {
189        return $this->componentDir = $dir;
190    }
191
192    /**
193     * Retrieves the given package's vendor directory, where it's installed.
194     *
195     * @param array $package
196     *   The package to retrieve the vendor directory for.
197     * @return string
198     */
199    public function getVendorDir(array $package)
200    {
201        // The root package vendor directory is not handled by getInstallPath().
202        if (isset($package['is-root']) && $package['is-root'] === true) {
203            $path = getcwd();
204
205            if (!file_exists($path.DIRECTORY_SEPARATOR.'composer.json')) {
206                for ($temp = __DIR__; strlen($temp) > 3; $temp = dirname($temp)) {
207                    if (file_exists($temp.DIRECTORY_SEPARATOR.'composer.json')) {
208                        $path = $temp;
209                    }
210                }
211            }
212
213            return $path;
214        }
215
216        if (!isset($package['version'])) {
217            $package['version'] = '1.0.0';
218        }
219        $loader = new ArrayLoader();
220        $completePackage = $loader->load($package);
221
222        return $this->installationManager->getInstallPath($completePackage);
223    }
224}
225