sfc-8.x-1.3/src/Plugin/SingleFileComponent/SimpleComponent.php

src/Plugin/SingleFileComponent/SimpleComponent.php
<?php

namespace Drupal\sfc\Plugin\SingleFileComponent;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;
use Drupal\sfc\ComponentBase;
use Drupal\sfc\Plugin\Derivative\SimpleComponentDeriver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Contains a component that renders based on the contents of a .sfc file.
 *
 * @SingleFileComponent(
 *   id = "sfc_simple_component",
 *   deriver = "\Drupal\sfc\Plugin\Derivative\SimpleComponentDeriver",
 * )
 */
class SimpleComponent extends ComponentBase {

  /**
   * An array of file data.
   *
   * @var array
   */
  protected $fileData;

  /**
   * Loads, parses, and returns simple file data.
   *
   * @return array
   *   An array containing keys that match up with ComponentBase's constants.
   */
  protected function getFileData() {
    if (!empty($this->fileData)) {
      return $this->fileData;
    }

    $contents = sfc_require($this->pluginDefinition['simple_file']);
    // These default values mimic ComponentBase constants/methods.
    $new_data = [
      'JS' => NULL,
      'ATTACH' => NULL,
      'DETACH' => NULL,
      'VANILLA_JS' => FALSE,
      'CSS' => NULL,
      'TEMPLATE' => '',
      'SELECTOR' => NULL,
      'DEPENDENCIES' => NULL,
      'LIBRARY' => NULL,
      'prepareContext' => NULL,
      'buildContextForm' => NULL,
      'validateContextForm' => NULL,
      'submitContextForm' => NULL,
      'actions' => [],
    ];
    // Parse HTML content of file.
    $this->parseFileHtml($contents['content'], $new_data);
    // Parse variables set with PHP.
    if (isset($contents['selector'])) {
      $new_data['SELECTOR'] = $contents['selector'];
    }
    if (isset($contents['dependencies'])) {
      $new_data['DEPENDENCIES'] = $contents['dependencies'];
    }
    if (isset($contents['library'])) {
      $new_data['LIBRARY'] = $contents['library'];
    }
    $callbacks = [
      'prepareContext',
      'buildContextForm',
      'validateContextForm',
      'submitContextForm',
    ];
    foreach ($callbacks as $callback) {
      if (isset($contents[$callback]) && is_callable($contents[$callback])) {
        $new_data[$callback] = $contents[$callback];
      }
    }
    if (isset($contents['actions']) && is_array($contents['actions'])) {
      $new_data['actions'] = $contents['actions'];
    }
    $this->fileData = $new_data;
    return $this->fileData;
  }

  /**
   * Parses the HTML content of the .sfc file.
   *
   * @param string $content
   *   The HTML content of an .sfc file.
   * @param array $file_data
   *   The file data array.
   */
  protected function parseFileHtml($content, array &$file_data) {
    if (preg_match('/^<script>([\s\S]+?)^<\/script>/im', $content, $matches)) {
      $file_data['JS'] = $matches[1];
    }
    if (preg_match('/^<script[^>]*data-type="attach"[^>]*>([\s\S]+?)^<\/script>/im', $content, $matches)) {
      $file_data['ATTACH'] = $matches[1];
    }
    if (preg_match('/^<script[^>]*data-type="detach"[^>]*>([\s\S]+?)^<\/script>/im', $content, $matches)) {
      $file_data['DETACH'] = $matches[1];
    }
    if (preg_match('/^<script[^>]*data-vanilla[^>]*>/im', $content)) {
      $file_data['VANILLA_JS'] = TRUE;
    }
    if (preg_match('/^<style[^>]*>([\s\S]+?)^<\/style>/im', $content, $matches)) {
      $file_data['CSS'] = $matches[1];
    }
    if (preg_match('/^<template[^>]*>([\s\S]+?)^<\/template>/im', $content, $matches)) {
      $file_data['TEMPLATE'] = trim($matches[1], "\n");
    }
  }

  /**
   * {@inheritdoc}
   */
  public function prepareContext(array &$context) {
    $context['sfc_unique_id'] = self::determineUniqueId($context);
    $attributes = new Attribute();
    $attributes->setAttribute('data-sfc-id', $this->getId());
    $attributes->setAttribute('data-sfc-unique-id', $context['sfc_unique_id']);
    $context['sfc_attributes'] = $attributes;
    $data = $this->getFileData();
    $action_ids = array_keys($data['actions']);
    foreach ($action_ids as $name) {
      $url = new Url('sfc.action', [
        'component_id' => $this->getId(),
        'action' => $name,
      ]);
      $url->setOption('query', ['sfc_unique_id' => $context['sfc_unique_id']]);
      $context['sfc_actions'][$name] = $url;
    }
    if ($data['prepareContext']) {
      $this->autowireCall($data['prepareContext'], $this->pluginDefinition['callback_autowire']['prepareContext'], [&$context]);
    }
  }

  /**
   * Calls a callback using dependency injection, similar to Symfony Autowiring.
   *
   * @param callable $callback
   *   The callback.
   * @param array $autowire_args
   *   An array mapping argument indexes to service IDs.
   * @param array $defaults
   *   An array mapping argument indexes or service IDs to default values.
   *
   * @return mixed
   *   The return value of $callback.
   */
  protected function autowireCall(callable $callback, array $autowire_args, array $defaults) {
    if (!isset($defaults[SimpleComponentDeriver::AUTOWIRE_CURRENT_REQUEST])) {
      $defaults[SimpleComponentDeriver::AUTOWIRE_CURRENT_REQUEST] = \Drupal::request();
    }
    $args = [];
    foreach ($autowire_args as $i => $service_id) {
      if (isset($defaults[$i])) {
        $args[$i] = &$defaults[$i];
      }
      elseif (isset($defaults[$service_id])) {
        $args[$i] = $defaults[$service_id];
      }
      else {
        $args[$i] = \Drupal::getContainer()->get($service_id);
      }
    }
    return call_user_func_array($callback, $args);
  }

  /**
   * {@inheritdoc}
   */
  public function performAction($name, Request $request) {
    $data = $this->getFileData();
    if (isset($data['actions'][$name]) && is_callable($data['actions'][$name])) {
      return $this->autowireCall($data['actions'][$name], $this->pluginDefinition['action_autowire'][$name], [SimpleComponentDeriver::AUTOWIRE_CURRENT_REQUEST => $request]);
    }
    throw new NotFoundHttpException('Action not found');
  }

  /**
   * {@inheritdoc}
   */
  protected function getAttachmentData() {
    $data = $this->getFileData();
    return [
      'selector' => $this::SELECTOR ? $this::SELECTOR : $this->getFallBackSelector(),
      'attach' => $data['ATTACH'],
      'detach' => $data['DETACH'],
      'vanilla_js' => $data['VANILLA_JS'],
    ];
  }

  /**
   * {@inheritdoc}
   */
  protected function getCss() {
    return $this->replaceCssPaths($this->getFileData()['CSS']);
  }

  /**
   * {@inheritdoc}
   */
  protected function getJs() {
    return $this->getFileData()['JS'];
  }

  /**
   * {@inheritdoc}
   */
  protected function getDependencies() {
    return $this->getFileData()['DEPENDENCIES'];
  }

  /**
   * {@inheritdoc}
   */
  protected function getTemplateData() {
    return $this->getFileData()['TEMPLATE'];
  }

  /**
   * {@inheritdoc}
   */
  public function getComponentFileName() {
    return $this->pluginDefinition['simple_file'];
  }

  /**
   * {@inheritdoc}
   */
  protected function hasDependencies() {
    return (bool) $this->getFileData()['DEPENDENCIES'];
  }

  /**
   * {@inheritdoc}
   */
  protected function hasAttachments() {
    $data = $this->getFileData();
    return $data['ATTACH'] || $data['DETACH'];
  }

