View source
<?php
namespace Drupal\imagemagick\Plugin\ImageToolkit;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Timer;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\ImageToolkit\ImageToolkitBase;
use Drupal\Core\ImageToolkit\ImageToolkitOperationManagerInterface;
use Drupal\Core\Url;
use Drupal\imagemagick\ImagemagickFormatMapperInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ImagemagickToolkit extends ImageToolkitBase {
protected $isWindows;
protected $moduleHandler;
protected $formatMapper;
protected $appRoot;
protected $arguments = [];
protected $width;
protected $height;
protected $frames;
protected $sourceLocalPath = '';
protected $sourceFormat = '';
protected $exifInfo = [];
protected $destination = NULL;
protected $destinationLocalPath = '';
protected $destinationFormat = '';
public function __construct(array $configuration, $plugin_id, array $plugin_definition, ImageToolkitOperationManagerInterface $operation_manager, LoggerInterface $logger, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ImagemagickFormatMapperInterface $format_mapper, $app_root) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $operation_manager, $logger, $config_factory);
$this->moduleHandler = $module_handler;
$this->formatMapper = $format_mapper;
$this->appRoot = $app_root;
$this->isWindows = substr(PHP_OS, 0, 3) === 'WIN';
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('image.toolkit.operation.manager'), $container
->get('logger.channel.image'), $container
->get('config.factory'), $container
->get('module_handler'), $container
->get('imagemagick.format_mapper'), $container
->get('app.root'));
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$config = $this->configFactory
->getEditable('imagemagick.settings');
$form['imagemagick'] = [
'#markup' => $this
->t("<a href=':im-url'>ImageMagick</a> and <a href=':gm-url'>GraphicsMagick</a> are stand-alone packages for image manipulation. At least one of them must be installed on the server, and you need to know where it is located. Consult your server administrator or hosting provider for details.", [
':im-url' => 'http://www.imagemagick.org',
':gm-url' => 'http://www.graphicsmagick.org',
]),
];
$form['quality'] = [
'#type' => 'number',
'#title' => $this
->t('Image quality'),
'#size' => 10,
'#min' => 0,
'#max' => 100,
'#maxlength' => 3,
'#default_value' => $config
->get('quality'),
'#field_suffix' => '%',
'#description' => $this
->t('Define the image quality of processed images. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
];
$form['suite'] = [
'#type' => 'details',
'#open' => TRUE,
'#collapsible' => FALSE,
'#title' => $this
->t('Graphics package'),
];
$options = [
'imagemagick' => $this
->getPackageLabel('imagemagick'),
'graphicsmagick' => $this
->getPackageLabel('graphicsmagick'),
];
$form['suite']['binaries'] = [
'#type' => 'radios',
'#title' => $this
->t('Suite'),
'#default_value' => $this
->getPackage(),
'#options' => $options,
'#required' => TRUE,
'#description' => $this
->t("Select the graphics package to use."),
];
$form['suite']['path_to_binaries'] = [
'#type' => 'textfield',
'#title' => $this
->t('Path to the package executables'),
'#default_value' => $config
->get('path_to_binaries'),
'#required' => FALSE,
'#description' => $this
->t('If needed, the path to the package executables (<kbd>convert</kbd>, <kbd>identify</kbd>, <kbd>gm</kbd>, etc.), <b>including</b> the trailing slash/backslash. For example: <kbd>/usr/bin/</kbd> or <kbd>C:\\Program Files\\ImageMagick-6.3.4-Q16\\</kbd>.'),
];
$status = $this
->checkPath($config
->get('path_to_binaries'));
if (empty($status['errors'])) {
$version_info = explode("\n", preg_replace('/\\r/', '', Html::escape($status['output'])));
}
else {
$version_info = $status['errors'];
}
$form['suite']['version'] = [
'#type' => 'details',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#title' => $this
->t('Version information'),
'#description' => '<pre>' . implode('<br />', $version_info) . '</pre>',
];
$form['formats'] = [
'#type' => 'details',
'#open' => TRUE,
'#collapsible' => FALSE,
'#title' => $this
->t('Image formats'),
];
$form['formats']['use_identify'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Use "identify"'),
'#default_value' => $config
->get('use_identify'),
'#description' => $this
->t('Use the <kbd>identify</kbd> command to parse image files to determine image format and dimensions. If not selected, the PHP <kbd>getimagesize</kbd> function will be used, BUT this will limit the image formats supported by the toolkit.'),
];
$form['formats']['enabled'] = [
'#type' => 'item',
'#title' => $this
->t('Enabled images'),
'#description' => $this
->t("@suite formats: %formats<br />Image file extensions: %extensions", [
'%formats' => implode(', ', $this->formatMapper
->getEnabledFormats()),
'%extensions' => Unicode::strtolower(implode(', ', static::getSupportedExtensions())),
'@suite' => $this
->getPackageLabel(),
]),
];
$form['formats']['mapping'] = [
'#type' => 'details',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#title' => $this
->t('Enable/disable image formats'),
'#description' => $this
->t("Edit the map below to enable/disable image formats. Enabled image file extensions will be determined by the enabled formats, through their MIME types. More information in the module's README.txt"),
];
$form['formats']['mapping']['image_formats'] = [
'#type' => 'textarea',
'#rows' => 15,
'#default_value' => Yaml::encode($config
->get('image_formats')),
];
if (empty($status['errors'])) {
$this
->addArgument('-list format');
$this
->imagemagickExec('convert', $output);
$this
->resetArguments();
$formats_info = implode('<br />', explode("\n", preg_replace('/\\r/', '', Html::escape($output))));
$form['formats']['list'] = [
'#type' => 'details',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#title' => $this
->t('Format list'),
'#description' => $this
->t("Supported image formats returned by executing <kbd>'convert -list format'</kbd>. <b>Note:</b> these are the formats supported by the installed @suite executable, <b>not</b> by the toolkit.<br /><br />", [
'@suite' => $this
->getPackageLabel(),
]),
];
$form['formats']['list']['list'] = [
'#markup' => "<pre>" . $formats_info . "</pre>",
];
}
$form['exec'] = [
'#type' => 'details',
'#open' => TRUE,
'#collapsible' => FALSE,
'#title' => $this
->t('Execution options'),
];
$form['exec']['prepend'] = [
'#type' => 'textfield',
'#title' => $this
->t('Prepend arguments'),
'#default_value' => $config
->get('prepend'),
'#required' => FALSE,
'#description' => $this
->t('Use this to add e.g. <kbd>-limit</kbd> or <kbd>-debug</kbd> arguments in front of the others when executing the <kbd>identify</kbd> and <kbd>convert</kbd> commands.'),
];
$form['exec']['locale'] = [
'#type' => 'textfield',
'#title' => $this
->t('Locale'),
'#default_value' => $config
->get('locale'),
'#required' => FALSE,
'#description' => $this
->t("The locale to be used to prepare the command passed to executables. The default, <kbd>'en_US.UTF-8'</kbd>, should work in most cases. If that is not available on the server, enter another locale. On *nix servers, type <kbd>'locale -a'</kbd> in a shell window to see a list of all locales available."),
];
$form['exec']['log_warnings'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Log warnings'),
'#default_value' => $config
->get('log_warnings'),
'#description' => $this
->t('Log a warning entry in the watchdog when the execution of a command returns with a non-zero code, but no error message.'),
];
$form['exec']['debug'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Display debugging information'),
'#default_value' => $config
->get('debug'),
'#description' => $this
->t('Shows commands and their output to users with the %permission permission.', [
'%permission' => $this
->t('Administer site configuration'),
]),
];
$form['advanced'] = [
'#type' => 'details',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#title' => $this
->t('Advanced image settings'),
];
$form['advanced']['density'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Change image resolution to 72 ppi'),
'#default_value' => $config
->get('advanced.density'),
'#return_value' => 72,
'#description' => $this
->t("Resamples the image <a href=':help-url'>density</a> to a resolution of 72 pixels per inch, the default for web images. Does not affect the pixel size or quality.", [
':help-url' => 'http://www.imagemagick.org/script/command-line-options.php#density',
]),
];
$form['advanced']['colorspace'] = [
'#type' => 'select',
'#title' => $this
->t('Convert colorspace'),
'#default_value' => $config
->get('advanced.colorspace'),
'#options' => [
'RGB' => $this
->t('RGB'),
'sRGB' => $this
->t('sRGB'),
'GRAY' => $this
->t('Gray'),
],
'#empty_value' => 0,
'#empty_option' => $this
->t('- Original -'),
'#description' => $this
->t("Converts processed images to the specified <a href=':help-url'>colorspace</a>. The color profile option overrides this setting.", [
':help-url' => 'http://www.imagemagick.org/script/command-line-options.php#colorspace',
]),
'#states' => [
'enabled' => [
':input[name="imagemagick[advanced][profile]"]' => [
'value' => '',
],
],
],
];
$form['advanced']['profile'] = [
'#type' => 'textfield',
'#title' => $this
->t('Color profile path'),
'#default_value' => $config
->get('advanced.profile'),
'#description' => $this
->t("The path to a <a href=':help-url'>color profile</a> file that all processed images will be converted to. Leave blank to disable. Use a <a href=':color-url'>sRGB profile</a> to correct the display of professional images and photography.", [
':help-url' => 'http://www.imagemagick.org/script/command-line-options.php#profile',
':color-url' => 'http://www.color.org/profiles.html',
]),
];
return $form;
}
public function getPackage($package = NULL) {
if ($package === NULL) {
$package = $this->configFactory
->get('imagemagick.settings')
->get('binaries');
}
return $package;
}
public function getPackageLabel($package = NULL) {
switch ($this
->getPackage($package)) {
case 'imagemagick':
return $this
->t('ImageMagick');
case 'graphicsmagick':
return $this
->t('GraphicsMagick');
default:
return $package;
}
}
public function checkPath($path, $package = NULL) {
$status = [
'output' => '',
'errors' => [],
];
$package = $package ?: $this
->getPackage();
$binary = $package === 'imagemagick' ? 'convert' : 'gm';
$executable = $this
->getExecutable($binary, $path);
if (!empty($path)) {
if (!is_file($executable)) {
$status['errors'][] = $this
->t('The @suite executable %file does not exist.', [
'@suite' => $this
->getPackageLabel($package),
'%file' => $executable,
]);
}
elseif (!is_executable($executable)) {
$status['errors'][] = $this
->t('The @suite file %file is not executable.', [
'@suite' => $this
->getPackageLabel($package),
'%file' => $executable,
]);
}
}
if ($status['errors'] && ($open_basedir = ini_get('open_basedir'))) {
$status['errors'][] = $this
->t('The PHP <a href=":php-url">open_basedir</a> security restriction is set to %open-basedir, which may prevent to locate the @suite executable.', [
'@suite' => $this
->getPackageLabel($package),
'%open-basedir' => $open_basedir,
':php-url' => 'http://php.net/manual/en/ini.core.php#ini.open-basedir',
]);
}
if (!$status['errors']) {
$error = NULL;
$this
->runOsShell($executable, '-version', $package, $status['output'], $error);
if ($error !== '') {
$status['errors'][] = $error;
}
}
return $status;
}
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
try {
$image_formats = Yaml::decode($form_state
->getValue([
'imagemagick',
'formats',
'mapping',
'image_formats',
]));
$errors = $this->formatMapper
->validateMap($image_formats);
if ($errors) {
$form_state
->setErrorByName('imagemagick][formats][mapping][image_formats', new FormattableMarkup("<pre>@errors</pre>", [
'@errors' => Yaml::encode($errors),
]));
}
} catch (InvalidDataTypeException $e) {
$form_state
->setErrorByName('imagemagick][formats][mapping][image_formats', $this
->t("YAML syntax error: @error", [
'@error' => $e
->getMessage(),
]));
}
if ($form_state
->getValue([
'image_toolkit',
]) === 'imagemagick') {
$status = $this
->checkPath($form_state
->getValue([
'imagemagick',
'suite',
'path_to_binaries',
]), $form_state
->getValue([
'imagemagick',
'suite',
'binaries',
]));
if ($status['errors']) {
$form_state
->setErrorByName('imagemagick][suite][path_to_binaries', new FormattableMarkup(implode('<br />', $status['errors']), []));
}
}
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configFactory
->getEditable('imagemagick.settings')
->set('quality', $form_state
->getValue([
'imagemagick',
'quality',
]))
->set('binaries', $form_state
->getValue([
'imagemagick',
'suite',
'binaries',
]))
->set('path_to_binaries', $form_state
->getValue([
'imagemagick',
'suite',
'path_to_binaries',
]))
->set('use_identify', $form_state
->getValue([
'imagemagick',
'formats',
'use_identify',
]))
->set('image_formats', Yaml::decode($form_state
->getValue([
'imagemagick',
'formats',
'mapping',
'image_formats',
])))
->set('prepend', $form_state
->getValue([
'imagemagick',
'exec',
'prepend',
]))
->set('locale', $form_state
->getValue([
'imagemagick',
'exec',
'locale',
]))
->set('log_warnings', (bool) $form_state
->getValue([
'imagemagick',
'exec',
'log_warnings',
]))
->set('debug', $form_state
->getValue([
'imagemagick',
'exec',
'debug',
]))
->set('advanced.density', $form_state
->getValue([
'imagemagick',
'advanced',
'density',
]))
->set('advanced.colorspace', $form_state
->getValue([
'imagemagick',
'advanced',
'colorspace',
]))
->set('advanced.profile', $form_state
->getValue([
'imagemagick',
'advanced',
'profile',
]))
->save();
}
public function isValid() {
return (bool) $this
->getMimeType();
}
public function getSourceLocalPath() {
return $this->sourceLocalPath;
}
public function setSourceLocalPath($path) {
$this->sourceLocalPath = $path;
return $this;
}
public function getSourceFormat() {
return $this->sourceFormat;
}
public function setSourceFormat($format) {
$this->sourceFormat = $this->formatMapper
->isFormatEnabled($format) ? $format : '';
return $this;
}
public function setSourceFormatFromExtension($extension) {
$format = $this->formatMapper
->getFormatFromExtension($extension);
$this->sourceFormat = $format ?: '';
return $this;
}
public function getExifOrientation() {
if (empty($this->exifInfo)) {
$this
->parseExifData();
}
return isset($this->exifInfo['Orientation']) ? $this->exifInfo['Orientation'] : NULL;
}
public function setExifOrientation($exif_orientation) {
$this->exifInfo['Orientation'] = !empty($exif_orientation) ? (int) $exif_orientation !== 0 ? (int) $exif_orientation : NULL : NULL;
return $this;
}
public function getFrames() {
return $this->frames;
}
public function setFrames($frames) {
$this->frames = $frames;
return $this;
}
public function getDestination() {
return $this->destination;
}
public function setDestination($destination) {
$this->destination = $destination;
return $this;
}
public function getDestinationLocalPath() {
return $this->destinationLocalPath;
}
public function setDestinationLocalPath($path) {
$this->destinationLocalPath = $path;
return $this;
}
public function getDestinationFormat() {
return $this->destinationFormat;
}
public function setDestinationFormat($format) {
$this->destinationFormat = $this->formatMapper
->isFormatEnabled($format) ? $format : '';
return $this;
}
public function setDestinationFormatFromExtension($extension) {
$format = $this->formatMapper
->getFormatFromExtension($extension);
$this->destinationFormat = $format ?: '';
return $this;
}
public function getWidth() {
return $this->width;
}
public function setWidth($width) {
$this->width = $width;
return $this;
}
public function getHeight() {
return $this->height;
}
public function setHeight($height) {
$this->height = $height;
return $this;
}
public function getMimeType() {
return $this->formatMapper
->getMimeTypeFromFormat($this
->getSourceFormat());
}
public function getArguments() {
return $this->arguments ?: [];
}
public function addArgument($arg) {
$this->arguments[] = $arg;
return $this;
}
public function prependArgument($arg) {
array_unshift($this->arguments, $arg);
return $this;
}
public function findArgument($arg) {
foreach ($this
->getArguments() as $i => $a) {
if (strpos($a, $arg) === 0) {
return $i;
}
}
return FALSE;
}
public function removeArgument($index) {
if (isset($this->arguments[$index])) {
unset($this->arguments[$index]);
}
return $this;
}
public function resetArguments() {
$this->arguments = [];
return $this;
}
public function countArguments() {
return count($this->arguments);
}
public function escapeShellArg($arg) {
static $percentage_sign_replace_pattern = '1357902468IMAGEMAGICKPERCENTSIGNPATTERN8642097531';
static $config_locale;
if (!isset($config_locale)) {
$config_locale = $this->configFactory
->get('imagemagick.settings')
->get('locale');
if (empty($config_locale)) {
$config_locale = FALSE;
}
}
if ($this->isWindows) {
$arg = str_replace('%', $percentage_sign_replace_pattern, $arg);
}
if ($config_locale === FALSE) {
$arg_escaped = escapeshellarg($arg);
}
else {
$current_locale = setlocale(LC_CTYPE, 0);
if ($current_locale != $config_locale) {
setlocale(LC_CTYPE, $config_locale);
$arg_escaped = escapeshellarg($arg);
setlocale(LC_CTYPE, $current_locale);
}
else {
$arg_escaped = escapeshellarg($arg);
}
}
if ($this->isWindows) {
$arg_escaped = str_replace($percentage_sign_replace_pattern, '%', $arg_escaped);
}
return $arg_escaped;
}
public function save($destination) {
$this
->setDestination($destination);
if ($ret = $this
->convert()) {
$this->moduleHandler
->alter('imagemagick_post_save', $this);
$this
->setDestinationLocalPath('');
}
return $ret;
}
public function parseFile() {
$this->moduleHandler
->alter('imagemagick_pre_parse_file', $this);
if ($this->configFactory
->get('imagemagick.settings')
->get('use_identify')) {
return $this
->parseFileViaIdentify();
}
else {
return $this
->parseFileViaGetImageSize();
}
}
protected function parseFileViaIdentify() {
switch ($this
->getPackage()) {
case 'imagemagick':
$this
->addArgument('-format ' . $this
->escapeShellArg("format:%[magick]|width:%[width]|height:%[height]|exif_orientation:%[EXIF:Orientation]\\n"));
break;
case 'graphicsmagick':
$this
->addArgument('-format ' . $this
->escapeShellArg("format:%m|width:%w|height:%h|exif_orientation:%[EXIF:Orientation]\\n"));
break;
}
if ($identify_output = $this
->identify()) {
$frames = explode("\n", $identify_output);
while (empty($frames[count($frames) - 1])) {
array_pop($frames);
}
if (count($frames) > 1) {
$this
->setFrames(count($frames));
}
$info = explode('|', $frames[0]);
$data = [];
foreach ($info as $item) {
list($key, $value) = explode(':', $item);
$data[trim($key)] = trim($value);
}
$format = isset($data['format']) ? $data['format'] : NULL;
if ($this->formatMapper
->isFormatEnabled($format)) {
$this
->setSourceFormat($format)
->setWidth((int) $data['width'])
->setHeight((int) $data['height'])
->setExifOrientation($data['exif_orientation']);
return TRUE;
}
}
return FALSE;
}
protected function parseFileViaGetImageSize() {
if ($data = @getimagesize($this
->getSourceLocalPath())) {
$format = $this->formatMapper
->getFormatFromExtension(image_type_to_extension($data[2], FALSE));
if ($format) {
$this
->setSourceFormat($format)
->setWidth($data[0])
->setHeight($data[1]);
return TRUE;
}
}
return FALSE;
}
protected function parseExifData() {
$continue = TRUE;
$mime_type = $this
->getMimeType();
if (!in_array($mime_type, [
'image/jpeg',
'image/tiff',
])) {
$continue = FALSE;
}
$local_path = $this
->getSourceLocalPath();
if ($continue && empty($local_path)) {
$continue = FALSE;
}
if ($continue && !function_exists('exif_read_data')) {
$this->logger
->error('The PHP EXIF extension is not installed. The \'imagemagick\' toolkit is unable to automatically determine image orientation.');
$continue = FALSE;
}
if ($continue && ($exif_data = @exif_read_data($this
->getSourceLocalPath()))) {
$this->exifInfo = $exif_data;
return $this;
}
$this
->setExifOrientation(NULL);
return $this;
}
protected function identify() {
$command = 'identify';
$this->moduleHandler
->alter('imagemagick_arguments', $this, $command);
$output = NULL;
$ret = $this
->imagemagickExec($command, $output);
$this
->resetArguments();
return $ret === TRUE ? $output : FALSE;
}
protected function convert() {
$command = 'convert';
$this->moduleHandler
->alter('imagemagick_arguments', $this, $command);
return $this
->imagemagickExec($command) === TRUE ? file_exists($this
->getDestinationLocalPath()) : FALSE;
}
protected function imagemagickExec($command, &$output = NULL, &$error = NULL, $path = NULL) {
switch ($command) {
case 'convert':
$binary = $this
->getPackage() === 'imagemagick' ? 'convert' : 'gm';
break;
case 'identify':
$binary = $this
->getPackage() === 'imagemagick' ? 'identify' : 'gm';
break;
}
$cmd = $this
->getExecutable($binary, $path);
if ($source_path = $this
->getSourceLocalPath()) {
$source_path = $this
->escapeShellArg($source_path);
}
if ($destination_path = $this
->getDestinationLocalPath()) {
$destination_path = $this
->escapeShellArg($destination_path);
if (($format = $this
->getDestinationFormat()) !== '') {
$destination_path = $format . ':' . $destination_path;
}
}
switch ($command) {
case 'identify':
switch ($this
->getPackage()) {
case 'imagemagick':
$cmdline = implode(' ', $this
->getArguments()) . ' ' . $source_path;
break;
case 'graphicsmagick':
$cmdline = 'identify ' . implode(' ', $this
->getArguments()) . ' ' . $source_path;
break;
}
break;
case 'convert':
switch ($this
->getPackage()) {
case 'imagemagick':
$cmdline = $source_path . ' ' . implode(' ', $this
->getArguments()) . ' ' . $destination_path;
break;
case 'graphicsmagick':
$cmdline = 'convert ' . implode(' ', $this
->getArguments()) . ' ' . $source_path . ' ' . $destination_path;
break;
}
break;
}
$return_code = $this
->runOsShell($cmd, $cmdline, $this
->getPackage(), $output, $error);
if ($return_code !== FALSE) {
if ($return_code != 0) {
if ($error === '') {
if ($this->configFactory
->get('imagemagick.settings')
->get('log_warnings') === TRUE) {
$this->logger
->warning("@suite returned with code @code [command: @command @cmdline]", [
'@suite' => $this
->getPackageLabel(),
'@code' => $return_code,
'@command' => $cmd,
'@cmdline' => $cmdline,
]);
}
}
else {
$this->logger
->error("@suite error @code: @error [command: @command @cmdline]", [
'@suite' => $this
->getPackageLabel(),
'@code' => $return_code,
'@error' => $error,
'@command' => $cmd,
'@cmdline' => $cmdline,
]);
}
return $return_code;
}
return TRUE;
}
return FALSE;
}
protected function runOsShell($command, $arguments, $id, &$output = NULL, &$error = NULL) {
if ($this->isWindows) {
$command = 'start "' . $id . '" /D ' . $this
->escapeShellArg($this->appRoot) . ' /B ' . $this
->escapeShellArg($command);
}
$command_line = $command . ' ' . $arguments;
$descriptors = [
0 => [
'pipe',
'r',
],
1 => [
'pipe',
'w',
],
2 => [
'pipe',
'w',
],
];
Timer::start('imagemagick:runOsShell');
if ($h = proc_open($command_line, $descriptors, $pipes, $this->appRoot)) {
$output = '';
while (!feof($pipes[1])) {
$output .= fgets($pipes[1]);
}
$output = utf8_encode($output);
$error = '';
while (!feof($pipes[2])) {
$error .= fgets($pipes[2]);
}
$error = utf8_encode($error);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
$return_code = proc_close($h);
}
else {
$return_code = FALSE;
}
$execution_time = Timer::stop('imagemagick:runOsShell')['time'];
if ($this->configFactory
->get('imagemagick.settings')
->get('debug')) {
$this
->debugMessage('@suite command: <pre>@raw</pre> executed in @execution_timems', [
'@suite' => $this
->getPackageLabel($id),
'@raw' => print_r($command_line, TRUE),
'@execution_time' => $execution_time,
]);
if ($output !== '') {
$this
->debugMessage('@suite output: <pre>@raw</pre>', [
'@suite' => $this
->getPackageLabel($id),
'@raw' => print_r($output, TRUE),
]);
}
if ($error !== '') {
$this
->debugMessage('@suite error @return_code: <pre>@raw</pre>', [
'@suite' => $this
->getPackageLabel($id),
'@return_code' => $return_code,
'@raw' => print_r($error, TRUE),
]);
}
}
return $return_code;
}
public function debugMessage($message, array $context) {
$this->logger
->debug($message, $context);
if (\Drupal::currentUser()
->hasPermission('administer site configuration')) {
if (isset($context['@raw'])) {
$raw = explode("\n", $context['@raw']);
if (count($raw) > 10) {
$tmp = [];
for ($i = 0; $i < 9; $i++) {
$tmp[] = $raw[$i];
}
$tmp[] = (string) $this
->t('[Further text stripped. The watchdog log has the full text.]');
$context['@raw'] = implode("\n", $tmp);
}
}
drupal_set_message($this
->t($message, $context), 'status', TRUE);
}
}
public function getExecutable($binary, $path = NULL) {
if (!isset($path)) {
$path = $this->configFactory
->get('imagemagick.settings')
->get('path_to_binaries');
}
$executable = $binary;
if ($this->isWindows) {
$executable .= '.exe';
}
return $path . $executable;
}
public function getRequirements() {
$reported_info = [];
if (stripos(ini_get('disable_functions'), 'proc_open') !== FALSE) {
$severity = REQUIREMENT_ERROR;
$reported_info[] = $this
->t("The <a href=':proc_open_url'>proc_open()</a> PHP function is disabled. It must be enabled for the toolkit to work. Edit the <a href=':disable_functions_url'>disable_functions</a> entry in your php.ini file, or consult your hosting provider.", [
':proc_open_url' => 'http://php.net/manual/en/function.proc-open.php',
':disable_functions_url' => 'http://php.net/manual/en/ini.core.php#ini.disable-functions',
]);
}
else {
$status = $this
->checkPath($this->configFactory
->get('imagemagick.settings')
->get('path_to_binaries'));
if (!empty($status['errors'])) {
$severity = REQUIREMENT_ERROR;
foreach ($status['errors'] as $error) {
$reported_info[] = $error;
}
$reported_info[] = $this
->t('Go to the <a href=":url">Image toolkit</a> page to configure the toolkit.', [
':url' => Url::fromRoute('system.image_toolkit_settings')
->toString(),
]);
}
else {
$severity = REQUIREMENT_INFO;
$version_info = explode("\n", preg_replace('/\\r/', '', Html::escape($status['output'])));
$more_info_available = FALSE;
foreach ($version_info as $key => $item) {
if (stripos($item, 'feature') !== FALSE || $key > 4) {
$more_info_available = TRUE;
break;
}
$reported_info[] = $item;
}
if ($more_info_available) {
$reported_info[] = $this
->t('To display more information, go to the <a href=":url">Image toolkit</a> page, and expand the \'Version information\' section.', [
':url' => Url::fromRoute('system.image_toolkit_settings')
->toString(),
]);
}
$reported_info[] = '';
$reported_info[] = $this
->t("Enabled image file extensions: %extensions", [
'%extensions' => Unicode::strtolower(implode(', ', static::getSupportedExtensions())),
]);
}
}
return [
'imagemagick' => [
'title' => $this
->t('ImageMagick'),
'description' => [
'#markup' => implode('<br />', $reported_info),
],
'severity' => $severity,
],
];
}
public static function isAvailable() {
return TRUE;
}
public static function getSupportedExtensions() {
return \Drupal::service('imagemagick.format_mapper')
->getEnabledExtensions();
}
}