* @git 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; } }