Release of v3.2.1-rc1

Improved the Schema Table update engine (more). Fix autoloader timing, and loading. Implement the Joomla Powers in JCB code, to move away from JClasses.
This commit is contained in:
2024-04-27 15:45:07 +02:00
parent f99eae901a
commit 43cdc68e34
69 changed files with 316 additions and 319 deletions

View File

@ -486,56 +486,52 @@ abstract class Schema implements SchemaInterface
*
* This function checks if there's a significant difference between the current
* data type and the expected data type that would require updating the database schema.
* It ignores size and other modifiers for certain data types where MySQL considers
* these attributes irrelevant for storage.
* It ignores display width for numeric types where MySQL considers these attributes
* irrelevant for storage but considers size and other modifiers for types like VARCHAR.
*
* @param string $currentType The current data type from the database schema.
* @param string $expectedType The expected data type to validate against.
* @param string $currentType The current data type from the database schema.
* @param string $expectedType The expected data type to validate against.
*
* @return bool Returns true if the data type change is significant, otherwise false.
* @return bool Returns true if the data type change is significant, otherwise false.
* @since 3.2.1
*/
function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool
protected function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool
{
// we only do this for Joomla 4+
if ($this->currentVersion != 3)
// Normalize both input types to lowercase for case-insensitive comparison
$currentType = strtolower($currentType);
$expectedType = strtolower($expectedType);
// Regex to extract the base data type and numeric parameters with named groups
$typePattern = '/^(?<datatype>\w+)(\((?<params>\d+(,\d+)?)\))?/';
// Match types and parameters
preg_match($typePattern, $currentType, $currentMatches);
preg_match($typePattern, $expectedType, $expectedMatches);
// Compare base types
if ($currentMatches['datatype'] !== $expectedMatches['datatype'])
{
// Normalize both input types to lowercase for case-insensitive comparison
$currentType = strtolower($currentType);
$expectedType = strtolower($expectedType);
// Define types where size or other modifiers are irrelevant
$sizeIrrelevantTypes = [
'int', 'tinyint', 'smallint', 'mediumint', 'bigint', // Standard integer types
'int unsigned', 'tinyint unsigned', 'smallint unsigned', 'mediumint unsigned', 'bigint unsigned', // Unsigned integer types
];
// Check if the type involves size-irrelevant types
foreach ($sizeIrrelevantTypes as $type)
{
if (strpos($expectedType, $type) !== false)
{
// Remove any numeric sizes and modifiers for comparison
$pattern = '/\(\d+\)|unsigned|\s*/';
$cleanCurrentType = preg_replace($pattern, '', $currentType);
$cleanExpectedType = preg_replace($pattern, '', $expectedType);
// Compare the cleaned types
if ($cleanCurrentType === $cleanExpectedType)
{
return false; // No significant change
}
}
}
return true; // Base types differ
}
// Perform a standard case-insensitive comparison for other types
if (strcasecmp($currentType, $expectedType) == 0)
// Define types where size and other modifiers are irrelevant
$sizeIrrelevantTypes = [
'int', 'tinyint', 'smallint', 'mediumint', 'bigint',
'float', 'double', 'decimal', 'numeric' // Numeric types where display width is irrelevant
];
// If the type is not in the size irrelevant list, compare full definitions
if (!in_array($currentMatches['datatype'], $sizeIrrelevantTypes))
{
return false; // No significant change
return $currentType !== $expectedType; // Use full definition for types where size matters
}
return true; // Significant datatype change detected
// For size irrelevant types, only compare base type, ignoring size and unsigned
$currentBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $currentType);
$expectedBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $expectedType);
// Perform a final comparison for numeric types ignoring size
return $currentBaseType !== $expectedBaseType;
}
/**

View File

@ -293,6 +293,24 @@ class Config extends BaseConfig
return strlen((string) $this->component_code_name);
}
/**
* get component autoloader path
*
* @return string The component autoloader path
* @since 3.2.0
*/
protected function getComponentautoloaderpath(): string
{
if ($this->joomla_version == 3)
{
return 'helpers/powerloader.php';
}
else
{
return 'src/Helper/PowerloaderHelper.php';
}
}
/**
* get add namespace prefix
*
@ -636,9 +654,12 @@ class Config extends BaseConfig
return [
'jjt' => 'Joomla' . '.JText._(',
'js' => 'Text:' . ':script(',
't' => 'Text:' . ':_(', // namespace and J version will be found
't' => 'Text:' . ':_(', // namespace and J version will be found
'ts' => 'Text:' . ':sprintf(', // namespace and J version will be found
'jt' => 'JustTEXT:' . ':_('
'jt' => 'JustTEXT:' . ':_(',
'spjs' => 'Joomla__' . '_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:' . ':script(', // the joomla power version
'spt' => 'Joomla__' . '_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:' . ':_(', // the joomla power version
'spts' => 'Joomla__' . '_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:' . ':sprintf(' // the joomla power version
];
}

View File

@ -546,10 +546,14 @@ final class CustomFieldTypeFile
if ($this->config->get('joomla_version', 3) != 3)
{
$placeholders['JFactory::getUser()'] = 'Factory::getApplication()->getIdentity()';
$placeholders['\JFactory::getUser()'] = 'Factory::getApplication()->getIdentity()';
$placeholders['Factory::getUser()'] = 'Factory::getApplication()->getIdentity()';
$placeholders['JFactory::'] = 'Factory::';
$placeholders['\JFactory::'] = 'Factory::';
$placeholders['JHtml::'] = 'Html::';
$placeholders['\JHtml::'] = 'Html::';
$placeholders['JText::'] = 'Text::';
$placeholders['\JText::'] = 'Text::';
$placeholders['JComponentHelper::'] = 'ComponentHelper::';
$placeholders['\JComponentHelper::'] = 'ComponentHelper::';
}

View File

@ -405,7 +405,7 @@ class Compiler extends Infusion
{
CFactory::_('Power')->load($super_powers);
}
// set the autoloader for Powers (second time)
// set the autoloader for Powers
CFactory::_('Power.Autoloader')->set();
// get the bom file
$bom = FileHelper::getContent(CFactory::_('Config')->bom_path);

View File

@ -2149,8 +2149,8 @@ class Infusion extends Interpretation
CFactory::_('Compiler.Builder.Component.Fields')->varExport(null, 1)
);
// set the autoloader for Powers (first time)
CFactory::_('Power.Autoloader')->set();
// set the autoloader for Powers
CFactory::_('Power.Autoloader')->setFiles();
// tweak system to set stuff to the module domain
$_backup_target = CFactory::_('Config')->build_target;

View File

@ -147,7 +147,9 @@ class Extractor
}
}
// now get the JText: :script()
if (in_array('JText:' . ':script(', $lang_string_targets) || in_array('Text:' . ':script(', $lang_string_targets))
if (in_array('JText:' . ':script(', $lang_string_targets)
|| in_array('Text:' . ':script(', $lang_string_targets)
|| in_array('Joomla__' . '_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:' . ':script(', $lang_string_targets))
{
$sc_text[] = GetHelper::allBetween(
$content, "JText:" . ":script('", "'"
@ -161,6 +163,12 @@ class Extractor
$sc_text[] = GetHelper::allBetween(
$content, 'Text:' . ':script("', '"'
);
$sc_text[] = GetHelper::allBetween(
$content, "Joomla__" ."_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:" . ":script('", "'"
);
$sc_text[] = GetHelper::allBetween(
$content, 'Joomla__' . '_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:' . ':script("', '"'
);
// combine into one array
$sc_text = ArrayHelper::merge($sc_text);
// we need to add a check to insure these JavaScript lang matchup
@ -192,6 +200,7 @@ class Extractor
if ($lang_string_target === 'Joomla' . '.JText._('
|| $lang_string_target === 'JText:' . ':script('
|| $lang_string_target === 'Text:' . ':script('
|| $lang_string_target === 'Joomla__' . '_ba6326ef_cb79_4348_80f4_ab086082e3c5___Power:' . ':script('
|| $lang_string_target === 'JustTEXT:' . ':_(')
{
continue;

View File

@ -77,7 +77,27 @@ class Autoloader
// reset all autoloaders power placeholders
$this->content->set('ADMIN_POWER_HELPER', '');
$this->content->set('SITE_POWER_HELPER', '');
$this->content->set('PLUGIN_POWER_AUTOLOADER', '');
$this->content->set('CUSTOM_POWER_AUTOLOADER', '');
$this->content->set('SITE_PLUGIN_POWER_AUTOLOADER', '');
$this->content->set('SITE_CUSTOM_POWER_AUTOLOADER', '');
}
/**
* Set the autoloader into the active content array
*
* @return void
* @since 3.2.0
*/
public function setFiles()
{
// check if we are using a plugin
$this->content->set('PLUGIN_POWER_AUTOLOADER', PHP_EOL . PHP_EOL . $this->getAutoloaderFile(2));
$this->content->set('SITE_PLUGIN_POWER_AUTOLOADER', PHP_EOL . PHP_EOL . $this->getAutoloaderFile(2, 'JPATH_SITE'));
// to add to custom files
$this->content->add('CUSTOM_POWER_AUTOLOADER', $this->getAutoloaderFile(0));
$this->content->add('SITE_CUSTOM_POWER_AUTOLOADER', $this->getAutoloaderFile(0, 'JPATH_SITE'));
}
/**
@ -89,9 +109,9 @@ class Autoloader
public function set()
{
// make sure we only load this once
if (ArrayHelper::check($this->power->namespace) && !$this->content->isString('CUSTOM_POWER_AUTOLOADER'))
if (ArrayHelper::check($this->power->namespace) && !$this->content->isString('ADMIN_POWER_HELPER'))
{
/************************* IMPORTANT SORT NOTICE ***********************************************
/* ********************** IMPORTANT SORT NOTICE *****************************************
* make sure the name space values are sorted from the longest string to the shortest
* so that the search do not mistakenly match a shorter namespace before a longer one
* that has the same short namespace for example:
@ -102,110 +122,18 @@ class Autoloader
* ^^^^^^^^^^^^^^^^^^^^^^
* NameSpace\SubName\SubSubName\ClassName
* ^^^^^^^^^^^^^^^^^^^^^^
***********************************************************************************************/
** *********************************************************************************************/
uksort($this->power->namespace, fn($a, $b) => strlen((string) $b) - strlen((string) $a));
// check if we are using a plugin
if ($this->loadPluginAutoloader())
{
$this->content->set('PLUGIN_POWER_AUTOLOADER', $this->getPluginAutoloader());
}
// load to admin helper class
$this->content->add('ADMIN_POWER_HELPER', $this->getHelperAutoloader());
// load to the helper class
if ($this->loadHelperAutoloader())
{
// load to admin helper class
$this->content->add('ADMIN_POWER_HELPER', $this->getHelperAutoloader());
// load to site helper class if needed
if ($this->loadSiteAutoloader())
{
$this->content->add('SITE_POWER_HELPER', $this->getHelperAutoloader());
}
}
// to add to custom files
$this->content->add('CUSTOM_POWER_AUTOLOADER', $this->getHelperAutoloader());
// load to site helper class if needed
$this->content->add('SITE_POWER_HELPER', $this->getHelperAutoloader());
}
}
/**
* Should we load the plugin autoloader
*
* @return bool
* @since 3.2.0
*/
private function loadPluginAutoloader(): bool
{
return $this->content->exists('PLUGIN_POWER_AUTOLOADER');
}
/**
* Should we load the helper class autoloader
*
* @return bool
* @since 3.2.0
*/
private function loadHelperAutoloader(): bool
{
// for now we load it if the plugin is not loaded
// but we may want to add a switch that
// controls this behaviour.
// return !$this->loadPluginAutoloader();
// lets load it anyway (can't break anything)
// but we will still like a switch for this
return true;
}
/**
* Should we load the autoloader in site area
*
* @return bool
* @since 3.2.0
*/
private function loadSiteAutoloader(): bool
{
return (!$this->config->remove_site_folder || !$this->config->remove_site_edit_folder);
}
/**
* Get helper autoloader code
*
* @return string
* @since 3.2.0
*/
private function getPluginAutoloader(): string
{
// load the code
$code = [];
// if we should not load in the site are
if (($script = $this->getBLockSiteLoading()) !== null)
{
$code[] = $script;
}
// add the composer stuff here
if (($script = $this->getComposer(2)) !== null)
{
$code[] = $script;
}
// get the helper autoloader
if (($script = $this->getAutoloader(2)) !== null)
{
$code[] = $script;
}
// if we have any
if (!empty($code))
{
return PHP_EOL . PHP_EOL . implode(PHP_EOL . PHP_EOL, $code);
}
return '';
}
/**
* Get helper autoloader code
*
@ -245,30 +173,29 @@ class Autoloader
}
/**
* Get code that will block the plugin from loading
* the autoloader in the site area
* Get autoloader file
*
* @param int $tabSpace The dynamic tab spacer
* @param string $area The target area
*
* @return string|null
* @since 3.2.0
* @since 3.2.1
*/
private function getBLockSiteLoading(): ?string
private function getAutoloaderFile(int $tabSpace, string $area = 'JPATH_ADMINISTRATOR'): ?string
{
// if we should not load in the site are
if (!$this->loadSiteAutoloader())
{
// we add code to prevent this plugin from triggering on the site area
$not_site = [];
$not_site[] = Indent::_(2) . '//'
. Line::_(__Line__, __Class__) . ' do not run the autoloader in the site area';
$not_site[] = Indent::_(2) . 'if ($this->app->isClient(\'site\'))';
$not_site[] = Indent::_(2) . '{';
$not_site[] = Indent::_(3) . 'return;';
$not_site[] = Indent::_(2) . '}';
// we start building the autoloaded file loader
$autoload_file = [];
$autoload_file[] = Indent::_($tabSpace) . '//'
. Line::_(__Line__, __Class__) . " The power autoloader for this project ($area) area.";
$autoload_file[] = Indent::_($tabSpace) . "\$power_autoloader = $area . '/components/com_"
. $this->config->get('component_code_name', 'ERROR') . '/'
. $this->config->get('component_autoloader_path', 'ERROR') . "';";
$autoload_file[] = Indent::_($tabSpace) . 'if (file_exists($power_autoloader))';
$autoload_file[] = Indent::_($tabSpace) . '{';
$autoload_file[] = Indent::_($tabSpace) . Indent::_(1) . 'require_once $power_autoloader;';
$autoload_file[] = Indent::_($tabSpace) . '}';
return implode(PHP_EOL, $not_site);
}
return null;
return implode(PHP_EOL, $autoload_file);
}
/**
@ -287,7 +214,7 @@ class Autoloader
$autoload_method = [];
$autoload_method[] = Indent::_($tabSpace) . '//'
. Line::_(__Line__, __Class__) . ' register additional namespace';
$autoload_method[] = Indent::_($tabSpace) . '\spl_autoload_register(function ($class) {';
$autoload_method[] = Indent::_($tabSpace) . 'spl_autoload_register(function ($class) {';
$autoload_method[] = Indent::_($tabSpace) . Indent::_(1) . '//'
. Line::_(__Line__, __Class__) . ' project-specific base directories and namespace prefix';
$autoload_method[] = Indent::_($tabSpace) . Indent::_(1) . '$search = [';

View File

@ -158,28 +158,38 @@ class Data
// check if layout keys were passed
if (!ArrayHelper::check($layouts))
{
$layout_bucket = [];
// set the Layout data
$lay1 = GetHelper::allBetween(
if (($layouts_found = GetHelper::allBetween(
$content, "LayoutHelper::render('", "',"
);
$lay2 = GetHelper::allBetween(
$content, 'LayoutHelper::render("', '",'
);
if (ArrayHelper::check($lay1)
&& ArrayHelper::check($lay2))
)) !== null)
{
$layouts = array_merge($lay1, $lay2);
$layout_bucket[] = $layouts_found;
}
else
if (($layouts_found = GetHelper::allBetween(
$content, 'LayoutHelper::render("', '",'
)) !== null)
{
if (ArrayHelper::check($lay1))
{
$layouts = $lay1;
}
elseif (ArrayHelper::check($lay2))
{
$layouts = $lay2;
}
$layout_bucket[] = $layouts_found;
}
// set the Layout data
if (($layouts_found = GetHelper::allBetween(
$content, "Joomla__" . "_7ab82272_0b3d_4bb1_af35_e63a096cfe0b___Power::render('", "',"
)) !== null)
{
$layout_bucket[] = $layouts_found;
}
if (($layouts_found = GetHelper::allBetween(
$content, 'Joomla__' . '_7ab82272_0b3d_4bb1_af35_e63a096cfe0b___Power::render("', '",'
)) !== null)
{
$layout_bucket[] = $layouts_found;
}
// Flatten and merge all collected layouts if any
if ($layout_bucket !== [])
{
$layouts = array_merge($layouts, ...$layout_bucket);
}
}