Last active
January 3, 2016 03:29
-
-
Save solocommand/8402724 to your computer and use it in GitHub Desktop.
Composer script to auto-enable installed bundles in Symfony2 AppKernel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Cygnus\CoreBundle\Composer; | |
use Composer\Script\Event; | |
use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator; | |
class ScriptHandler | |
{ | |
private static $coreBundleName = 'cygnus/base-core-bundle'; | |
private static $packagesToEnable = array(); | |
private static $lockedRepository; | |
public static function postActionCmd(Event $event) | |
{ | |
// Get the composer instance from the recieved event | |
$composer = $event->getComposer(); | |
// Set the locked Repository (installed packages) for later use. | |
self::$lockedRepository = $composer->getLocker()->getLockedRepository(); | |
// Get the core package | |
$corePackage = self::getPackage(self::$coreBundleName); | |
// Recursively get packages that should be enabled in AppKernel. | |
self::registerPackages($corePackage); | |
// Initiate translation of package names and insertion into AppKernel. | |
self::updateAppKernel(); | |
// Load routes from installed bundles, if applicable. | |
self::updateBundleRoutes(); | |
} | |
private static function getBundlePath($package, $packageName) | |
{ | |
$baseDir = substr(__DIR__, 0, strpos(__DIR__, 'vendor/') + strlen('vendor/')); | |
$namespace = self::getNamespace($package); | |
$srcPath = $package->getAutoload()['psr-0']; | |
$targetDir = (is_null($package->getTargetDir())) ? $namespace : $package->getTargetDir(); | |
$path = ($srcPath == 'src/') ? $srcPath.$targetDir : $targetDir; | |
// Stupid hack for symfony components | |
if (0 === strpos($packageName, 'symfony')) { | |
return $baseDir."symfony/symfony/src/Symfony/Bundle/".ucfirst(str_replace('symfony/', '', str_replace('-bundle', 'Bundle', $packageName))); | |
} | |
return $fullPath = $baseDir.$packageName."/".$path; | |
} | |
private static function getNamespace($package) | |
{ | |
return key($package->getAutoload()['psr-0']); | |
} | |
private static function getBundleNames() | |
{ | |
$bundleNames = array(); | |
foreach (self::$packagesToEnable as $packageName => $package) | |
{ | |
$className = null; | |
$fullPath = self::getBundlePath($package, $packageName); | |
if ($handle = @opendir($fullPath)) | |
{ | |
// Check for Bundle within root path | |
while(false !== ($entry = readdir($handle))) | |
{ | |
if (false !== strpos($entry, 'Bundle')) | |
{ | |
$className = self::getClassNameFromFile("$fullPath/$entry"); | |
} | |
} | |
// Check for Bundle file within namespace subdir path. | |
$namespacePath = $fullPath . "/" . str_replace("\\", "/", self::getNamespace($package)); | |
if (null == $className && $namespaceHandle = @opendir($namespacePath)) | |
{ | |
while(false !== ($entry = readdir($namespaceHandle))) | |
{ | |
if (false !== strpos($entry, 'Bundle')) | |
{ | |
$className = self::getClassNameFromFile("$namespacePath/$entry"); | |
} | |
} | |
} | |
} else { | |
echo "Unable to open path $fullPath for package {$package->getName()}.\r\n"; | |
} | |
if (null == $className) | |
{ | |
echo "Unable to find className for package {$package->getName()}. Checked paths: {$fullPath} and {$namespacePath}.\r\n"; | |
} else { | |
$bundleNames[] = $className; | |
} | |
} | |
return array_unique($bundleNames); | |
} | |
private static function getRoutableBundles() | |
{ | |
$routableBundles = array(); | |
foreach (self::$packagesToEnable as $packageName => $package) | |
{ | |
// Only allow dynamic routing from cygnus bundles. | |
// If third-party routes are needed, define them in the package that requires them. | |
if (false === strpos($packageName, 'cygnus')) continue; | |
$className = null; | |
$bundlePath = self::getBundlePath($package, $packageName); | |
// Get the Bundle name from the path, e.g. vendor/cygnus/base-core-bundle/Cygnus/CoreBundle becomes CygnusCoreBundle | |
$bundleName = str_replace('/', '', substr($bundlePath, strrpos($bundlePath, '/', -1 - strlen(substr($bundlePath, strrpos($bundlePath, '/'))))+1)); | |
if (file_exists("$bundlePath/Resources/config/routing.yml")) { | |
$routableBundles[$bundleName] = "@$bundleName/Resources/config/routing.yml"; | |
} elseif (file_exists("$bundlePath/Resources/config/routing.xml")) { | |
$routableBundles[$bundleName] = "@$bundleName/Resources/config/routing.xml"; | |
} elseif (file_exists("$bundlePath/Resources/config/routing.php")) { | |
$routableBundles[$bundleName] = "@$bundleName/Resources/config/routing.php"; | |
} | |
} | |
return array_unique($routableBundles); | |
} | |
private static function updateBundleRoutes() | |
{ | |
// Bring in routes from loaded cygnus bundles | |
$appBase = substr(__DIR__, 0, strpos(__DIR__, 'vendor/')) . "app/"; | |
$routesToAdd = self::getRoutableBundles(); | |
$routingConfig = ""; | |
$routeLines = file("{$appBase}config/routing.yml"); | |
$existingRoutes = array(); | |
foreach ($routeLines as $route) { | |
if (strpos($route, '# ') === 0) { | |
$comment = str_replace('# ', '', $route); | |
if (false !== strpos($comment, ' ')) { | |
$existingRoutes[] = substr($comment, 0, strpos($comment, ' ')); | |
} | |
} | |
} | |
$appRoutesHandle = fopen("{$appBase}config/routing.yml", 'a'); | |
foreach ($routesToAdd as $bundleName => $bundleResource) | |
{ | |
// $bundleName = self::getBundleName($routingConfig); | |
if (in_array($bundleName, $existingRoutes)) continue; | |
$routeComment = "# $bundleName generated routes\r\n"; | |
$routeName = strtolower(preg_replace('/\B([A-Z])/', '_$1', $bundleName)) . ":\r\n"; | |
$routeResource = " resource: \"$bundleResource\"\r\n\r\n"; | |
$routingConfig .= $routeComment; | |
$routingConfig .= $routeName; | |
$routingConfig .= $routeResource; | |
} | |
if (empty($routingConfig)) return; | |
echo "Appending routes to configuration.\r\n"; | |
fwrite($appRoutesHandle, $routingConfig); | |
} | |
private static function updateAppKernel() | |
{ | |
// Automagically pull in required dependancies via SensioGeneratorBundle's KernelManipulator | |
$bundlesToLoad = self::getBundleNames(); | |
$appBase = substr(__DIR__, 0, strpos(__DIR__, 'vendor/')) . "app/"; | |
require_once $appBase."AppKernel.php"; | |
$kernel = new \AppKernel('dev', true); | |
$kernelManipulator = new KernelManipulator($kernel); | |
foreach ($bundlesToLoad as $bundle) | |
{ | |
try { | |
$kernelManipulator->addBundle($bundle); | |
echo "Enabled bundle ".$bundle." in AppKernel.\r\n"; | |
} catch (\RuntimeException $e) { | |
// Catch already loaded bundles and prevent a runtime exception | |
echo "Bundle ".$bundle." was already enabled in AppKernel, skipping.\r\n"; | |
} | |
} | |
} | |
private static function registerPackages($package) { | |
foreach ($package->getRequires() as $key => $packageLink) | |
{ | |
if (FALSE !== strpos($key, 'bundle')) { | |
$package = self::getPackage($key); | |
self::$packagesToEnable[$key] = $package; | |
// Recursively register dependant bundles -- @todo: may need to be cygnus only? | |
$subRequires = $package->getRequires(); | |
if (!empty($subRequires)) { | |
self::registerPackages($package); | |
} | |
} | |
} | |
} | |
private static function getPackage($name, $version = null) | |
{ | |
if (is_null($version)) | |
{ | |
$version = self::findLockedPackageVersion($name); | |
} | |
return self::$lockedRepository->findPackage($name, $version); | |
} | |
private static function findLockedPackageVersion(&$name) | |
{ | |
$lockedPackages = self::$lockedRepository->getPackages(); | |
$version = null; | |
foreach ($lockedPackages as $package) | |
{ | |
if ($name == $package->getName() || $name == $package->getPrettyName()) { | |
return $package->getPrettyVersion(); | |
} else if (in_array($name, $package->getNames())) { | |
$name = $package->getName(); | |
return $package->getPrettyVersion(); | |
} else { | |
$names = $package->getNames(); | |
} | |
} | |
throw new \Exception('Unable to find installed/locked version of '.$name.' package.'); | |
} | |
/** | |
* getClassNameFromFile | |
* https://github.com/WouterJ/shift-php/blob/master/src/Wj/Shift/Bundle/Bundle.php#L41-84 | |
* @license https://github.com/WouterJ/shift-php/blob/master/LICENSE | |
* | |
* @param string $file File to parse | |
* | |
* @access private | |
* @static | |
* | |
* @return string Class Name | |
*/ | |
private static function getClassNameFromFile($file) | |
{ | |
$code = file_get_contents($file); | |
$tokens = token_get_all($code); | |
$namespace = ''; | |
$class = ''; | |
$ns_token = false; | |
$class_token = false; | |
foreach ($tokens as $token) { | |
if (!is_array($token)) { | |
continue; | |
} | |
if (\T_NAMESPACE === $token[0]) { | |
$ns_token = true; | |
$class_token = false; | |
continue; | |
} elseif (\T_CLASS === $token[0]) { | |
$class_token = true; | |
$ns_token = false; | |
continue; | |
} | |
if ($ns_token) { | |
if (\T_WHITESPACE === $token[0] || \T_NS_SEPARATOR === $token[0] || \T_STRING === $token[0]) { | |
$namespace .= $token[1]; | |
} else { | |
$ns_token = false; | |
} | |
} | |
if ($class_token) { | |
if (\T_WHITESPACE === $token[0] || \T_STRING === $token[0]) { | |
$class .= $token[1]; | |
} else { | |
$class_token = false; | |
break; | |
} | |
} | |
} | |
return $class == '' ? false : ltrim(trim($namespace).'\\'.trim($class), '\\'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment