dolibarr/htdocs/modulebuilder/class/NamingContract.class.php

152 lines
5.2 KiB
PHP
Raw Normal View History

NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
<?php
/* Copyright (C) 2026 ATM Consulting <support@atm-consulting.fr>
* Copyright (C) 2026 MDW <mdeweerd@users.noreply.github.com>
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/modulebuilder/class/NamingContract.class.php
* \ingroup modulebuilder
* \brief Immutable value object for module/object name substitutions.
*/
/**
* Immutable value object holding all case variants of a module/object pair.
*
* Provides the canonical, ordered substitution map used by modulebuilder template generation.
* The substitution order in getSubstitutionMap() is deterministic: uppercase and mixed-case
* tokens always precede lowercase ones, preventing partial matches when str_replace processes
* entries sequentially on the same string.
*
* applyTo() uses str_replace() directly NOT make_substitutions() to avoid unintended
* processing of Dolibarr's __(key)__ and __[key]__ patterns present in raw template content.
*/
final class NamingContract
{
/** @var string PascalCase module name, e.g. "MyModule" */
public $moduleNameCase;
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
/** @var string Lowercase module name, e.g. "mymodule" */
public $moduleNameLower;
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
/** @var string Uppercase module name, e.g. "MYMODULE" */
public $moduleNameUpper;
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
/** @var string PascalCase object name, e.g. "MyObject" (empty string for module-only contracts) */
public $objectNameCase;
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
/** @var string Lowercase object name, e.g. "myobject" (empty string for module-only contracts) */
public $objectNameLower;
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
/** @var string Uppercase object name, e.g. "MYOBJECT" (empty string for module-only contracts) */
public $objectNameUpper;
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
/**
* @param string $moduleName Raw module name accepts any casing, ucfirst() is applied
* @param string $objectName Raw object name empty string creates a module-only contract
* @throws \InvalidArgumentException If module and object names are identical (case-insensitive)
*/
public function __construct(string $moduleName, string $objectName = '')
{
$this->moduleNameCase = ucfirst($moduleName);
$this->moduleNameLower = strtolower($moduleName);
$this->moduleNameUpper = strtoupper($moduleName);
if ($objectName === '') {
$this->objectNameCase = '';
$this->objectNameLower = '';
$this->objectNameUpper = '';
return;
}
if (strtolower($moduleName) === strtolower($objectName)) {
throw new \InvalidArgumentException(
'Module and object names cannot be identical (case-insensitive match): "'
. $moduleName . '" vs "' . $objectName . '"'
);
}
$this->objectNameCase = ucfirst($objectName);
$this->objectNameLower = strtolower($objectName);
$this->objectNameUpper = strtoupper($objectName);
}
/**
* Returns the canonical, ordered substitution map.
*
* Order matters: uppercase and mixed-case tokens precede lowercase tokens so that
* str_replace sequential processing cannot partially consume a longer token variant.
* Object tokens (positions 812) are omitted for module-only contracts (objectName = '').
*
* @return array<string, string>
*/
public function getSubstitutionMap(): array
{
$map = [
'MYMODULE' => $this->moduleNameUpper,
'MyModule' => $this->moduleNameCase,
'My module' => $this->moduleNameCase,
'my module' => $this->moduleNameLower,
'Mon module' => $this->moduleNameCase,
'mon module' => $this->moduleNameLower,
'mymodule' => $this->moduleNameLower,
];
if ($this->objectNameLower !== '') {
$map['MYOBJECT'] = $this->objectNameUpper;
$map['MyObject'] = $this->objectNameCase;
$map['My Object'] = $this->objectNameCase;
$map['my object'] = $this->objectNameLower;
$map['myobject'] = $this->objectNameLower;
}
return $map;
}
/**
* Apply the canonical substitution map to a string (file content).
*
* @param string $content File content to process
* @return string Content with substitutions applied
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
*/
public function applyTo(string $content): string
{
$map = $this->getSubstitutionMap();
return str_replace(array_keys($map), array_values($map), $content);
}
/**
* Apply substitution to a filename (lowercase tokens only).
*
* Filenames in Dolibarr are always lowercase only mymodule/myobject are substituted.
* For module-only contracts (objectNameLower = ''), myobject is not substituted.
*
* @param string $filename Template filename containing mymodule/myobject placeholders
* @return string File name with substitutions applied
NEW : Replace MyObject MyModule occurences (#38370) * [COREMB] Centralize myobject/mymodule substitutions via NamingContract value object Introduce NamingContract (immutable value object) and NamingContractValidator to replace 6 scattered ad-hoc $arrayreplacement blocks across index.php and modulebuilder.lib.php. Fixes a critical bug where MYOBJECT token was commented out in initobject, leaving generated class files with unsubstituted placeholders. - NamingContract: canonical ordered substitution map (12 tokens with object, 7 module-only), applyTo() via str_replace (not make_substitutions, to avoid processing Dolibarr __(key)__ patterns in raw templates), applyToFilename() for lowercase-only filename substitution - StrictNamingContractValidator: post-generation scan for residual myobject/ mymodule tokens, skipping /* BEGIN/END MODULEBUILDER */ structural markers - modulebuilderValidateGeneratedFile() helper: non-blocking warnings with XSS-safe output via dol_escape_htmltag() - Collision guard: InvalidArgumentException if module === object name (case-insensitive), wrapped in try-catch at all instantiation sites - 24 PHPUnit tests, 52 assertions * fix(initobject): apply object substitution to all module PHP files After initmodule creates the module structure, files like index.php, lib/module.lib.php, admin/setup.php and class/api_module.class.php still contain myobject/mymodule placeholders. initobject now scans ALL PHP files in the module directory and applies the full NamingContract substitution, and deletes the orphan stats/myobject_index.php placeholder. * fix(initmodule): prevent array_merge from renumbering the '500000' key PHP casts numeric string keys like '500000' to int(500000) internally. array_merge() then renumbers integer keys starting from 0, turning '500000' into key 0. make_substitutions() then calls str_replace(0, ...) which casts 0 to the string '0', replacing EVERY zero digit in every generated file with the idmodule value. Fix: replace the bare '500000' key with '$this->numero = 500000' so that array_merge sees a non-numeric string key and preserves it as-is. * Fix initapi: apply header substitution after addObjectsToApiFile addObjectsToApiFile substitutes properties/constructor/includes via regex but leaves PHPDoc header and class declaration with mymodule/myobject tokens. Fix: apply $arrayreplacement (minus 'MYOBJECT' uppercase key) after addObjectsToApiFile to resolve the remaining residuals. 'MYOBJECT' is excluded to preserve the /* BEGIN MODULEBUILDER API MYOBJECT */ placeholder that addObjectsToApiFile relies on for future object additions. * Move NamingContractTest to core test directory NamingContract and NamingContractValidator are core modulebuilder classes; their tests belong alongside other Dolibarr core tests in test/phpunit/, not in htdocs/modulebuilder/test/phpunit/ (the custom-module pattern). Updated require_once paths accordingly. * fix useless file * fix(phpcs): add missing @param descriptions and function docblocks - NamingContract: merge adjacent string literals in exception message, add description to @param $content in applyTo() - NamingContractValidator: add descriptions to all bare @param tags in interface and StrictNamingContractValidator methods - NamingContractTest: add /** @return void */ docblocks to all test methods, merge unnecessary string concat on template variable * fix(phan/phpstan): remove readonly, add baselines for test file - NamingContract: replace readonly properties with plain public string (readonly requires PHP 8.1, phan minimum target is PHP 7.2) - dev/tools/phan/baseline.txt: add NamingContractTest.php suppression for PhanUndeclaredMethod (PHPUnit stubs not available in phan scope) - NamingContractTest.php: add @phpstan-ignore class.notFound on class declaration and method.notFound on each assertion call (PHPUnit not in phpstan bootstrap, consistent with CommonClassTest.class.php) * fix(phan/phpstan): suppress typed property warnings, NoopNew, class.notFound - baseline.txt: add PhanCompatibleTypedProperty for NamingContract.class.php (typed properties require PHP 7.4, phan min target is 7.2 — warning only but phan exits 1 on any issue including warnings) - baseline.txt: add PhanNoopNew for NamingContractTest.php (constructor called for exception side-effect in guard tests is intentional) - NamingContractTest.php: move @phpstan-ignore class.notFound to inline comment on class declaration (docblock placement not picked up by phpstan) * fix(phan/phpstan): add missing phan suppress on index.php l.574, fix phpstan class docblock - index.php l.574: add @phan-suppress-next-line PhanPluginSuspiciousParamPosition (same pattern as l.577 and l.587 for identical dolReplaceInFile calls where local variable name $destfile matches parameter #3 of the function) - NamingContractTest.php: use standalone /** @phpstan-ignore class.notFound */ docblock before class declaration (inline // comment not picked up by phpstan on class declarations; separate docblock matches CommonClassTest.class.php pattern) * Update baseline.txt * fix(phan): add phan-file-suppress on NamingContractTest for PHPUnit undeclared methods * fix(phan): suppress PhanNoopNew on expectException patterns in NamingContractTest --------- Co-authored-by: Laurent Destailleur <eldy@destailleur.fr>
2026-05-25 12:52:38 +00:00
*/
public function applyToFilename(string $filename): string
{
$search = ['mymodule'];
$replace = [$this->moduleNameLower];
if ($this->objectNameLower !== '') {
$search[] = 'myobject';
$replace[] = $this->objectNameLower;
}
return str_replace($search, $replace, $filename);
}
}