loopit-8.x-1.x-dev/loopit.drush.inc

loopit.drush.inc
<?php

use Drupal\loopit\Aggregate\AggregateEntity;
use Drupal\loopit\Aggregate\AggregatePlugin;
use Drupal\loopit\Aggregate\AggregateService;

function loopit_drush_command() {
  $items['method-overrides'] = array(
    'description' => 'Compare overrided Plugin/Service methods on core/contrib updates',
    'arguments' => array(
    ),
    'aliases' => ['m-ovrd'],
  );

  return $items;
}

function drush_loopit_method_overrides() {

  foreach (['public://method_override', 'public://method_parent'] as $path) {
    file_unmanaged_delete_recursive($path);
  }

  $aggreg = AggregateEntity::getClasses([
    '*/\*handlers' => '__custom__',
    '*/\*class' => '__custom__',
    '*/\*provider' => '__custom__',
  ]);
  $context = $aggreg->getContext();

  $cid_plugin = 'loopit:aggregate:plugin_definitions';
  $cache_plugin = \Drupal::cache()->get($cid_plugin);
  $aggreg = AggregatePlugin::getClasses(['*class' => '__custom__', '*provider' => '__custom__', '*deriver' => '__custom__']);
  $context += $aggreg->getContext();

  // TODO: The command can't finish if at least one chache is missing for plugins and services.
  // Just run it again
  $cid_service = 'loopit:aggregate:service_definitions';
  $cache_service = \Drupal::cache()->get($cid_service);
  if (!$cache_service && !$cache_plugin) {
    drush_log(t('Run this command again (some cache was missing, but have been just created)'), 'error');
    return;
  }
  $aggreg = AggregateService::getClasses(['*class' => '__custom__', '*Class' => '__custom__']);
  $context += $aggreg->getContext();
  $customs = $context['entity_class_centric'] +  $context['service_class_centric'] +  $context['plugin_class_centric'];

  ksort($customs);

  $codes1 = $codes2 = array();
  foreach($customs as $name => $def) {
    $def['class'] = $name;

    if(!class_exists($def['class'])) {
      continue;
    }
    /** @var $reflection \ReflectionClass */
    $reflection = new ReflectionClass($def['class']);

    $methods = $reflection->getMethods();
    $parent = $reflection->getParentClass();

    // Nothing to do if there is no parent
    if (!$parent || is_string($parent)) {

      drush_log(t('The custom service class @class has no parent class', ['@class' => $def['class']]), 'notice');
      continue;
    }
    // Override methods by class
    $overrides = array();
    foreach($methods as $method) {
      foreach($parent->getMethods() as $parent_method) {
        if(
            $method->name == $parent_method->name
            && $method->class == $def['class']
          ) {
          $overrides[] = array($method, $parent_method);
        }
      }
    }
    if (!$overrides) {
      drush_log(t('The inherit class @class has no override methods', ['@class' => $def['class']]), 'notice');
    }

    foreach($overrides as $override) {
      list($method, $parent_method) = $override;

      $reflection = new ReflectionClass($method->class);

      // TODO: special handling
      if($reflection->isTrait()) {

      }

      $filename = $method->getFileName();
      $start_line = $method->getStartLine() - 1; // it's actually - 1, otherwise you wont get the function() block
      $end_line = $method->getEndLine();
      $length = $end_line - $start_line;
      $source = file($filename);
      $web_root = \Drupal::root();
      if(($pos = strpos($method->getFileName(), $web_root)) === 0) {
        $relative_filename = substr($method->getFileName(), strlen($web_root)+1);

        // Init $codes1 (extender) class if not exists
        if(!isset($codes1[$relative_filename])) {
          $codes1[$relative_filename] = [
            '<?php' . "\n" .
            'namespace ' . $reflection->getNamespaceName() . ';' . "\n" .
            '' . "\n" .
            'class ' . $reflection->getShortName() . ' {' . "\n"
          ];
        }

        // Append $method code to $codes1
        $codes1[$relative_filename][$method->name] = "\n" . implode("", array_slice($source, $start_line, $length));

        $parent_method_reflection = new ReflectionClass($parent_method->class);
        if($parent_method_reflection->isInterface()) {

          unset($codes1[$relative_filename][$method->name]);
          continue;
        }
        $filename = $parent_method->getFileName();
        $start_line = $parent_method->getStartLine() - 1; // it's actually - 1, otherwise you wont get the function() block
        $end_line = $parent_method->getEndLine();
        $length = $end_line - $start_line;
        $source = file($filename);

        // Init $codes2 (parent) class if not exists
        if(!isset($codes2[$relative_filename])) {
          $codes2[$relative_filename] = '<?php' . "\n" .
          'namespace ' . $parent_method_reflection->getNamespaceName() . ';' . "\n" .
          '' . "\n" .
          'class ' . $parent_method_reflection->getShortName() . ' {' . "\n";
        }

        // Append parent $method code to $codes2
        // TODO: #critical: what about if the method is in the grand parent?
        $codes2[$relative_filename] .= "\n" . implode("", array_slice($source, $start_line, $length));
        $code2 = $codes2[$relative_filename];
      }
      else {
        // TODO: #major: How to for overrides in symlinks: ex. /var/www/html/develxp/
        //   Need to get the relative drupal path
        //   Use the psr4 root namespace from $class_loader_casted['*decorated']['-prefixDirsPsr4'] from \Drupal::service('class_loader');
        //   - Drupal\search_krumo\ => /var/www/html/dnd/web/modules/custom/search_krumo/src
        //     for class Drupal\search_krumo\Plugin\Devel\Dumper\KrumoDebug => psr4 is str_replace \ / ?
        //     for realfile /var/www/html/develxp/src/Plugin/Devel/Dumper/KrumoDebug.php
        drush_log(t('Method @method from file @file is not in webroot @webroot', [
          '@method' => $method->name,
          '@file' => $method->getFileName(),
          '@webroot' => $web_root
        ]), 'warning');
      }
    }
  }

  $dirnames = [];
  foreach ($codes1 as $relative_filename => $code1) {

    if (count($code1) == 1) {
      drush_log(t('All parent methods are abstract for class @class', ['@class' => $relative_filename]), 'notice');
      continue;
    }
    $code1 = implode('', $code1);

    // Close $code1 classe
    $code1 .= "\n" . '}' . "\n";

    // Close $code2 classe
    $code2 = $codes2[$relative_filename];
    $code2 .= "\n" . '}' . "\n";

    $filename1 = 'public://method_override/' . $relative_filename;
    $filename2 = 'public://method_parent/' . $relative_filename;
    $dirname1 = substr($filename1, 0, strrpos($filename1, '/'));

    // Delete old files (init) for $dirname1
    if(!isset($dirnames[$dirname1])) {
      //rmdir($dirname1);
      file_prepare_directory($dirname1, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
      foreach(glob(drupal_realpath($dirname1) . '/*') as $todelete) {
        if (!is_dir($todelete)) {
          unlink($todelete);
        }
      }
      $dirnames[$dirname1] = $dirname1;
      //echo $dirname1 . "\n";
    }
    $dirname2 = substr($filename2, 0, strrpos($filename2, '/'));

    // Delete old files (init) for $dirname2
    if(!isset($dirnames[$dirname2])) {
      //rmdir($dirname2);
      file_prepare_directory($dirname2, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
      foreach(glob(drupal_realpath($dirname2) . '/*') as $todelete) {
        if (!is_dir($todelete)) {
          unlink($todelete);
        }
      }
      $dirnames[$dirname2] = $dirname2;

    }

    // Write files
    file_put_contents($filename1, $code1);
//     drush_log(t('Source class written to file @file', ['@file' => $filename1]), 'success');
    file_put_contents($filename2, $code2);
//     drush_log(t('Extended class written to file @file', ['@file' => $filename2]), 'success');

  }

  $public_files = \Drupal::service('file_system')->realpath('public://');
  drush_log('meld ' . $public_files . '/method_override/ ' . $public_files . '/method_parent/', 'notice');
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc