jcb-compiler/src/ca111518-e47c-4049-b1b2-cb0.../code.php

401 lines
12 KiB
PHP

<?php
/**
* @package Joomla.Component.Builder
*
* @created 4th September, 2022
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Utilities\ArrayHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Utilities\GetHelper;
use VDM\Joomla\Componentbuilder\Compiler\Factory as Compiler;
use VDM\Joomla\Componentbuilder\Compiler\Config;
use VDM\Joomla\Componentbuilder\Compiler\Placeholder;
use VDM\Joomla\Componentbuilder\Compiler\Language;
use VDM\Joomla\Componentbuilder\Compiler\Language\Extractor;
use VDM\Joomla\Componentbuilder\Compiler\Power\Extractor as Power;
/**
* Compiler Placeholder Reverse
*
* @since 3.2.0
*/
class Reverse
{
/**
* Compiler Config
*
* @var Config
* @since 3.2.0
**/
protected Config $config;
/**
* Compiler Placeholder
*
* @var Placeholder
* @since 3.2.0
**/
protected Placeholder $placeholder;
/**
* Compiler Language
*
* @var Language
* @since 3.2.0
**/
protected Language $language;
/**
* Compiler Language Extractor
*
* @var Extractor
* @since 3.2.0
**/
protected Extractor $extractor;
/**
* Super Power Extractor
*
* @var Power
* @since 3.2.0
**/
protected Power $power;
/**
* Constructor.
*
* @param Config|null $config The compiler config object.
* @param Placeholder|null $placeholder The compiler placeholder object.
* @param Language|null $language The compiler language object.
* @param Extractor|null $extractor The compiler language extractor object.
* @param Power|null $power The compiler power extractor object.
*
* @since 3.2.0
*/
public function __construct(
?Config $config = null, ?Placeholder $placeholder = null,
?Language $language = null, ?Extractor $extractor = null,
?Power $power = null)
{
$this->config = $config ?: Compiler::_('Config');
$this->placeholder = $placeholder ?: Compiler::_('Placeholder');
$this->language = $language ?: Compiler::_('Language');
$this->extractor = $extractor ?: Compiler::_('Language.Extractor');
$this->power = $power ?: Compiler::_('Power.Extractor');
}
/**
* Reverse Engineer the dynamic placeholders (TODO hmmmm this is not ideal)
*
* @param string $string The string to reverse
* @param array $placeholders The values to search for
* @param string $target The target path type
* @param int|null $id The custom code id
* @param string $field The field name
* @param string $table The table name
* @param array|null $useStatements The file use statements (needed for super powers)
*
* @return string
* @since 3.2.0
*/
public function engine(string $string, array &$placeholders,
string $target, ?int $id = null, string $field = 'code',
string $table = 'custom_code', ?array $useStatements = null): string
{
// get local code if set
if ($id > 0 && $code = base64_decode(
(string) GetHelper::var($table, $id, 'id', $field)
))
{
$string = $this->setReverse(
$string, $code, $target, $useStatements
);
}
return $this->placeholder->update($string, $placeholders, 2);
}
/**
* Reverse engineer the dynamic language, and super powers
*
* @param string $updateString The string to update
* @param string $string The string to use language update
* @param string $target The target path type
* @param array|null $useStatements The file use statements (needed for super powers)
*
* @return string
* @since 3.2.0
*/
protected function setReverse(string $updateString, string $string,
string $target, ?array $useStatements): string
{
// we have to reverse engineer to super powers
$updateString = $this->reverseSuperPowers($updateString, $string, $useStatements);
// reverse engineer the language strings
$updateString = $this->reverseLanguage($updateString, $string, $target);
// reverse engineer the custom code (if possible)
// $updateString = $this->reverseCustomCode($updateString, $string); // TODO - we would like to also reverse basic customcode
return $updateString;
}
/**
* Set the super powers keys for the reveres process
*
* @param string $updateString The string to update
* @param string $string The string to use for super power update
* @param array|null $useStatements The file use statements (needed for super powers)
*
* @return string
* @since 3.2.0
*/
protected function reverseSuperPowers(string $updateString, string $string,
?array $useStatements): string
{
// only if we have use statements can we reverse engineer this
if ($useStatements !== null && ($powers = $this->power->reverse($string)) !== null &&
($reverse = $this->getReversePower($powers, $useStatements)) !== null)
{
return $this->placeholder->update($updateString, $reverse);
}
return $updateString;
}
/**
* Set the super powers keys for the reveres process
*
* @param array $powers The powers found in the database text
* @param array $useStatements The file use statements
*
* @return array|null
* @since 3.2.0
*/
protected function getReversePower(array $powers, array $useStatements): ?array
{
$matching_statements = [];
foreach ($useStatements as $use_statement)
{
$namespace = substr($use_statement, 4, -1); // remove 'use ' and ';'
$class_name = '';
// Check for 'as' alias
if (strpos($namespace, ' as ') !== false)
{
list($namespace, $class_name) = explode(' as ', $namespace);
}
// If there is no 'as' alias, get the class name from the last '\'
if (empty($class_name))
{
$last_slash = strrpos($namespace, '\\');
if ($last_slash !== false)
{
$class_name = substr($namespace, $last_slash + 1);
}
}
// Check if the namespace is in the powers array
if (in_array($namespace, $powers))
{
$guid = array_search($namespace, $powers);
$matching_statements[$class_name] =
'Super_'.'_'.'_' . str_replace('-', '_', $guid) . '_'.'_'.'_Power';
}
}
if ($matching_statements !== [])
{
return $matching_statements;
}
return null;
}
/**
* Set the language strings for the reveres process
*
* @param string $updateString The string to update
* @param string $string The string to use language update
* @param string $target The target path type
*
* @return string
* @since 3.2.0
*/
protected function reverseLanguage(string $updateString, string $string, string $target): string
{
// get targets to search for
$lang_string_targets = array_filter(
$this->config->lang_string_targets,
fn($get): bool => strpos($string, (string) $get) !== false
);
// check if we should continue
if (ArrayHelper::check($lang_string_targets))
{
// start lang holder
$lang_holders = [];
// set the lang for both since we don't know what area is being targeted
$_tmp = $this->config->lang_target;
// set the lang based on target
if (strpos($target, 'module') !== false)
{
// backup lang prefix
$_tmp_lang_prefix = $this->config->lang_prefix;
// set the new lang prefix
$lang_prefix = strtoupper(
str_replace('module', 'mod', $target)
);
$this->config->set('lang_prefix', $lang_prefix);
// now set the lang
if (isset($this->extractor->langKeys[$this->config->lang_prefix]))
{
$this->config->lang_target = $this->extractor->langKeys[$this->config->lang_prefix];
}
else
{
$this->config->lang_target = 'module';
}
}
elseif (strpos($target, 'plugin') !== false)
{
// backup lang prefix
$_tmp_lang_prefix = $this->config->lang_prefix;
// set the new lang prefix
$lang_prefix = strtoupper(
str_replace('plugin', 'plg', $target)
);
$this->config->set('lang_prefix', $lang_prefix);
// now set the lang
if (isset($this->extractor->langKeys[$this->config->lang_prefix]))
{
$this->config->lang_target = $this->extractor->langKeys[$this->config->lang_prefix];
}
else
{
$this->config->lang_target = 'plugin';
}
}
else
{
$this->config->lang_target = 'both';
}
// set language data
foreach ($lang_string_targets as $lang_string_target)
{
$lang_check[] = GetHelper::allBetween(
$string, $lang_string_target . "'", "'"
);
$lang_check[] = GetHelper::allBetween(
$string, $lang_string_target . '"', '"'
);
}
// merge arrays
$lang_array = ArrayHelper::merge($lang_check);
// continue only if strings were found
if (ArrayHelper::check(
$lang_array
)) //<-- not really needed hmmm
{
foreach ($lang_array as $lang)
{
$_key_lang = StringHelper::safe($lang, 'U');
// this is there to insure we dont break already added Language strings
if ($_key_lang === $lang)
{
continue;
}
// build lang key
$key_lang = $this->config->lang_prefix . '_' . $_key_lang;
// set lang content string
$this->language->set($this->config->lang_target, $key_lang, $lang);
// reverse the placeholders
foreach ($lang_string_targets as $lang_string_target)
{
$lang_holders[$lang_string_target . "'" . $key_lang . "'"]
= $lang_string_target . "'" . $lang . "'";
$lang_holders[$lang_string_target . '"' . $key_lang . '"']
= $lang_string_target . '"' . $lang . '"';
}
}
// return the found placeholders
$updateString = $this->placeholder->update(
$updateString, $lang_holders
);
}
// reset the lang
$this->config->lang_target = $_tmp;
// also rest the lang prefix if set
if (isset($_tmp_lang_prefix))
{
$lang_prefix = $_tmp_lang_prefix;
$this->config->set('lang_prefix', $_tmp_lang_prefix);
}
}
return $updateString;
}
/**
* Set the custom code placeholder for the reveres process
*
* @param string $updateString The string to update
* @param string $string The string to use for super power update
*
* @return string
* @since 3.2.0
*/
protected function reverseCustomCode(string $updateString, string $string): string
{
// check if content has custom code place holder
if (strpos($string, '[CUSTO' . 'MCODE=') !== false)
{
$found = GetHelper::allBetween(
$string, '[CUSTO' . 'MCODE=', ']'
);
$bucket = [];
if (ArrayHelper::check($found))
{
foreach ($found as $key)
{
// we only update those without args
if (is_numeric($key) && $get_func_name = GetHelper::var(
'custom_code', $key, 'id', 'function_name'
))
{
$bucket[$get_func_name] = (int) $key;
}
elseif (StringHelper::check($key)
&& strpos((string) $key, '+') === false)
{
$get_func_name = trim((string) $key);
if (isset($bucket[$get_func_name]) || !$found_local = GetHelper::var(
'custom_code', $get_func_name, 'function_name',
'id'
))
{
continue;
}
$bucket[$get_func_name] = (int) $found_local;
}
}
// TODO - we need to now get the customcode
// search and replace the customcode with the placeholder
}
}
return $updateString;
}
}