From 2c54773ab740aa8b105b999e5df0e0f147b282c5 Mon Sep 17 00:00:00 2001 From: fredo Date: Mon, 20 Nov 2023 09:47:39 +0000 Subject: [PATCH 1/3] fixed error in create-view --- packages/core/actions/config/create-view.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/core/actions/config/create-view.php b/packages/core/actions/config/create-view.php index ab6edc4b4..b6b8b89cc 100644 --- a/packages/core/actions/config/create-view.php +++ b/packages/core/actions/config/create-view.php @@ -68,6 +68,17 @@ throw new Exception('view_already_exists', QN_ERROR_INVALID_PARAM); } +$nest = explode("/",$entity); +array_pop($nest); +$path = implode("/",$nest); + +if(!is_dir(QN_BASEDIR."/packages/{$package}/views/{$path}")){ + mkdir(QN_BASEDIR."/packages/{$package}/views/{$path}",0777,true); + if(!is_dir(QN_BASEDIR."/packages/{$package}/views/{$path}")) { + throw new Exception('file_access_denied', QN_ERROR_UNKNOWN); + } +} + $f = fopen($file,"w"); if(!$f) { From f0e0c3b16859b0c6975587e26a60541df185a0a1 Mon Sep 17 00:00:00 2001 From: fredo Date: Mon, 20 Nov 2023 10:08:17 +0000 Subject: [PATCH 2/3] fixed create and delete model --- packages/core/actions/config/create-model.php | 7 +++++-- packages/core/actions/config/delete-model.php | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/actions/config/create-model.php b/packages/core/actions/config/create-model.php index f3b81b45a..48930cb2b 100755 --- a/packages/core/actions/config/create-model.php +++ b/packages/core/actions/config/create-model.php @@ -53,6 +53,9 @@ $namespace = implode("\\", $parts); +$nest = str_replace("\\","/",$namespace); + + if(strlen($name) <= 0){ throw new Exception('empty_model_name', QN_ERROR_INVALID_PARAM); } @@ -62,7 +65,7 @@ } // Verification de la non existence du model -$file = QN_BASEDIR."/packages/{$package}/classes/{$model_path}.class.php"; +$file = QN_BASEDIR."/packages/{$package}/classes/{$nest}/{$name}.class.php"; if(file_exists($file)) { throw new Exception('model_already_exists', QN_ERROR_INVALID_PARAM); @@ -88,7 +91,7 @@ } // create folder if missing -$dir = QN_BASEDIR."/packages/{$package}/classes/".str_replace("\\","/",$namespace); +$dir = QN_BASEDIR."/packages/{$package}/classes/".$nest; $d = mkdir($dir, 0777, true); $f = fopen($file,"w"); diff --git a/packages/core/actions/config/delete-model.php b/packages/core/actions/config/delete-model.php index 6cd20bc7c..099f1e83b 100644 --- a/packages/core/actions/config/delete-model.php +++ b/packages/core/actions/config/delete-model.php @@ -48,6 +48,9 @@ $namespace = implode("\\", $parts); + +$nest = str_replace("\\","/",$namespace); + if(strlen($name) <= 0){ throw new Exception('empty_model_name', QN_ERROR_INVALID_PARAM); } @@ -57,7 +60,7 @@ } // Verification de la non existence du model -$file = QN_BASEDIR."/packages/{$package}/classes/{$model_path}.class.php"; +$file = QN_BASEDIR."/packages/{$package}/classes/{$nest}/{$name}.class.php"; if(!file_exists($file)) { throw new Exception('model_does_not_exists', QN_ERROR_INVALID_PARAM); From 7d98ed5ce0b8ec5b716af95bc40ba5b86d189cf4 Mon Sep 17 00:00:00 2001 From: fredo Date: Mon, 20 Nov 2023 15:10:40 +0000 Subject: [PATCH 3/3] added update-controller and renamed save-model to update-model --- .../core/actions/config/update-controller.php | 184 ++++++++++++++++++ .../{save-model.php => update-model.php} | 74 ++----- 2 files changed, 203 insertions(+), 55 deletions(-) create mode 100644 packages/core/actions/config/update-controller.php rename packages/core/actions/config/{save-model.php => update-model.php} (86%) diff --git a/packages/core/actions/config/update-controller.php b/packages/core/actions/config/update-controller.php new file mode 100644 index 000000000..9e9571c48 --- /dev/null +++ b/packages/core/actions/config/update-controller.php @@ -0,0 +1,184 @@ + + Some Rights Reserved, Cedric Francoys, 2010-2021 + Licensed under GNU LGPL 3 license +*/ +use PhpParser\{Node, NodeTraverser, NodeVisitorAbstract, ParserFactory, NodeFinder, NodeDumper, PrettyPrinter, BuilderFactory, Comment}; +use PhpParser\Node\Expr\CallLike; +use equal\orm\Model; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Name; + +list($params, $providers) = eQual::announce([ + 'description' => "Translate an entity definition to a PHP file and store it in related package dir.", + 'help' => "This controller rely on the PHP binary. In order to make them work, sure the PHP binary is present in the PATH.", + 'response' => [ + 'content-type' => 'text/plain', + 'charset' => 'UTF-8', + 'accept-origin' => '*' + ], + 'params' => [ + 'controller' => [ + 'description' => 'Name of the controller.', + 'type' => 'string', +// 'required' => true + ], + 'operation' => [ + 'description' => 'Operation the controller relates to.', + 'type' => 'string', + 'selection' => [ + 'do', + 'get', + 'show' + ] + ], + 'payload' => [ + 'description' => 'Controller `announce` descriptor.', + 'type' => 'array', +// 'required' => true + ] + ], + 'providers' => ['context', 'orm'] +]); + +/** + * @var \equal\php\Context $context + * @var \equal\orm\ObjectManager $orm + */ +list($context, $orm) = [$providers['context'], $providers['orm']]; + +// Create all the object to use for using PhpParser +$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); +$nodeFinder = new NodeFinder; +$traverser = new NodeTraverser; +$prettyPrinter = new PhpParser\PrettyPrinter\Standard; + +// Get the parts of the entity string, separated by backslashes +$params['entity'] = str_replace('_', '\\', $params['entity']); +$parts = explode('\\', $params['entity']); + +// Get the package name from the first part of the string +$package = array_shift($parts); +// Get the file name from the last part of the string +$filename = array_pop($parts); +// Get the class path from the remaining part +$class_path = implode('/', $parts); + + +// Get a string representation from the code_php variable, with backslashes escaped +$code_string = str_replace("\\\\", "\\", var_export($params['payload'], true)); + +// #test #toremove +// $code_string = "[ +// 'description' => \"save a representation of a view to a json file\", +// 'response' => [ +// 'content-type' => 'text/plain', +// 'charset' => 'UTF-8', +// 'accept-origin' => '*', +// 'schema' => [ +// 'type' => 'entity', +// 'qty' => 'one', +// 'entity' => 'core\User', +// 'values' => [] +// ] +// ], +// 'params' => [ +// 'entity' => [ +// 'description' => 'name of the entity', +// 'type' => 'string', +// 'required' => true +// ], +// 'view_id' => [ +// 'description' => 'id of the view', +// 'type' => 'string', +// 'required' => true +// ], +// ], +// 'access' => [ +// 'visibility' => 'protected', +// 'groups' => ['admins'] +// ], +// 'providers' => ['context'] +// ]"; + +// Create a virtual file holding the default structure, and generate a minimal AST +$ast_temp = $parser->parse("findFirst($ast_temp, function(Node $node) { + // support for `announce` + if(get_class($node) == 'PhpParser\Node\Expr\FuncCall' && property_exists($node, 'name') && get_class($node->name) == 'PhpParser\Node\Name' && $node->name->getFirst() == 'announce') { + return true; + } + // support for `eQual::announce` + if(get_class($node) == 'PhpParser\Node\Expr\StaticCall' && property_exists($node, 'class') && get_class($node->class) == 'PhpParser\Node\Name' && $node->class->getFirst() == 'eQual') { + return property_exists($node, 'name') && get_class($node->name) == 'PhpParser\Node\Identifier' && $node->name->name == 'announce'; + } + return false; +}); + +// Add a visitor to the traverse in order to consider the getColumns method and update its content with new schema +$traverser->addVisitor( + new class($node) extends NodeVisitorAbstract { + private $node; + + public function __construct($node) { + $this->node = $node; + } + + public function leaveNode(Node $node) { + if ($node->name->name === 'announce') { + return $this->node; + } + } + } + ); + +// Get the full path of the file +$dir = ['do' => 'actions', 'get' => 'date', 'show' => 'apps'][$params['operation']]; +$file = QN_BASEDIR."/packages/{$package}/{$dir}/{$class_path}/{$filename}.php"; + +// Get the code from the original file ... +$code = file_get_contents($file); +// ... and parse it to create an AST +$stmtOriginal = $parser->parse($code); + + +// #test #toremove fake original file +// $stmtOriginal = $parser->parse("traverse($stmtOriginal); +// Pretty print the modified AST ... +$result = $prettyPrinter->prettyPrintFile($stmtModified); + +// ... and write back the code to the file +file_put_contents($file, $result); + +try { + // apply coding standards (ecs.php is expected in QN_BASEDIR) + $command = 'php ./vendor/bin/ecs check "' . str_replace('\\', '/', $file) . '" --fix'; + + if(exec($command) === false) { + throw new Exception('command_failed', QN_ERROR_UNKNOWN); + } +} +catch(Exception $e) { + trigger_error("PHP::unable to beautify rendered file ($file): ".$e->getMessage(), QN_REPORT_INFO); +} + +$result = file_get_contents($file); + +$context->httpResponse() + ->body($result) + ->send(); \ No newline at end of file diff --git a/packages/core/actions/config/save-model.php b/packages/core/actions/config/update-model.php similarity index 86% rename from packages/core/actions/config/save-model.php rename to packages/core/actions/config/update-model.php index e282ad4cf..9f1c5aa12 100644 --- a/packages/core/actions/config/save-model.php +++ b/packages/core/actions/config/update-model.php @@ -6,8 +6,7 @@ */ use PhpParser\{Node, NodeTraverser, NodeVisitorAbstract, ParserFactory, NodeFinder, NodeDumper, PrettyPrinter, BuilderFactory, Comment}; use equal\orm\Model; - -list($params, $providers) = announce([ +list($params, $providers) = eQual::announce([ 'description' => "Translate an entity definition to a PHP file and store it in related package dir.", 'help' => "This controller rely on the PHP binary. In order to make them work, sure the PHP binary is present in the PATH.", 'response' => [ @@ -16,7 +15,7 @@ 'accept-origin' => '*' ], 'params' => [ - 'entity' => [ + 'entity' => [ 'description' => 'Name of the entity (class).', 'type' => 'string', 'required' => true @@ -39,62 +38,45 @@ ], 'providers' => ['context', 'orm'] ]); - /** * @var \equal\php\Context $context * @var \equal\orm\ObjectManager $orm */ list($context, $orm) = [$providers['context'], $providers['orm']]; - // Create all the object to use for using PhpParser $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); $nodeFinder = new NodeFinder; $traverser = new NodeTraverser; $prettyPrinter = new PhpParser\PrettyPrinter\Standard; - // Get the parts of the entity string, separated by backslashes $parts = explode('\\', $params['entity']); - // Get the package name from the first part of the string $package = array_shift($parts);// Get the package name from the first part of the string // Get the file name from the last part of the string $filename = array_pop($parts); // Get the class path from the remaining part $class_path = implode('/', $parts); - $getColumns = null; $code_php = null; - -// Get the full path of the file -$file = QN_BASEDIR."/packages/{$package}/classes/{$class_path}/{$filename}.class.php"; - // If you want to use save-model for updating a class/getColumns if($params['part'] == 'class') { // Decode the JSON string to a PHP object $code_php = sanitizeColumns($params['payload']['fields']); - - // Get the string representation of the code_php variable, with backslashes escaped - $code_string = str_replace("\\\\", "\\", var_export($code_php, true) ); - - - // Create a temporary file with the following contents and then parse it in order to generate a minimal AST - $ast_temp = $parser->parse("parse("findFirst($ast_temp, function(Node $node) {return $node->name->name === "getColumns";}); - // Add a visitor to the traverse in order to consider the getColumns method and update its content with new schema $traverser->addVisitor( new class($nodeGetColumns, "getColumns") extends NodeVisitorAbstract { - private $node; private $target; - public function __construct($node, $target) { $this->node = $node; $this->target = $target; } - public function leaveNode(Node $node) { if ($node->name->name === $this->target) { return $this->node; @@ -102,18 +84,13 @@ public function leaveNode(Node $node) { } } ); - - // Add a visitor to the traverser for setting the doc comments on the class node $traverser->addVisitor( new class($code_php) extends NodeVisitorAbstract { - private $code_php; - public function __construct($code_php) { $this->code_php = $code_php; } - public function leaveNode(Node $node) { if ($node instanceof Node\Stmt\Class_) { $node->setDocComment(new Comment\Doc(getPropertiesAsComments($this->code_php))); @@ -123,66 +100,54 @@ public function leaveNode(Node $node) { } ); } - +// Get the full path of the file +$file = QN_BASEDIR."/packages/{$package}/classes/{$class_path}/{$filename}.class.php"; // Get the code from the original file ... $code = file_get_contents($file); // ... and parse it to create an AST -$stmts = $parser->parse($code); - +$stmtOriginal = $parser->parse($code); // Update the AST by using visitors attached to the traverser -$modifiedStmts = $traverser->traverse($stmts); - +$stmtModified = $traverser->traverse($stmtOriginal); // Pretty print the modified AST ... -$result = $prettyPrinter->prettyPrintFile($modifiedStmts); +$result = $prettyPrinter->prettyPrintFile($stmtModified); // ... and write back the code to the file file_put_contents($file, $result); - try { // apply coding standards (ecs.php is expected in QN_BASEDIR) $command = 'php ./vendor/bin/ecs check "' . str_replace('\\', '/', $file) . '" --fix'; - if(exec($command) === false) { throw new Exception('command_failed', QN_ERROR_UNKNOWN); } } catch(Exception $e) { - // #todo - trigger a warning + trigger_error("PHP::unable to beautify rendered file ($file): ".$e->getMessage(), QN_REPORT_INFO); } - $result = file_get_contents($file); - $context->httpResponse() ->body($result) ->send(); - /** - * This function deletes the property of the schema that are already inherited from the Model. + * Filter out the properties of the schema from special columns inherited from the Model. * * @param array $properties The new schema, with all the properties even those that are already in th Model * - * @return array A new array without the properties already inherited from Model + * @return array A new array without the properties inherited from Model. */ function sanitizeColumns($properties) { $result = []; $special_columns = Model::getSpecialColumns(); foreach($properties as $property => $descriptor) { - if(array_key_exists($property, $special_columns)){ - if($descriptor != $special_columns[$property]){ - if($special_columns[$property]["type"] != 'datetime') { - $result[$property] = $descriptor; - } - } + if(!array_key_exists($property, $special_columns)){ + $result[$property] = $descriptor; } - else { + elseif($descriptor !== $special_columns[$property]){ $result[$property] = $descriptor; } } - return $result; } - /** - * This function gets the comments for the properties of the model + * Generate the PHP comments for the properties of the model. * * @param array The properties which need to be in the doc. * @return string PHP Doc string. @@ -214,7 +179,6 @@ function getPropertiesAsComments($properties) { } $comment .= "\n"; } - $comment .= "*/"; return $comment; -} +} \ No newline at end of file