tikiwiki/packages/tiki-pkg-mediaalchemyst/alchemy/phpexiftool/lib/PHPExiftool/Tool/Command/ClassesBuilder.php

589 lines
17 KiB
PHP
Raw Normal View History

2023-11-20 21:52:04 +01:00
<?php
/**
* This file is part of the PHPExiftool package.
*
* (c) Alchemy <support@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace PHPExiftool\Tool\Command;
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use PHPExiftool\ClassUtils\Builder;
use PHPExiftool\ClassUtils\TagProviderBuilder;
use PHPExiftool\Exiftool;
use PHPExiftool\InformationDumper;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DomCrawler\Crawler;
/**
*
* @author Romain Neutron - imprec@gmail.com
* @license http://opensource.org/licenses/MIT MIT
*/
class ClassesBuilder extends Command
{
/**
* Output interface for Command
*
* @var ConsoleOutput
*/
protected $output;
/**
*
* @var array
*/
protected $classes = array();
/**
*
* @var array
*/
protected $types = array();
/**
* @see Console\Command\Command
*/
protected function configure()
{
$this
->setName('classes-builder')
->setDescription('Build Tags classes from exiftool documentation.')
->addOption('with-mwg', '', null, 'Include MWG tags')
->addOption('write', 'w', null, 'Write classes on disk')
->addOption('force', 'f', null, 'Force classes write whenever files already exists');
return $this;
}
/**
* @see Console\Command\Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$start = microtime(true);
$this->output = $output;
$this->output->write('Extracting datas... ');
$logger = new Logger('Builder');
$logger->pushHandler(new NullHandler());
if ($input->getOption('verbose')) {
$logger->pushHandler(new StreamHandler('php://stdout'));
}
$dumper = new InformationDumper(new Exiftool($logger));
$options = array();
if($input->getOption('with-mwg')) {
$options[] = InformationDumper::LISTOPTION_MWG;
}
$dump = $dumper->listDatas(InformationDumper::LISTTYPE_SUPPORTED_XML, $options);
$this->output->writeln('Done !');
$this->output->writeln('Generating classes... ');
$this->extractDump($dump, $output);
if ( ! $input->getOption('write')) {
$this->output->writeln(
'These classes were not written. Use --write to write on disk'
);
} else {
$this->output->writeln('Erasing previous files... ');
system('rm -R ' . __DIR__ . '/../../Driver/Tag/*');
system('rm -R ' . __DIR__ . '/../../Driver/Type/*');
$this->output->writeln('Writing files... ');
$this->writeClasses($input->getOption('force'));
}
$this->output->writeln(
sprintf(
'%d classes generated in %d seconds (%d Mb)'
, count($this->classes), (microtime(true) - $start), memory_get_peak_usage() >> 20
)
);
}
/**
*
* @return ClassesBuilder
*/
protected function writeClasses($force = false)
{
$n = 0;
$classesBuffer = new TagProviderBuilder('', 'TagProvider', array(), '\\Pimple');
$buffer = array();
foreach ($this->classes as $class) {
try {
$class->write($force);
if (strpos($class->getNamespace(), 'PHPExiftool\\Driver\\Tag') === 0) {
if ( ! isset($buffer[$class->getProperty('GroupName')])) {
$buffer[$class->getProperty('GroupName')] = array();
}
$buffer[$class->getProperty('GroupName')][$class->getProperty('Name')] = $class->getNamespace() . '\\' . $class->getClassname();
}
$this->output->write(sprintf("\rwriting class #%5d", $n ++ ));
} catch (\Exception $e) {
$this->output->writeln(
sprintf("\n<error>Error while writing class %s</error>", $class->getPathfile())
);
}
}
$classesBuffer->setClasses($buffer);
$classesBuffer->write(true);
$this->output->writeln('');
return $this;
}
protected function generateTypes()
{
foreach ($this->types as $type => $data) {
if ($type == '?')
$type = 'unknown';
$classname = self::generateClassname($type);
$properties = array(
'ExiftoolName' => $data,
'PHPMap' => $this->getTypeMap($type),
);
$classpath = sprintf('%s', $classname);
$this->classes[$classpath] = new Builder('Type', $classname, $properties, 'AbstractType', array('\\PHPExiftool\\Driver\\AbstractType'));
}
return;
}
protected function getTypeMap($type)
{
/**
* Some of these types are described here:
* http://trac.greenstone.org/browser/main/trunk/greenstone2/perllib/cpan/Image/ExifTool/README
* http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-9.13/lib/Image/ExifTool/PICT.pm
*/
switch ($type) {
# Formats defined in the wiki
case 'int8s':
case 'int8u':
case 'int16s':
case 'int16u':
case 'int16uRev':
case 'int32s':
case 'int32u':
case 'int32uRev':
case 'int64s':
case 'int64u':
case 'rational32s':
case 'rational32u':
case 'rational64s':
case 'rational64u':
case 'fixed16s':
case 'fixed32s':
case 'fixed32u':
case 'var_int16u':
# Apple data structures in PICT images
case 'Int8uText':
case 'Int8u2Text':
case 'Int16Data':
case 'Int32uData':
# Source unknown ...
case 'var_int8u':
case 'rational':
case 'integer':
case 'real':
case 'digits':
case 'signed':
case 'unsigned':
return 'int';
break;
# Formats defined in the wiki
case 'float':
case 'double':
case 'extended':
return 'float';
break;
# Formats defined in the wiki
case 'undef':
case 'binary':
# Source unknown ...
case 'var_undef':
case '?':
case 'null':
case 'unknown':
case 'Unknown':
return 'binary';
break;
# Formats defined in the wiki
case 'string':
case 'pstring':
case 'var_string':
case 'var_pstr32':
# Apple data structures in PICT images
case 'Arc':
case 'BitsRect#': # version-depended
case 'BitsRgn#': # version-depended
case 'CompressedQuickTime':
case 'DirectBitsRect':
case 'DirectBitsRgn':
case 'FontName':
case 'PixPat':
case 'Point':
case 'PointText':
case 'Polygon':
case 'Rect':
case 'RGBColor':
case 'Rgn':
case 'ShortLine':
# Source unknown ...
case 'lang-alt':
case 'resize':
case 'utf8':
return 'string';
break;
# Source unknown ...
case 'date':
return 'date';
break;
# Source unknown ...
case 'boolean':
return 'boolean';
break;
default:
$this->output->writeln(sprintf("No type found for %s", $type));
break;
}
return;
}
protected function createTagClass($namespace, $classname, array $properties)
{
if ($classname == 'Reserved') {
return;
}
$namespace = self::generateNamespace('Tag\\' . $namespace);
$classpath = sprintf('%s\\%s', $namespace, $classname);
if (isset($this->classes[$classpath])) {
foreach ($properties as $property => $value) {
if ($this->classes[$classpath]->getProperty($property) != $value) {
if (in_array($property, array('Writable', 'flag_Binary', 'flag_List'))) {
$this->classes[$classpath]->setProperty($property, 'false');
} elseif ($property === 'Values') {
$new_value = array();
if ( ! is_array($this->classes[$classpath]->getProperty($property))) {
if (is_array($value)) {
$new_value = $value;
}
} else {
if (is_array($value) && $this->classes[$classpath]->getProperty($property) != $value) {
$new_value = array_merge($this->classes[$classpath]->getProperty($property), $value);
} else {
$new_value = $this->classes[$classpath]->getProperty($property);
}
}
$this->classes[$classpath]->setProperty($property, $new_value);
} else {
$this->classes[$classpath]->setProperty($property, 'mixed');
}
}
}
} else {
$this->classes[$classpath] = new Builder($namespace, $classname, $properties, 'AbstractTag', array('JMS\\Serializer\\Annotation\\ExclusionPolicy', '\\PHPExiftool\\Driver\\AbstractTag'), array('@ExclusionPolicy("all")'));
}
return;
}
protected function extractDump($dump, OutputInterface $output)
{
$crawler = new Crawler();
$crawler->addContent($dump);
$tag_count = null;
if ($output->getVerbosity() >= OutputInterface::VERBOSITY_QUIET) {
$output->write('Compute tag count...');
$tag_count = count($crawler->filter('table>tag'));
$output->writeln(sprintf('%d', $tag_count));
}
if (!$this->getHelperSet()->has('progress')) {
$progress = new ProgressBar($output);
$progress->start($tag_count);
} else {
$progress = $this->getHelper('progress');
$progress->start($output, $tag_count);
}
foreach ($crawler->filter('table') as $table) {
$table_crawler = new Crawler();
$table_crawler->addNode($table);
$tag_group_name = $table_crawler->attr('g1');
$tag_full_name = $table_crawler->attr('name');
$tag_g0 = $table_crawler->attr('g0');
$tag_g2 = $table_crawler->attr('g2');
$tags = $table_crawler->filter('tag');
foreach ($tags as $tag) {
$tag_crawler = new Crawler();
$tag_crawler->addNode($tag);
$extra = array();
if ($tag_crawler->attr('g0')) {
$extra['local_g0'] = $tag_crawler->attr('g0');
}
if ($tag_crawler->attr('g1') && ! in_array($tag_crawler->attr('g1'), array('MakerNotes', 'Chapter#'))) {
$g_name = $tag_crawler->attr('g1');
$extra['local_g1'] = $tag_crawler->attr('g1');
} else {
$g_name = $tag_group_name;
}
if ($tag_crawler->attr('g2')) {
$extra['local_g2'] = $tag_crawler->attr('g2');
}
$flags = explode(',', $tag_crawler->attr('flags'));
if (in_array('Avoid', $flags)) {
$extra['flag_Avoid'] = 'true';
}
if (in_array('Binary', $flags)) {
$extra['flag_Binary'] = 'true';
}
if (in_array('Permanent', $flags)) {
$extra['flag_Permanent'] = 'true';
}
if (in_array('Protected', $flags)) {
$extra['flag_Protected'] = 'true';
}
if (in_array('Unsafe', $flags)) {
$extra['flag_Unsafe'] = 'true';
}
if (in_array('List', $flags)) {
$extra['flag_List'] = 'true';
}
if (in_array('Mandatory', $flags)) {
$extra['flag_Mandatory'] = 'true';
}
if (in_array('Bag', $flags)) {
$extra['flag_Bag'] = 'true';
}
if (in_array('Seq', $flags)) {
$extra['flag_Seq'] = 'true';
}
if (in_array('Alt', $flags)) {
$extra['flag_Alt'] = 'true';
}
$subspace = str_replace('::', '\\', $g_name);
$tag_name = $tag_crawler->attr('name');
$classname = self::generateClassname($tag_name);
$tag_id = $tag_crawler->attr('id');
$properties = array_merge(array(
'Id' => $tag_id,
'Name' => $tag_name,
'FullName' => $tag_full_name,
'GroupName' => $g_name,
'g0' => $tag_g0,
'g1' => $tag_group_name,
'g2' => $tag_g2,
'Type' => $tag_crawler->attr('type'),
'Writable' => $tag_crawler->attr('writable'),
'Description' => $tag_crawler->filter('desc[lang="en"]')->first()->text(),
), $extra);
if ($tag_crawler->attr('count')) {
$properties['MaxLength'] = $tag_crawler->attr('count');
}
$this->types[$tag_crawler->attr('type')] = $tag_crawler->attr('type');
if ($tag_crawler->attr('index')) {
$properties['Index'] = $tag_crawler->attr('index');
}
if (count($tag_crawler->filter('values')) > 0) {
$values = array();
$values_tag = $tag_crawler->filter('values')->first();
$Keys = $values_tag->filter('key');
foreach ($Keys as $Key) {
$KeyCrawler = new Crawler();
$KeyCrawler->addNode($Key);
$Id = $KeyCrawler->attr('id');
$Label = $KeyCrawler->filter('val[lang="en"]')->first()->text();
$values[$Id] = array('Id' => $Id, 'Label' => $Label);
}
$properties['Values'] = $values;
}
$this->createTagClass($subspace, $classname, $properties);
$progress->advance();
}
}
$progress->finish();
$this->generateTypes();
}
protected static $reservedNames = array(
'abstract',
'and',
'array',
'as',
'bool',
'break',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'do',
'else',
'elseif',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'extends',
'false',
'final',
'float',
'for',
'foreach',
'function',
'function',
'global',
'goto',
'if',
'implements',
'instanceof',
'int',
'interface',
'mixed',
'namespace',
'new',
'null',
'numeric',
'object',
'old_function',
'or',
'private',
'protected',
'public',
'resource',
'static',
'string',
'switch',
'throw',
'true',
'try',
'use',
'var',
'while',
'xor',
'yield',
);
/**
*
* @param type $name
* @return type
*/
public static function generateClassname($name)
{
$values = preg_split('/\\ |-|_|\\#/', ltrim($name, '0123456789'));
foreach ($values as $key => $value) {
$values[$key] = ucfirst($value);
}
$retval = implode('', $values);
if (in_array(strtolower($retval), static::$reservedNames)) {
$retval = $retval . '0';
}
return $retval;
}
public static function generateNamespace($namespace)
{
$values = explode('\\', $namespace);
foreach ($values as $key => $value) {
$values[$key] = ucfirst(self::generateClassname($value));
}
return implode('\\', $values);
}
}