SharedPackageInstaller   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 242
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 20
Bugs 15 Features 6
Metric Value
dl 0
loc 242
c 20
b 15
f 6
wmc 26
lcom 1
cbo 12
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getSymlinkSourcePath() 0 16 3
A __construct() 0 17 1
A getInstallPath() 0 14 2
A getPackageVendorSymlink() 0 4 1
A install() 0 12 3
A isInstalled() 0 9 3
A update() 0 18 2
A uninstall() 0 20 3
A isSourceDirUnused() 0 6 1
A createPackageVendorSymlink() 0 14 3
A removePackageVendorSymlink() 0 16 3
A supports() 0 4 1
1
<?php
2
3
/*
4
 * This file is part of the "Composer Shared Package Plugin" package.
5
 *
6
 * https://github.com/Letudiant/composer-shared-package-plugin
7
 *
8
 * For the full license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace LEtudiant\Composer\Installer;
13
14
use Composer\Composer;
15
use Composer\Config;
16
use Composer\DependencyResolver\Operation\InstallOperation;
17
use Composer\DependencyResolver\Operation\UninstallOperation;
18
use Composer\Downloader\FilesystemException;
19
use Composer\Installer\LibraryInstaller;
20
use Composer\IO\IOInterface;
21
use Composer\Package\PackageInterface;
22
use Composer\Repository\InstalledRepositoryInterface;
23
use LEtudiant\Composer\Data\Package\PackageDataManagerInterface;
24
use LEtudiant\Composer\Installer\Config\SharedPackageInstallerConfig;
25
use LEtudiant\Composer\Util\SymlinkFilesystem;
26
27
/**
28
 * @author Sylvain Lorinet <[email protected]>
29
 */
