--- /dev/null
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Console\Utils\Site.
+ */
+
+namespace Drupal\Console\Utils;
+
+use Drupal\Core\Cache\Cache;
+use Symfony\Component\DomCrawler\Crawler;
+use GuzzleHttp\Client;
+
+/**
+ * Class DrupalHelper
+ *
+ * @package Drupal\Console\Utils
+ */
+class DrupalApi
+{
+ protected $appRoot;
+ protected $entityTypeManager;
+
+ private $caches = [];
+ private $bundles = [];
+ private $vocabularies = [];
+ private $roles = [];
+
+ /**
+ * DebugCommand constructor.
+ *
+ * @param Client $httpClient
+ */
+
+ protected $httpClient;
+
+ /**
+ * ServerCommand constructor.
+ *
+ * @param $appRoot
+ * @param $entityTypeManager
+ */
+ public function __construct($appRoot, $entityTypeManager, Client $httpClient)
+ {
+ $this->appRoot = $appRoot;
+ $this->entityTypeManager = $entityTypeManager;
+ $this->httpClient = $httpClient;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDrupalVersion()
+ {
+ return \Drupal::VERSION;
+ }
+
+ /**
+ * Auxiliary function to get all available drupal caches.
+ *
+ * @return array The all available drupal caches
+ */
+ public function getCaches()
+ {
+ if (!$this->caches) {
+ foreach (Cache::getBins() as $name => $bin) {
+ $this->caches[$name] = $bin;
+ }
+ }
+
+ return $this->caches;
+ }
+
+ /**
+ * Validate if a string is a valid cache.
+ *
+ * @param string $cache The cache name
+ *
+ * @return mixed The cache name if valid or FALSE if not valid
+ */
+ public function isValidCache($cache)
+ {
+ // Get the valid caches
+ $caches = $this->getCaches();
+ $cacheKeys = array_keys($caches);
+ $cacheKeys[] = 'all';
+
+ if (!in_array($cache, array_values($cacheKeys))) {
+ return false;
+ }
+
+ return $cache;
+ }
+
+ /**
+ * @return array
+ */
+ public function getBundles()
+ {
+ if (!$this->bundles) {
+ $nodeTypes = $this->entityTypeManager->getStorage('node_type')->loadMultiple();
+
+ foreach ($nodeTypes as $nodeType) {
+ $this->bundles[$nodeType->id()] = $nodeType->label();
+ }
+ }
+
+ return $this->bundles;
+ }
+
+ /**
+ * @return array
+ */
+ public function getVocabularies()
+ {
+ if (!$this->vocabularies) {
+ $vocabularies = $this->entityTypeManager->getStorage('taxonomy_vocabulary')->loadMultiple();
+
+ foreach ($vocabularies as $vocabulary) {
+ $this->vocabularies[$vocabulary->id()] = $vocabulary->label();
+ }
+ }
+
+ return $this->vocabularies;
+ }
+
+ /**
+ * @param bool|FALSE $reset
+ * @param bool|FALSE $authenticated
+ * @param bool|FALSE $anonymous
+ *
+ * @return array
+ */
+ public function getRoles($reset=false, $authenticated=true, $anonymous=false)
+ {
+ if ($reset || !$this->roles) {
+ $roles = $this->entityTypeManager->getStorage('user_role')->loadMultiple();
+ if (!$authenticated) {
+ unset($roles['authenticated']);
+ }
+ if (!$anonymous) {
+ unset($roles['anonymous']);
+ }
+ foreach ($roles as $role) {
+ $this->roles[$role->id()] = $role->label();
+ }
+ }
+
+ return $this->roles;
+ }
+
+ /**
+ * @param $module
+ * @param $limit
+ * @param $stable
+ * @return array
+ * @throws \Exception
+ */
+ public function getProjectReleases($module, $limit = 10, $stable = false)
+ {
+ if (!$module) {
+ return [];
+ }
+
+ $projectPageResponse = $this->httpClient->getUrlAsString(
+ sprintf(
+ 'https://updates.drupal.org/release-history/%s/8.x',
+ $module
+ )
+ );
+
+ if ($projectPageResponse->getStatusCode() != 200) {
+ throw new \Exception('Invalid path.');
+ }
+
+ $releases = [];
+ $crawler = new Crawler($projectPageResponse->getBody()->getContents());
+ $filter = './project/releases/release/version';
+ if ($stable) {
+ $filter = './project/releases/release[not(version_extra)]/version';
+ }
+
+ foreach ($crawler->filterXPath($filter) as $element) {
+ $releases[] = $element->nodeValue;
+ }
+
+ if (count($releases)>$limit) {
+ array_splice($releases, $limit);
+ }
+
+ return $releases;
+ }
+
+ /**
+ * @param $project
+ * @param $release
+ * @param null $destination
+ * @return null|string
+ */
+ public function downloadProjectRelease($project, $release, $destination = null)
+ {
+ if (!$release) {
+ $releases = $this->getProjectReleases($project, 1);
+ $release = current($releases);
+ }
+
+ if (!$destination) {
+ $destination = sprintf(
+ '%s/%s.tar.gz',
+ sys_get_temp_dir(),
+ $project
+ );
+ }
+
+ $releaseFilePath = sprintf(
+ 'https://ftp.drupal.org/files/projects/%s-%s.tar.gz',
+ $project,
+ $release
+ );
+
+ if ($this->downloadFile($releaseFilePath, $destination)) {
+ return $destination;
+ }
+
+ return null;
+ }
+
+ public function downloadFile($url, $destination)
+ {
+ $this->httpClient->get($url, ['sink' => $destination]);
+
+ return file_exists($destination);
+ }
+
+ /**
+ * Gets Drupal modules releases from Packagist API.
+ *
+ * @param string $module
+ * @param int $limit
+ * @param bool $unstable
+ *
+ * @return array
+ */
+ public function getPackagistModuleReleases($module, $limit = 10, $unstable = true)
+ {
+ if (!trim($module)) {
+ return [];
+ }
+
+ return $this->getComposerReleases(
+ sprintf(
+ 'http://packagist.drupal-composer.org/packages/drupal/%s.json',
+ trim($module)
+ ),
+ $limit,
+ $unstable
+ );
+ }
+
+ /**
+ * Gets Drupal releases from Packagist API.
+ *
+ * @param string $url
+ * @param int $limit
+ * @param bool $unstable
+ *
+ * @return array
+ */
+ private function getComposerReleases($url, $limit = 10, $unstable = false)
+ {
+ if (!$url) {
+ return [];
+ }
+
+ $packagistResponse = $this->httpClient->getUrlAsString($url);
+
+ if ($packagistResponse->getStatusCode() != 200) {
+ throw new \Exception('Invalid path.');
+ }
+
+ try {
+ $packagistJson = json_decode(
+ $packagistResponse->getBody()->getContents()
+ );
+ } catch (\Exception $e) {
+ return [];
+ }
+
+ $versions = array_keys((array)$packagistJson->package->versions);
+
+ // Remove Drupal 7 versions
+ $i = 0;
+ foreach ($versions as $version) {
+ if (0 === strpos($version, "7.") || 0 === strpos($version, "dev-7.")) {
+ unset($versions[$i]);
+ }
+ $i++;
+ }
+
+ if (!$unstable) {
+ foreach ($versions as $key => $version) {
+ if (strpos($version, "-")) {
+ unset($versions[$key]);
+ }
+ }
+ }
+
+ if (is_array($versions)) {
+ return array_slice($versions, 0, $limit);
+ }
+
+ return [];
+ }
+
+ /**
+ * @Todo: Remove when issue https://www.drupal.org/node/2556025 get resolved
+ *
+ * Rebuilds all caches even when Drupal itself does not work.
+ *
+ * @param \Composer\Autoload\ClassLoader $class_loader
+ * The class loader.
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The current request.
+ *
+ * @see rebuild.php
+ */
+ public function drupal_rebuild($class_loader, \Symfony\Component\HttpFoundation\Request $request)
+ {
+ // Remove Drupal's error and exception handlers; they rely on a working
+ // service container and other subsystems and will only cause a fatal error
+ // that hides the actual error.
+ restore_error_handler();
+ restore_exception_handler();
+
+ // Force kernel to rebuild php cache.
+ \Drupal\Core\PhpStorage\PhpStorageFactory::get('twig')->deleteAll();
+
+ // Bootstrap up to where caches exist and clear them.
+ $kernel = new \Drupal\Core\DrupalKernel('prod', $class_loader);
+ $kernel->setSitePath(\Drupal\Core\DrupalKernel::findSitePath($request));
+
+ // Invalidate the container.
+ $kernel->invalidateContainer();
+
+ // Prepare a NULL request.
+ $kernel->prepareLegacyRequest($request);
+
+ foreach (Cache::getBins() as $bin) {
+ $bin->deleteAll();
+ }
+
+ // Disable recording of cached pages.
+ \Drupal::service('page_cache_kill_switch')->trigger();
+
+ drupal_flush_all_caches();
+
+ // Restore Drupal's error and exception handlers.
+ // @see \Drupal\Core\DrupalKernel::boot()
+ set_error_handler('_drupal_error_handler');
+ set_exception_handler('_drupal_exception_handler');
+ }
+}