  /**
   * {@inheritdoc}
   */
  protected function hasCss() {
    return (bool) $this->getFileData()['CSS'];
  }

  /**
   * {@inheritdoc}
   */
  protected function hasJs() {
    return (bool) $this->getFileData()['JS'];
  }

  /**
   * {@inheritdoc}
   */
  protected function hasLibraryData() {
    return (bool) $this->getFileData()['LIBRARY'] || !empty($this->getLocalLibraryData());
  }

  /**
   * {@inheritdoc}
   */
  protected function getLibraryData() {
    $local_library = $this->getLocalLibraryData();
    $library = $this->getFileData()['LIBRARY'];

    if ($library) {
      if (isset($library['css'])) {
        foreach ($library['css'] as &$files) {
          $this->processLibraryFiles($files);
        }
      }
      if (isset($library['js'])) {
        $this->processLibraryFiles($library['js']);
      }
    }
    else {
      $library = [];
    }

    if ($local_library) {
      if (!empty($library['dependencies'])) {
        unset($local_library['dependencies']);
      }
      $library = array_merge_recursive($library, $local_library);
    }

    return $library;
  }

  /**
   * Returns a library definition if <plugin_id>.<css|js> files exist.
   *
   * @return array
   *   A library definition.
   */
  protected function getLocalLibraryData() {
    $parts = pathinfo($this->getComponentFileName());
    $prefix = $parts['dirname'] . '/' . $parts['filename'];
    $css = $prefix . '.css';
    $js = $prefix . '.js';
    $library = [];
    if (file_exists($css)) {
      $relative_css = str_replace($this->appRoot, '', $css);
      $library['css'] = [
        'base' => [
          $relative_css => [],
        ],
      ];
    }
    if (file_exists($js)) {
      $relative_js = str_replace($this->appRoot, '', $js);
      $library['js'] = [
        $relative_js => [],
      ];
      $contents = file_get_contents($js);
      $dependencies = [];
      $depmap = [
        'once(' => 'core/once',
        'jQuery' => 'core/jquery',
        'Drupal.ajax' => 'core/drupal.ajax',
        'Drupal.Ajax' => 'core/drupal.ajax',
        'drupalSettings' => 'core/drupalSettings',
        'Drupal.' => 'core/drupal',
      ];
      foreach ($depmap as $search => $dep) {
        if (strpos($contents, $search) !== FALSE) {
          $dependencies[] = $dep;
        }
      }
      $library['dependencies'] = $dependencies;
    }
    return $library;
  }

  /**
   * {@inheritdoc}
   */
  public function buildContextForm(array $form, FormStateInterface $form_state, array $default_values = []) {
    if ($callback = $this->getFileData()['buildContextForm']) {
      $form = $this->autowireCall($callback, $this->pluginDefinition['callback_autowire']['buildContextForm'], [
        $form,
        $form_state,
        $default_values,
      ]);
    }
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateContextForm(array &$form, FormStateInterface $form_state) {
    if ($callback = $this->getFileData()['validateContextForm']) {
      $this->autowireCall($callback, $this->pluginDefinition['callback_autowire']['validateContextForm'], [
        $form,
        $form_state,
      ]);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitContextForm(array &$form, FormStateInterface $form_state) {
    if ($callback = $this->getFileData()['submitContextForm']) {
      $this->autowireCall($callback, $this->pluginDefinition['callback_autowire']['submitContextForm'], [
        $form,
        $form_state,
      ]);
    }
  }

  /**
   * Determines the unique ID for a component.
   *
   * @param array $context
   *   The context being passed to the Twig template.
   *
   * @return string
   *   The unique ID.
   */
  protected function determineUniqueId(array $context) {
    $unique_ids = &drupal_static(__FUNCTION__, []);

    // Parent components may unintentionally pass their unique IDs to children.
    $id = !isset($context['sfc_unique_id']) || in_array($context['sfc_unique_id'], $unique_ids, TRUE) ? uniqid($this->getId()) : $context['sfc_unique_id'];

    $unique_ids[] = $id;
    return $id;
  }

}

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

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