30
class SharedPackageInstaller extends LibraryInstaller
31
{
32
    const PACKAGE_TYPE = 'shared-package';
33
    const PACKAGE_PRETTY_NAME = 'letudiant/composer-shared-package-plugin';
34
35
    /**
36
     * @var SharedPackageInstallerConfig
37
     */
38
    protected $config;
39
40
    /**
41
     * @var PackageDataManagerInterface
42
     */
43
    protected $packageDataManager;
44
45
    /**
46
     * @var SymlinkFilesystem
47
     */
48
    protected $filesystem;
49
50
51
    /**
52
     * @param IOInterface                  $io
53
     * @param Composer                     $composer
54
     * @param SymlinkFilesystem            $filesystem
55
     * @param PackageDataManagerInterface  $dataManager
56
     * @param SharedPackageInstallerConfig $config
57
     */
58
    public function __construct(
59
        IOInterface $io,
60
        Composer $composer,
61
        SymlinkFilesystem $filesystem,
62
        PackageDataManagerInterface $dataManager,
63
        SharedPackageInstallerConfig $config
64
    )
65
    {
66
        $this->filesystem = $filesystem;
67
68
        parent::__construct($io, $composer, 'library', $this->filesystem);
69
70
        $this->config = $config;
71
        $this->vendorDir = $this->config->getVendorDir();
72
        $this->packageDataManager = $dataManager;
73
        $this->packageDataManager->setVendorDir($this->vendorDir);
74
    }
75
76
    /**
77
     * @inheritdoc
78
     */
79
    public function getInstallPath(PackageInterface $package)
80
    {
81
        $this->initializeVendorDir();
82
83
        $basePath =
84
            $this->vendorDir . DIRECTORY_SEPARATOR
85
            . $package->getPrettyName() . DIRECTORY_SEPARATOR
86
            . $package->getPrettyVersion()
87
        ;
88
89
        $targetDir = $package->getTargetDir();
90
91
        return $basePath . ($targetDir ? '/' . $targetDir : '');
92
    }
93
94
    /**
95
     * @param PackageInterface $package
96
     *
97
     * @return string
98
     */
99
    protected function getPackageVendorSymlink(PackageInterface $package)
100
    {
101
        return $this->config->getSymlinkDir() . DIRECTORY_SEPARATOR . $package->getPrettyName();
102
    }
103
104
    /**
105
     * @param InstalledRepositoryInterface $repo
106
     * @param PackageInterface             $package
107
     */
108
    public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
109
    {
110
        if (!is_readable($this->getInstallPath($package))) {
111
            parent::install($repo, $package);
112
        } elseif (!$repo->hasPackage($package)) {
113
            $this->binaryInstaller->installBinaries($package, $this->getInstallPath($package));
114
            $repo->addPackage(clone $package);
115
        }
116
117
        $this->createPackageVendorSymlink($package);
118
        $this->packageDataManager->addPackageUsage($package);
119
    }
120
121
    /**
122
     * @param InstalledRepositoryInterface $repo
123
     * @param PackageInterface             $package
124
     *
125
     * @return bool
126
     */
127
    public function isInstalled(InstalledRepositoryInterface $repo, PackageInterface $package)
128
    {
129
        // Just check if the sources folder and the link exist
130
        return
131
            $repo->hasPackage($package)
132
            && is_readable($this->getInstallPath($package))
133
            && is_link($this->getPackageVendorSymlink($package))
134
        ;
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
141
    {
142
        $this->packageDataManager->setPackageInstallationSource($initial);
143
        $this->packageDataManager->setPackageInstallationSource($target);
144
145
        // The package need only a code update because the version (branch), only the commit changed
146
        if ($this->getInstallPath($initial) === $this->getInstallPath($target)) {
147
            $this->createPackageVendorSymlink($target);
148
149
            parent::update($repo, $initial, $target);
150
        } else {
151
            // If the initial package sources folder exists, uninstall it
152
            $this->composer->getInstallationManager()->uninstall($repo, new UninstallOperation($initial));
153
154
            // Install the target package
155
            $this->composer->getInstallationManager()->install($repo, new InstallOperation($target));
156
        }
157
    }
158
159
    /**
160
     * @param InstalledRepositoryInterface $repo
161
     * @param PackageInterface             $package
162
     *
163
     * @throws FilesystemException
164
     */
165
    public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
166
    {
167
        if ($this->isSourceDirUnused($package) && $this->io->askConfirmation(
168
                "The package version <info>" . $package->getPrettyName() . "</info> "
169
                . "(<fg=yellow>" . $package->getPrettyVersion() . "</fg=yellow>) seems to be unused."
170
                . PHP_EOL
171
                . 'Do you want to <fg=red>delete the source folder</fg=red> ? [y/n] (default: no) : ',
172
                false
173
            )) {
174
            $this->packageDataManager->setPackageInstallationSource($package);
175
176
            parent::uninstall($repo, $package);
177
        } else {
178
            $this->binaryInstaller->removeBinaries($package);
179
            $repo->removePackage($package);
180
        }
181
182
        $this->packageDataManager->removePackageUsage($package);
183
        $this->removePackageVendorSymlink($package);
184
    }
185
186
    /**
187
     * Detect if other project use the dependency by using the "packages.json" file
188
     *
189
     * @param PackageInterface $package
190
     *
191
     * @return bool
192
     */
193
    protected function isSourceDirUnused(PackageInterface $package)
194
    {
195
        $usageData = $this->packageDataManager->getPackageUsage($package);
196
197
        return sizeof($usageData) <= 1;
198
    }
199
200
    /**
201
     * @param PackageInterface $package
202
     */
203
    protected function createPackageVendorSymlink(PackageInterface $package)
204
    {
205
        if ($this->config->isSymlinkEnabled() && $this->filesystem->ensureSymlinkExists(
206
                $this->getSymlinkSourcePath($package),
207
                $this->getPackageVendorSymlink($package)
208
            )
209
        ) {
210
            $this->io->write(array(
211
                '  - Creating symlink for <info>' . $package->getPrettyName()
212
                . '</info> (<fg=yellow>' . $package->getPrettyVersion() . '</fg=yellow>)',
213
                ''
214
            ));
215
        }
216
    }
217
218
    /**
219
     * @param PackageInterface $package
220
     *
221
     * @return string
222
     */
223
    protected function getSymlinkSourcePath(PackageInterface $package)
224
    {
225
        if (null != $this->config->getSymlinkBasePath()) {
226
            $targetDir = $package->getTargetDir();
227
            $sourcePath =
228
                $this->config->getSymlinkBasePath()
229
                . '/' . $package->getPrettyName()
230
                . '/' . $package->getPrettyVersion()
231
                . ($targetDir ? '/' . $targetDir : '')
232
            ;
233
        } else {
234
            $sourcePath = $this->getInstallPath($package);
235
        }
236
237
        return $sourcePath;
238
    }
239
240
    /**
241
     * @param PackageInterface $package
242
     *
243
     * @throws FilesystemException
244
     */
245
    protected function removePackageVendorSymlink(PackageInterface $package)
246
    {
247
        if (
248
            $this->config->isSymlinkEnabled()
249
            && $this->filesystem->removeSymlink($this->getPackageVendorSymlink($package))
250
        ) {
251
            $this->io->write(array(
252
                '  - Deleting symlink for <info>' . $package->getPrettyName() . '</info> '
253
                . '(<fg=yellow>' . $package->getPrettyVersion() . '</fg=yellow>)',
254
                ''
255
            ));
256
257
            $symlinkParentDirectory = dirname($this->getPackageVendorSymlink($package));
258
            $this->filesystem->removeEmptyDirectory($symlinkParentDirectory);
259
        }
260
    }
261
262
    /**
263
     * @param string $packageType
264
     *
265
     * @return bool
266
     */
267
    public function supports($packageType)
268
    {
269
        return true;
270
    }
271
}
272