Complex classes like Bowerphp often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Bowerphp, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 30 | class Bowerphp |
||
| 31 | { |
||
| 32 | protected $config; |
||
| 33 | protected $filesystem; |
||
| 34 | protected $githubClient; |
||
| 35 | protected $repository; |
||
| 36 | protected $output; |
||
| 37 | |||
| 38 | /** |
||
| 39 | * @param ConfigInterface $config |
||
| 40 | * @param Filesystem $filesystem |
||
| 41 | * @param Client $githubClient |
||
| 42 | * @param RepositoryInterface $repository |
||
| 43 | * @param BowerphpConsoleOutput $output |
||
| 44 | */ |
||
| 45 | public function __construct(ConfigInterface $config, Filesystem $filesystem, Client $githubClient, RepositoryInterface $repository, BowerphpConsoleOutput $output) |
||
| 46 | { |
||
| 47 | $this->config = $config; |
||
| 48 | $this->filesystem = $filesystem; |
||
| 49 | $this->githubClient = $githubClient; |
||
| 50 | $this->repository = $repository; |
||
| 51 | $this->output = $output; |
||
| 52 | } |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Init bower.json |
||
| 56 | * |
||
| 57 | * @param array $params |
||
| 58 | */ |
||
| 59 | public function init(array $params) |
||
| 60 | { |
||
| 61 | if ($this->config->bowerFileExists()) { |
||
| 62 | $bowerJson = $this->config->getBowerFileContent(); |
||
| 63 | $this->config->setSaveToBowerJsonFile(true); |
||
| 64 | $this->config->updateBowerJsonFile2($bowerJson, $params); |
||
| 65 | } else { |
||
| 66 | $this->config->initBowerJsonFile($params); |
||
| 67 | } |
||
| 68 | } |
||
| 69 | |||
| 70 | /** |
||
| 71 | * Install a single package |
||
| 72 | * |
||
| 73 | * @param PackageInterface $package |
||
| 74 | * @param InstallerInterface $installer |
||
| 75 | * @param bool $isDependency |
||
| 76 | */ |
||
| 77 | public function installPackage(PackageInterface $package, InstallerInterface $installer, $isDependency = false) |
||
| 78 | { |
||
| 79 | if (strpos($package->getName(), 'github') !== false) { |
||
| 80 | // install from a github endpoint |
||
| 81 | $name = basename($package->getName(), '.git'); |
||
| 82 | $repoUrl = $package->getName(); |
||
| 83 | $package = new Package($name, $package->getRequiredVersion()); |
||
| 84 | $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient); |
||
| 85 | $package->setRepository($this->repository); |
||
| 86 | $packageTag = $this->repository->findPackage($package->getRequiredVersion()); |
||
| 87 | if (is_null($packageTag)) { |
||
| 88 | throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion())); |
||
| 89 | } |
||
| 90 | } else { |
||
| 91 | $packageTag = $this->getPackageTag($package, true); |
||
| 92 | $package->setRepository($this->repository); |
||
| 93 | } |
||
| 94 | |||
| 95 | $package->setVersion($packageTag); |
||
| 96 | |||
| 97 | $this->updateBowerFile($package, $isDependency); |
||
| 98 | |||
| 99 | // if package is already installed, match current version with latest available version |
||
| 100 | if ($this->isPackageInstalled($package)) { |
||
| 101 | $packageBower = $this->config->getPackageBowerFileContent($package); |
||
| 102 | if ($packageTag == $packageBower['version']) { |
||
| 103 | // if version is fully matching, there's no need to install |
||
| 104 | return; |
||
| 105 | } |
||
| 106 | } |
||
| 107 | |||
| 108 | $this->output->writelnInfoPackage($package); |
||
| 109 | |||
| 110 | $this->output->writelnInstalledPackage($package); |
||
| 111 | |||
| 112 | $this->cachePackage($package); |
||
| 113 | |||
| 114 | $installer->install($package); |
||
| 115 | |||
| 116 | $overrides = $this->config->getOverrideFor($package->getName()); |
||
| 117 | if (array_key_exists('dependencies', $overrides)) { |
||
| 118 | $dependencies = $overrides['dependencies']; |
||
| 119 | } else { |
||
| 120 | $dependencies = $package->getRequires(); |
||
| 121 | } |
||
| 122 | if (!empty($dependencies)) { |
||
| 123 | foreach ($dependencies as $name => $version) { |
||
| 124 | $depPackage = new Package($name, $version); |
||
| 125 | if (!$this->isPackageInstalled($depPackage)) { |
||
| 126 | $this->installPackage($depPackage, $installer, true); |
||
| 127 | } else { |
||
| 128 | $this->updatePackage($depPackage, $installer); |
||
| 129 | } |
||
| 130 | } |
||
| 131 | } |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Install all dependencies |
||
| 136 | * |
||
| 137 | * @param InstallerInterface $installer |
||
| 138 | */ |
||
| 139 | public function installDependencies(InstallerInterface $installer) |
||
| 152 | |||
| 153 | /** |
||
| 154 | * Update a single package |
||
| 155 | * |
||
| 156 | * @param PackageInterface $package |
||
| 157 | * @param InstallerInterface $installer |
||
| 158 | */ |
||
| 159 | public function updatePackage(PackageInterface $package, InstallerInterface $installer) |
||
| 160 | { |
||
| 161 | if (!$this->isPackageInstalled($package)) { |
||
| 162 | throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName())); |
||
| 163 | } |
||
| 164 | if (is_null($package->getRequiredVersion())) { |
||
| 165 | $decode = $this->config->getBowerFileContent(); |
||
| 166 | if (empty($decode['dependencies']) || empty($decode['dependencies'][$package->getName()])) { |
||
| 167 | throw new InvalidArgumentException(sprintf('Package %s not found in bower.json', $package->getName())); |
||
| 168 | } |
||
| 169 | $package->setRequiredVersion($decode['dependencies'][$package->getName()]); |
||
| 170 | } |
||
| 171 | |||
| 172 | $bower = $this->config->getPackageBowerFileContent($package); |
||
| 173 | $package->setInfo($bower); |
||
| 174 | $package->setVersion($bower['version']); |
||
| 175 | $package->setRequires(isset($bower['dependencies']) ? $bower['dependencies'] : null); |
||
| 176 | |||
| 177 | $packageTag = $this->getPackageTag($package); |
||
| 178 | $package->setRepository($this->repository); |
||
| 179 | if ($packageTag == $package->getVersion()) { |
||
| 180 | // if version is fully matching, there's no need to update |
||
| 181 | return; |
||
| 182 | } |
||
| 183 | $package->setVersion($packageTag); |
||
| 184 | |||
| 185 | $this->output->writelnUpdatingPackage($package); |
||
| 186 | |||
| 187 | $this->cachePackage($package); |
||
| 188 | |||
| 189 | $installer->update($package); |
||
| 190 | |||
| 191 | $overrides = $this->config->getOverrideFor($package->getName()); |
||
| 192 | if (array_key_exists('dependencies', $overrides)) { |
||
| 193 | $dependencies = $overrides['dependencies']; |
||
| 194 | } else { |
||
| 195 | $dependencies = $package->getRequires(); |
||
| 196 | } |
||
| 197 | if (!empty($dependencies)) { |
||
| 198 | foreach ($dependencies as $name => $requiredVersion) { |
||
| 199 | $depPackage = new Package($name, $requiredVersion); |
||
| 200 | if (!$this->isPackageInstalled($depPackage)) { |
||
| 201 | $this->installPackage($depPackage, $installer, true); |
||
| 202 | } else { |
||
| 203 | $this->updatePackage($depPackage, $installer); |
||
| 204 | } |
||
| 205 | } |
||
| 206 | } |
||
| 207 | } |
||
| 208 | |||
| 209 | /** |
||
| 210 | * Update all dependencies |
||
| 211 | * |
||
| 212 | * @param InstallerInterface $installer |
||
| 213 | */ |
||
| 214 | public function updatePackages(InstallerInterface $installer) |
||
| 215 | { |
||
| 216 | $decode = $this->config->getBowerFileContent(); |
||
| 217 | if (!empty($decode['dependencies'])) { |
||
| 218 | foreach ($decode['dependencies'] as $packageName => $requiredVersion) { |
||
| 219 | $this->updatePackage(new Package($packageName, $requiredVersion), $installer); |
||
| 220 | } |
||
| 221 | } |
||
| 222 | } |
||
| 223 | |||
| 224 | /** |
||
| 225 | * @param PackageInterface $package |
||
| 226 | * @param string $info |
||
| 227 | * @return mixed |
||
| 228 | */ |
||
| 229 | public function getPackageInfo(PackageInterface $package, $info = 'url') |
||
| 230 | { |
||
| 231 | $decode = $this->lookupPackage($package->getName()); |
||
| 232 | |||
| 233 | $this->repository->setHttpClient($this->githubClient); |
||
| 234 | |||
| 235 | if ($info == 'url') { |
||
| 236 | $this->repository->setUrl($decode['url'], false); |
||
| 237 | |||
| 238 | return $this->repository->getUrl(); |
||
| 239 | } |
||
| 240 | |||
| 241 | if ($info == 'versions') { |
||
| 242 | $tags = $this->repository->getTags(); |
||
| 243 | usort($tags, function ($a, $b) { |
||
| 244 | return version_compare($b, $a); |
||
| 245 | }); |
||
| 246 | |||
| 247 | return $tags; |
||
| 248 | } |
||
| 249 | |||
| 250 | throw new RuntimeException(sprintf('Unsupported info option "%s".', $info)); |
||
| 251 | } |
||
| 252 | |||
| 253 | /** |
||
| 254 | * @param string $name |
||
| 255 | * @return array |
||
| 256 | */ |
||
| 257 | public function lookupPackage($name) |
||
| 261 | |||
| 262 | /** |
||
| 263 | * @param PackageInterface $package |
||
| 264 | * @return string |
||
| 265 | */ |
||
| 266 | public function getPackageBowerFile(PackageInterface $package) |
||
| 267 | { |
||
| 268 | $this->repository->setHttpClient($this->githubClient); |
||
| 269 | $lookupPackage = $this->lookupPackage($package->getName()); |
||
| 270 | $this->repository->setUrl($lookupPackage['url'], false); |
||
| 271 | $tag = $this->repository->findPackage($package->getRequiredVersion()); |
||
| 272 | |||
| 273 | return $this->repository->getBower($tag, true, $lookupPackage['url']); |
||
| 274 | } |
||
| 275 | |||
| 276 | /** |
||
| 277 | * Uninstall a single package |
||
| 278 | * |
||
| 279 | * @param PackageInterface $package |
||
| 280 | * @param InstallerInterface $installer |
||
| 281 | */ |
||
| 282 | public function uninstallPackage(PackageInterface $package, InstallerInterface $installer) |
||
| 283 | { |
||
| 284 | if (!$this->isPackageInstalled($package)) { |
||
| 285 | throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName())); |
||
| 286 | } |
||
| 287 | $installer->uninstall($package); |
||
| 288 | } |
||
| 289 | |||
| 290 | /** |
||
| 291 | * Search packages by name |
||
| 292 | * |
||
| 293 | * @param string $name |
||
| 294 | * @return array |
||
| 295 | */ |
||
| 296 | public function searchPackages($name) |
||
| 297 | { |
||
| 298 | try { |
||
| 299 | $url = $this->config->getBasePackagesUrl() . 'search/' . $name; |
||
| 300 | $response = $this->githubClient->getHttpClient()->get($url); |
||
| 301 | |||
| 302 | return json_decode($response->getBody(true), true); |
||
| 303 | } catch (RequestException $e) { |
||
| 304 | throw new RuntimeException(sprintf('Cannot get package list from %s.', str_replace('/packages/', '', $this->config->getBasePackagesUrl()))); |
||
| 305 | } |
||
| 306 | } |
||
| 307 | |||
| 308 | /** |
||
| 309 | * Get a list of installed packages |
||
| 310 | * |
||
| 311 | * @param InstallerInterface $installer |
||
| 312 | * @param Finder $finder |
||
| 313 | * @return array |
||
| 314 | */ |
||
| 315 | public function getInstalledPackages(InstallerInterface $installer, Finder $finder) |
||
| 319 | |||
| 320 | /** |
||
| 321 | * Check if package is installed |
||
| 322 | * |
||
| 323 | * @param PackageInterface $package |
||
| 324 | * @return bool |
||
| 325 | */ |
||
| 326 | public function isPackageInstalled(PackageInterface $package) |
||
| 330 | |||
| 331 | /** |
||
| 332 | * {@inheritdoc} |
||
| 333 | */ |
||
| 334 | public function isPackageExtraneous(PackageInterface $package, $checkInstall = false) |
||
| 335 | { |
||
| 336 | if ($checkInstall && !$this->isPackageInstalled($package)) { |
||
| 337 | return false; |
||
| 338 | } |
||
| 339 | try { |
||
| 373 | |||
| 374 | /** |
||
| 375 | * @param array $params |
||
| 376 | * @return array |
||
| 377 | */ |
||
| 378 | protected function createAClearBowerFile(array $params) |
||
| 393 | |||
| 394 | /** |
||
| 395 | * @param PackageInterface $package |
||
| 396 | * @param bool $setInfo |
||
| 397 | * @return string |
||
| 398 | */ |
||
| 399 | protected function getPackageTag(PackageInterface $package, $setInfo = false) |
||
| 420 | |||
| 421 | /** |
||
| 422 | * @param string $name |
||
| 423 | * @return array |
||
| 424 | */ |
||
| 425 | protected function findPackage($name) |
||
| 439 | |||
| 440 | /** |
||
| 441 | * @param PackageInterface $package |
||
| 442 | */ |
||
| 443 | private function cachePackage(PackageInterface $package) |
||
| 451 | |||
| 452 | /** |
||
| 453 | * @param PackageInterface $package |
||
| 454 | * @param bool $isDependency |
||
| 455 | */ |
||
| 456 | private function updateBowerFile(PackageInterface $package, $isDependency = false) |
||
| 466 | } |
||
| 467 |