From be80cbde3364a47abce37e4f7ae4270076fad6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20BISSON?= Date: Fri, 23 Mar 2018 22:55:23 +0100 Subject: [PATCH] Source code now conforms to PSR-2 Added composer support Added PHP Code Sniffer configuration Added PHP Mess Detector configuration Added Makefile --- Makefile | 18 +++ TplBlock.php | 283 ++++++++++++++++++++++++++++++++++++++++++ class.TplBlock.php | 139 --------------------- composer.json | 13 ++ phpcs.xml | 47 +++++++ phpmd.xml | 18 +++ sample/sample.php | 100 +++++++++------ test/TplBlockTest.php | 170 +++++++++++++++++-------- 8 files changed, 562 insertions(+), 226 deletions(-) create mode 100755 Makefile create mode 100644 TplBlock.php delete mode 100644 class.TplBlock.php create mode 100644 composer.json create mode 100644 phpcs.xml create mode 100644 phpmd.xml diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..ba2b73d --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +test: test-unit +lint: lint-md lint-cs + +# Run every available unit test +test-unit: + vendor/bin/phpunit --bootstrap vendor/autoload.php test + +# Run PHP Mess Detector against all the code, settings are in phpmd.xml +lint-md: + vendor/bin/phpmd TplBlock.php text ./phpmd.xml + vendor/bin/phpmd test/TplBlockTest.php text ./phpmd.xml + vendor/bin/phpmd sample/sample.php text ./phpmd.xml + +# Run PHP Code Sniffer against all the code, settings are in phpcs.xml +lint-cs: + vendor/bin/phpcs TplBlock.php -s + vendor/bin/phpcs test/TplBlockTest.php -s + vendor/bin/phpcs sample/sample.php -s diff --git a/TplBlock.php b/TplBlock.php new file mode 100644 index 0000000..135e99b --- /dev/null +++ b/TplBlock.php @@ -0,0 +1,283 @@ + + * @license GNU General Public License V3 + * @link https://github.com/gnieark/tplBlock/ + */ +namespace TplBlock; + +/** + * The TplBlock class. + * + * @category Template + * @package TplBlock + * @author gnieark + * @license GNU General Public License V3 + * @link https://github.com/gnieark/tplBlock/ + */ +class TplBlock +{ + /** + * The string starting a block start. + * + * @var string + */ + const BLOCKSTARTSTART = ''; + + /** + * The string starting a block end. + * + * @var string + */ + const BLOCKENDSTART = ''; + + /** + * The string starting an enclosure. + * + * @var string + */ + const STARTENCLOSURE = '{{'; + + /** + * The string ending an enclosure. + * + * @var string + */ + const ENDENCLOSURE = '}}'; + + /** + * The name of the block. + * + * @var string + */ + public $name = ''; + + /** + * The array containing the variables used by TplBlock. + * + * @var array + */ + private $vars = []; + + /** + * The array containing the sub blocks. + * + * @var array + */ + private $subBlocs = []; + + /** + * The regex recognizing that a block is unused. + * + * @var string + */ + private $unusedRegex = ""; + + /** + * Should we trim? + * + * @var boolean + */ + private $trim = true; + + /** + * Initialize TplBlock + * + * The name can be empty only for the top one block. + * + * @param string $name The template name + */ + public function __construct($name = "") + { + // Checks that name is valid. + if ($name !== "" and ! ctype_alnum($name)) { + throw new \UnexpectedValueException( + "Only alpha-numerics chars are allowed on the block name" + ); + } + + $this->name = $name; + + // Build the unused regex. + $this->unusedRegex = '/' + . self::BLOCKSTARTSTART + . ' *([a-z][a-z0-9.]*) *' + . self::BLOCKSTARTEND + . '(.*?)' + . self::BLOCKENDSTART + . ' *\1 *' + . self::BLOCKENDEND + . '/is' + ; + } + + /** + * Add simple variables + * + * The array must be structured like this: + * + * [ "key" => "value", "key2" => "value2" ] + * + * @param array $vars Variables to add. + * + * @return TplBlock For chaining. + */ + public function addVars(array $vars) + { + $this->vars = array_merge($this->vars, $vars); + + return $this; + } + + /** + * Add a sub block. + * + * @param TplBlock $bloc The block to add as a sub block. + * + * @return TplBlock For chaining. + */ + public function addSubBlock(TplBlock $bloc) + { + // An unnamed block cannot be a sub block. + if ($bloc->name === "") { + throw new \UnexpectedValueException( + "A sub tpl block can't have an empty name" + ); + } + + $this->subBlocs[$bloc->name][] = $bloc; + + return $this; + } + + /** + * Generate the sub block regex. + * + * @param string $prefix The prefix to add to the block name. + * @param string $blocName The block name. + * + * @return string The regex. + */ + private function subBlockRegex(string $prefix, string $blocName) + { + return '/' + . self::BLOCKSTARTSTART + . preg_quote($prefix . $blocName) + . self::BLOCKSTARTEND + . ($this->trim === false ? '' : '(?:\R|)?' ) + . '(.*?)' + . ($this->trim === false ? '' : '(?:\R|)?' ) + . self::BLOCKENDSTART + . preg_quote($prefix . $blocName) + . self::BLOCKENDEND + . '/is'; + } + + /** + * Shake the template string and input vars then returns the parsed text. + * + * @param string $str containing the template to parse + * @param string $subBlocsPath optional, for this class internal use. + * The path should look like "bloc.subbloc". + * + * @return string The processed output. + */ + public function applyTplStr(string $str, string $subBlocsPath = "") + { + // Replace all simple vars. + $prefix = $subBlocsPath === "" ? "" : $subBlocsPath . "."; + + foreach ($this->vars as $key => $value) { + $str = str_replace( + self::STARTENCLOSURE . $prefix . $key . self::ENDENCLOSURE, + $value, + $str + ); + } + + // Parse blocs. + foreach ($this->subBlocs as $blocName => $blocsArr) { + $str = preg_replace_callback( + $this->subBlockRegex($prefix, $blocName), + function ($m) use ($blocName, $blocsArr, $prefix) { + $out = ""; + foreach ($blocsArr as $bloc) { + // Recursion. + $out .= $bloc->applyTplStr( + $m[1], + $prefix . $blocName + ); + } + + return $out; + }, + $str + ); + } + + // Delete unused blocs. + $str = preg_replace($this->unusedRegex, "", $str); + + return $str; + } + + /** + * Load a file, and pass his content to applyTplStr function. + * + * @param string $file The file path of the template to load + * + * @return string The processed output. + */ + public function applyTplFile(string $file) + { + if (! $tplStr = file_get_contents($file)) { + throw new \UnexpectedValueException("Cannot read given file $file"); + } + + return $this->applyTplStr($tplStr, ""); + } + + /** + * Enables trimming. + * + * @return TplBlock For chaining. + */ + public function doTrim() + { + $this->trim = true; + + return $this; + } + + /** + * Disables trimming. + * + * @return TplBlock For chaining. + */ + public function dontTrim() + { + $this->trim = false; + + return $this; + } +} diff --git a/class.TplBlock.php b/class.TplBlock.php deleted file mode 100644 index 55d46d3..0000000 --- a/class.TplBlock.php +++ /dev/null @@ -1,139 +0,0 @@ -'; - const blockEndStart = ''; - - const startEnclosure = '{{'; - const endEnclosure = '}}'; - - public $name = ''; - private $vars = array(); - private $subBlocs = array(); - private $unusedRegex = ""; - - /* - * Initialise TplBlock - * Input object name - * Can be empty only for the top one block - */ - - public function __construct($name = NULL){ - $this->name = $name; - - if(!is_null($name) && !ctype_alnum($name)){ - throw new InvalidTemplateException("Only alpha-numerics chars are allowed on the block name"); - return false; - } - $this->unusedRegex = '/' - . self::blockStartStart - . ' *([a-z][a-z0-9.]*) *' - . self::blockStartEnd - . '(.*?)' - . self::blockEndStart - . ' *\1 *' - . self::blockEndEnd - . '/is' - ; - - } - - /* - * Add simple vars - * Input array structured like: - * {"key":"value","key2":"value2"} - */ - public function add_vars(ARRAY $vars){ - $this->vars = array_merge($this->vars,$vars); - } - - /* - * add_sub_block - * Input: a TplBlock object. - */ - public function add_sub_block(TplBlock $bloc){ - if(is_null($bloc->name) || empty($bloc->name)){ - throw new InvalidTemplateException("A sub tpl bloc can't have an empty name"); - return false; - } - $this->subBlocs[$bloc->name][] = $bloc; - } - - private function subBlockRegex($prefix, $blocName,$trim = true) { - return '/' - . self::blockStartStart - . preg_quote($prefix . $blocName) - . self::blockStartEnd - . (($trim === false)? '' : '(?:\R|)?' ) - . '(.*?)' - . (($trim === false)? '' : '(?:\R|)?' ) - . self::blockEndStart - . preg_quote($prefix . $blocName) - . self::blockEndEnd - . '/is'; - } - - /* - * Shake the template string and input vars - * Then returns the parsed text - * Input: - * $str String containing the template to parse - * $subBlocsPath String optional, for this class internal use. The path like "bloc.subbloc" - * $trim Boolean - * if true(default), the potentials Carriages returns beginning - * and ending the bloc are deleted - */ - public function apply_tpl_str($str,$subBlocsPath = "", $trim = true){ - - //replace all simple vars - $prefix = (empty($subBlocsPath)? "" : $subBlocsPath."."); - foreach($this->vars as $key=>$value){ - - $str = str_replace(self::startEnclosure . $prefix . $key . self::endEnclosure, - $value, - $str); - } - - //parse blocs - foreach($this->subBlocs as $blocName => $blocsArr){ - $str = preg_replace_callback( - $this->subBlockRegex($prefix, $blocName, $trim), - function($m) use($blocName,$blocsArr,$prefix, $trim) { - $out = ""; - foreach($blocsArr as $bloc){ - //recursion - $out.=$bloc->apply_tpl_str( $m[1] , $prefix . $blocName , $trim ); - } - return $out; - } - ,$str - ); - } - - // Delete unused blocs - $str = preg_replace($this->unusedRegex, "", $str); - return $str; - - } - - /* - * load a file, and pass his content to apply_tpl_str function. - */ - public function apply_tpl_file($file, $trim = true){ - if(!$tplStr = file_get_contents($file)){ - throw new InvalidTemplateException("Cannot read given file ".$file); - return false; - } - return $this->apply_tpl_str($tplStr, null, $trim); - } -} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..88dfd89 --- /dev/null +++ b/composer.json @@ -0,0 +1,13 @@ +{ + "autoload": { + "classmap": [ + "./" + ] + }, + "require-dev": { + "phpunit/phpunit": "^6", + "phpdocumentor/phpdocumentor": "2.*", + "phpmd/phpmd": "^2.6", + "gamegos/php-code-sniffer": "*" + } +} diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..47a9b14 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . + ./test + diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 0000000..0b7131d --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,18 @@ + + + + Custom rule set for TemplateEngine + + + + + + + + diff --git a/sample/sample.php b/sample/sample.php index 1ecb79c..a7e2f87 100644 --- a/sample/sample.php +++ b/sample/sample.php @@ -1,47 +1,77 @@ + * @license GNU General Public License V3 + * @link https://github.com/gnieark/tplBlock/ + */ +require_once "../vendor/autoload.php"; -include("../class.TplBlock.php"); +use TplBlock\TplBlock; -$tpl = new TplBlock(); +const PRIMES = [ 1, 2, 3, 5, 7, 11 ]; -//simples vars +/** + * Find divisors of a number. + * + * It works as long as the number is less than the last PRIMES number squared. + * + * @param int $number The number to find divisors for. + * + * @return array An array of divisors. + */ +function findDivisors(int $number) +{ + $divisors = []; + $index = 1; + while ($number > 1 and $index < count(PRIMES)) { + if ($number % PRIMES[$index] != 0) { + $index++; + continue; + } + + $number = $number / PRIMES[$index]; + $divisors[] = PRIMES[$index]; + } -$tpl->add_vars(array( - "name" => "Gnieark", - "title" => "Monsieur", - "firstname" => "Grouik" - ) - ); + return $divisors; +} -$primes = array(1,2,3,5,7,11); +$variables = [ + "name" => "Gnieark", + "title" => "Monsieur", + "firstname" => "Grouik", +]; -// a sub bloc -foreach($primes as $prime){ - $tplPrime = new TplBlock('primes'); - $tplPrime->add_vars(array('number' => $prime)); - $tpl->add_sub_block($tplPrime); -} +// Simples vars. +$template = (new TplBlock())->addVars($variables); -// test sub - sub blocs -for ($i = 2; $i < 121; $i++){ - - $tplNumber = new TplBlock('number'); - $tplNumber->add_vars( array("value" => $i)); - $index = 1; - $number = $i; - while ( $number > 1 && $index < count($primes)){ - if($number % $primes[$index] == 0){ - $number = $number / $primes[$index]; - $tplDivisor = new TplBlock("divisor"); - $tplDivisor->add_vars( array("value" => $primes[$index])); - $tplNumber->add_sub_block($tplDivisor); - }else{ - $index++; - } - } - $tpl->add_sub_block($tplNumber); +// A sub bloc. +foreach (PRIMES as $prime) { + $template->addSubBlock( + (new TplBlock("primes"))->addVars([ "number" => $prime ]) + ); } +// Find highest number for which we can find divisors. +$lastNumber = pow(PRIMES[count(PRIMES) - 1], 2); + +// Test sub - sub blocs. +for ($i = 2; $i <= $lastNumber; $i++) { + $templateNumber = (new TplBlock("number"))->addVars([ "value" => $i ]); + foreach (findDivisors($i) as $divisor) { + $templateNumber->addSubBlock( + (new TplBlock("divisor"))->addVars([ "value" => $divisor ]) + ); + } + + $template->addSubBlock($templateNumber); +} -echo $tpl->apply_tpl_file("tpl.txt"); \ No newline at end of file +echo $template->applyTplFile("tpl.txt"); diff --git a/test/TplBlockTest.php b/test/TplBlockTest.php index 63b6ec2..72abab9 100644 --- a/test/TplBlockTest.php +++ b/test/TplBlockTest.php @@ -1,45 +1,102 @@ - + * @license GNU General Public License V3 + * @link https://github.com/gnieark/tplBlock/ + */ +namespace TplBlockTest; + use PHPUnit\Framework\TestCase; -require_once __DIR__.'/../class.TplBlock.php'; +use TplBlock\TplBlock; -class TplBlockTest extends TestCase{ +/** + * The TplBlockTest class. + * + * @category Template + * @package TplBlock + * @author gnieark + * @license GNU General Public License V3 + * @link https://github.com/gnieark/tplBlock/ + */ +class TplBlockTest extends TestCase +{ /** - * @expectedException InvalidTemplateException - */ - public function testSendEmptyNameOnSubFunction(){ - $tpl = new TplBlock(); - $subTpl = new TplBlock(); - $tpl->add_sub_block($subTpl); + * A template cannot accept a sub template with no name. + * + * @return void + * + * @expectedException UnexpectedValueException + */ + public function testSendEmptyNameOnSubFunction() + { + $template = new TplBlock(); + $subTemplate = new TplBlock(); + + $template->addSubBlock($subTemplate); } - public function testsimpleVar(){ - $tpl = new TplBlock(); - $tpl->add_vars(array( - "name" => "Gnieark", - "title" => "Monsieur", - "firstname" => "Grouik" - ) - ); - $this->assertEquals("Hello Gnieark", $tpl->apply_tpl_str("Hello {{name}}")); + /** + * Verify that variable replacement takes place. + * + * @return void + */ + public function testSimpleVar() + { + $template = new TplBlock(); + + $variables = [ + "name" => "Gnieark", + "title" => "Monsieur", + "firstname" => "Grouik", + ]; + + $actual = $template + ->addVars($variables) + ->applyTplStr("Hello {{name}}"); + + $this->assertEquals("Hello Gnieark", $actual); } - //test from a file - public function testParseFromFile(){ - file_put_contents("temp.txt","Hello {{name}}"); - $tpl = new TplBlock(); - $tpl->add_vars(array( - "name" => "Gnieark", - "title" => "Monsieur", - "firstname" => "Grouik" - ) - ); - $this->assertEquals("Hello Gnieark", $tpl->apply_tpl_file("temp.txt")); - unlink("temp.txt"); + + /** + * Test from a file. + * + * @return void + */ + public function testParseFromFile() + { + file_put_contents("temp.txt", "Hello {{name}}"); + + $template = new TplBlock(); + + $variables = [ + "name" => "Gnieark", + "title" => "Monsieur", + "firstname" => "Grouik", + ]; + + $actual = $template + ->addVars($variables) + ->applyTplFile("temp.txt"); + + $this->assertEquals("Hello Gnieark", $actual); + + unlink("temp.txt"); } - //test blocs - public function testBlocs(){ - $str = " + /** + * Test blocs. + * + * @return void + */ + public function testBlocs() + { + $model = " Bhah blah wpooie456 have to be shown @@ -48,29 +105,38 @@ class TplBlockTest extends TestCase{ WONT to be shown "; - $tpl = new TplBlock(); - $tpl2 = new TplBlock("bloc"); - $tpl->add_sub_block($tpl2); - $str = $tpl->apply_tpl_str($str); - $this->assertContains('have',$str); - $this->assertFalse(strpos("WONT",$str)); + + $template = new TplBlock(); + + $actual = $template + ->addSubBlock(new TplBlock("bloc")) + ->applyTplStr($model); + + $this->assertContains("have", $actual); + $this->assertFalse(strpos("WONT", $actual)); } - //test if error on blocks names WTF /** - * @expectedException InvalidTemplateException - */ - public function testIfErrorOnForbiddenName(){ - $tpl = new TplBlock("kjsd54 65"); + * Test if error on blocks names WTF. + * + * @return void + * + * @expectedException UnexpectedValueException + */ + public function testIfErrorOnForbiddenName() + { + new TplBlock("kjsd54 65"); } - //test if error on blocks names WTF /** - * @expectedException InvalidTemplateException - */ - public function testIfErrorOnForbiddenNameAgain(){ - $tpl = new TplBlock("kjsd54.5"); + * Test if error on blocks names WTF. + * + * @return void + * + * @expectedException UnexpectedValueException + */ + public function testIfErrorOnForbiddenNameAgain() + { + new TplBlock("kjsd54.5"); } - - -} \ No newline at end of file +}