Skip to content

Commit

Permalink
Add parser for config files
Browse files Browse the repository at this point in the history
  • Loading branch information
mlocati committed Oct 3, 2016
1 parent 5a33047 commit d392fa6
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 0 deletions.
96 changes: 96 additions & 0 deletions src/Parser/ConfigFiles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace C5TL\Parser;

/**
* Extract translatable strings from core configuration PHP files.
*/
class ConfigFiles extends \C5TL\Parser
{
/**
* {@inheritdoc}
*
* @see \C5TL\Parser::getParserName()
*/
public function getParserName()
{
return function_exists('t') ? t('Core PHP Configurations Parser') : 'Core PHP Configurations Parser';
}

/**
* {@inheritdoc}
*
* @see \C5TL\Parser::canParseDirectory()
*/
public function canParseDirectory()
{
return true;
}

/**
* {@inheritdoc}
*
* @see \C5TL\Parser::canParseConcreteVersion()
*/
public function canParseConcreteVersion($version)
{
return version_compare($version, '5.7') >= 0;
}

/**
* {@inheritdoc}
*
* @see \C5TL\Parser::parseDirectoryDo()
*/
protected function parseDirectoryDo(\Gettext\Translations $translations, $rootDirectory, $relativePath, $subParsersFilter, $exclude3rdParty)
{
$directoryAlternatives = array(
array('', 'application', 'concrete'),
array('config/generated_overrides', 'config'),
);
switch ($relativePath) {
case '':
$directoryAlternatives = array('application/config/generated_overrides', 'application/config', 'concrete/config');
break;
case 'application':
$directoryAlternatives = array('config/generated_overrides', 'config');
break;
case 'concrete':
$directoryAlternatives = array('config');
break;
default:
return;
}
$prefix = ($relativePath === '') ? '' : "$relativePath/";
$this->parseFileTypes($translations, $rootDirectory, $prefix, $directoryAlternatives);
}

/**
* Parse the file type names.
*
* @param \Gettext\Translations $translations
* @param string $rootDirectory
* @param string $prefix
* @param string[] $directoryAlternatives
*/
private function parseFileTypes(\Gettext\Translations $translations, $rootDirectory, $prefix, $directoryAlternatives)
{
foreach ($directoryAlternatives as $subDir) {
$rel = ($subDir === '') ? 'app.php' : "$subDir/app.php";
$fileAbs = $rootDirectory.'/'.$rel;
if (!is_file($fileAbs)) {
continue;
}
$fileRel = $prefix.$rel;
$configFile = new \C5TL\Util\ConfigFile($fileAbs);
$config = $configFile->getArray();
if (isset($config['file_types']) && is_array($config['file_types'])) {
$fileTypes = $config['file_types'];
foreach (array_keys($fileTypes) as $fileType) {
$translation = $translations->insert('', $fileType);
$translation->addReference($fileRel);
}
}
}
}
}
156 changes: 156 additions & 0 deletions src/Util/ConfigFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

namespace C5TL\Util;

use C5TL\Options;
use Exception;

class ConfigFile
{
/**
* The file contents.
*
* @var string
*/
private $contents;

/**
* The position inside the file where we can add custom content.
*
* @var int
*/
private $customizersPosition;

/**
* Parsed array.
*
* @var array
*/
protected $array;

/**
* Last error catched by the evaluate method.
*
* @var array|null
*/
private $lastEvaluateError;

/**
* Initializes the instance.
*
* @param string $filename
*
* @throws Exception
*/
public function __construct($filename)
{
$this->readFile($filename);
$this->setCustomizersPosition();
if (!function_exists('t')) {
$this->addCustomizer('function t($arg) { return $arg; }');
}
$this->evaluate();
}

/**
* Read the contents of the file.
*
* @param string $filename
*
* @throws Exception
*
* @return string
*/
private function readFile($filename)
{
if (!is_file($filename)) {
throw new Exception('Failed to find file '.$filename);
}
if (!is_readable($filename)) {
throw new Exception('File is not readable: '.$filename);
}
$contents = @file_get_contents($filename);
if ($contents === false) {
throw new Exception('Failed to read file '.$filename);
}

$this->contents = str_replace(array("\r\n", "\r"), "\n", $contents);
}

/**
* Set the position inside the read content where we can add custom code.
*/
private function setCustomizersPosition()
{
if (!preg_match('/^\s*return[\n\s]+(?:\[|array[\s\n]*\()/ims', $this->contents, $m)) {
throw new Exception('Failed to determine the start of the return array');
}
$this->customizersPosition = strpos($this->contents, $m[0]);
}

private function addCustomizer($code)
{
$this->contents = substr($this->contents, 0, $this->customizersPosition)."\n".$code."\n".substr($this->contents, $this->customizersPosition);
}

public function evaluateAutoloader($className)
{
class_alias('C5TL\Util\ConfigFileFakeClass', $className);

return true;
}

public function evaluateHandleError()
{
$this->lastEvaluateError = func_get_args();
}

private function evaluate()
{
$tempDir = Options::getTemporaryDirectory();
for ($i = 0; ; ++$i) {
$filename = rtrim($tempDir, '/'.DIRECTORY_SEPARATOR).'/c5tltmp'.$i.'.php';
if (!file_exists($filename)) {
break;
}
}
if (@file_put_contents($filename, $this->contents) === false) {
throw new Exception('Failed to write a temporary file');
}
$errorReporting = @error_reporting();
error_reporting($errorReporting & ~E_NOTICE);
$exception = null;
$autoloader = array($this, 'evaluateAutoloader');
spl_autoload_register($autoloader, true, false);
$prevErrorHandler = set_error_handler(array($this, evaluateHandleError));
$this->lastEvaluateError = null;
try {
$code = include $filename;
} catch (\Exception $x) {
$exception = $x;
} catch (\Throwable $x) {
$exception = new \Exception($x->getMessage());
}
@set_error_handler($prevErrorHandler);
@unlink($filename);
@spl_autoload_unregister($autoloader);
@error_reporting($errorReporting);
if ($exception !== null) {
throw $exception;
}
if (!is_array($code) && $this->lastEvaluateError !== null) {
throw new Exception('Failed to read configuration file: '.$this->lastEvaluateError[1]);
}
$this->array = is_array($code) ? $code : array();
}

/**
* Return the read array.
*
* @return array
*/
public function getArray()
{
return $this->array;
}
}
19 changes: 19 additions & 0 deletions src/Util/ConfigFileFakeClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace C5TL\Util;

class ConfigFileFakeClass
{
// @see \Concrete\Core\File\Type\Type
const T_IMAGE = 1;
const T_VIDEO = 2;
const T_TEXT = 3;
const T_AUDIO = 4;
const T_DOCUMENT = 5;
const T_APPLICATION = 6;
const T_UNKNOWN = 99;

// @see \Concrete\Core\Asset\Asset
const ASSET_POSITION_HEADER = 'H';
const ASSET_POSITION_FOOTER = 'F';
}

0 comments on commit d392fa6

Please sign in to comment.