Select Git revision
CodeGeneratorService.php
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
CodeGeneratorService.php 14.04 KiB
<?php
namespace UnicaenCode\Service;
use Exception;
use UnicaenCode\Form\AbstractForm;
use UnicaenCode\Form\FormMaker;
use UnicaenCode\Generator\GeneratorFromInterface;
use UnicaenCode\Generator\GeneratorInterface;
use UnicaenCode\Service\Traits\ConfigServiceAwareTrait;
use UnicaenCode\Util;
use Laminas\Console\Console;
use Laminas\Http\Request;
use Laminas\Mvc\Controller\AbstractActionController;
use Laminas\Stdlib\ArrayUtils;
use Laminas\Stdlib\Parameters;
/**
*
*
* @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
*/
class CodeGeneratorService
{
use ConfigServiceAwareTrait;
public function generer(array $params): array
{
$oriParams = $params;
if ($title = $params['title'] ?? null) {
if (Console::isConsole()) {
echo $title."\n";
} else {
echo '<h1>' . $title . '</h1>';
}
}
$doGen = true;
if (Console::isConsole()) {
$console = $this->paramsFromConsole();
foreach ($oriParams as $name => $value) {
if (is_array($value)) {
if (array_key_exists($name, $console)) {
$params[$name] = $console[$name];
} else {
$params[$name] = $value['value'] ?? null;
}
}
}
} else {
$formParams = [];
if (Util::$controller->getRequest()->isPost()) {
$post = Util::$controller->getRequest()->getPost()->toArray();
} else {
$post = [];
$doGen = false;
}
foreach ($oriParams as $name => $value) {
if (is_array($value)) {
$formParams[$name] = $value;
if (array_key_exists($name, $post)) {
$formParams[$name]['value'] = $post[$name];
}
}
}
$form = FormMaker::createFromArray($formParams);
if ($form->hasSaisieElements()) { $form->afficher();
} else {
$doGen = true;
}
foreach ($oriParams as $name => $value) {
if (is_array($value)) {
$params[$name] = $form->getParamValue($name);
}
}
if ($cmd = $this->genCmd($oriParams, $params)) {
$params['commandLine'] = $cmd;
}
}
if ($doGen) {
$params = $this->generate($params);
}
return $params;
}
protected function paramsFromConsole(): array
{
$params = [];
$request = Util::$controller->getRequest();
$ps = $request->getParams()->toArray();
$i = 0;
while (array_key_exists('p' . $i, $ps)) {
$param = $ps['p' . $i];
$eq = strpos($param, '=');
if (false !== $eq) {
$key = substr($param, 0, $eq);
$value = substr($param, $eq + 1);
if ($value === 'true') $value = true;
if ($value === 'false') $value = false;
$params[$key] = $value;
}
$i++;
}
return $params;
}
public function getParams(string $name): array
{
$paramsDirs = $this->getServiceConfig()->getParamsDirs();
foreach ($paramsDirs as $paramsDir) {
$paramsFile = $paramsDir . '/' . $name . '.php';
if (file_exists($paramsFile)) break;
}
if (!$paramsFile) {
throw new Exception('Le fichier de paramètres ' . $name . ' n\'a pas été trouvé dans les répertoires de paramètres d\'UnicaenCode');
}
$params = require $paramsFile;
return $params;
}
protected function generate(array $params): array
{
/* Passage du générateur */
$generator = $params['generator'] ?? null;
if (is_string($generator)) {
if ($generator = $this->getGenerator($generator)) {
$params = $generator->generate($params);
}
} elseif (is_callable($generator)) {
$params = $generator($params);
}
/* On crée les sous-configs */
$genFrom = $this->getServiceConfig()->getGeneratorsFrom();
foreach ($genFrom as $key => $genConf) {
$this->generateFrom($params, $key);
}
foreach ($params as $name => $value) {
if (is_array($value) && !array_key_exists($name, $genFrom)) {
$params[$name] = $this->generate($value);
}
}
$params = $this->runTriggers($params);
/* Génération du fichier final & rendu */
$params = $this->makeCode($params);
if (Console::isConsole()){
$this->write($params);
}else{
$this->echo($params);
}
if (isset($params['commandLine'])) {
?>
<h1>Création des fichiers depuis la ligne de commandes</h1>
<div class="alert alert-warning">
<p>Pour ajouter les fichiers listés ci-dessus à votre projet (hors exemples de configuration), lancez depuis le répertoire de votre projet la
commande suivante :
<br/>
<code><?= $params['commandLine'] ?></code>
</p>
</div>
<?php
}
return $params;
}
protected function genCmd(array $oriParams, array $params): ?string
{
$diff = [];
foreach ($oriParams as $key => $oriVal) {
if (is_callable($oriVal)) continue;
if (is_array($oriVal)) $oriVal = $oriVal['value'] ?? null;
if ($oriVal != $params[$key]) {
$diff[$key] = $params[$key];
}
}
$cmd = 'php public/index.php UnicaenCode '; $viewName = Util::$controller->params()->fromRoute('view');
$cmd .= $viewName;
if (!empty($diff)) {
foreach ($diff as $k => $v) {
if ($v === true) $v = 'true';
if ($v === false) $v = 'false';
$cmd .= ' ' . $k . '=' . escapeshellarg($v);
}
}
return $cmd;
}
public function generateFrom(array &$params, string $key)
{
$genFrom = $this->getServiceConfig()->getGeneratorsFrom();
$generator = $genFrom[$key]['generator'] ?? null;
$useParams = $genFrom[$key]['useParams'] ?? [];
if (array_key_exists($key, $params) && $params[$key] === true) {
$subParams = self::transferParams($params, $useParams);
if ($generator) {
$subParams['generator'] = $generator;
}
$params[$key] = $this->generate($subParams);
}
}
public static function transferParams(array $params, array $useParams): array
{
$subParams = [];
foreach ($useParams as $useParam) {
if (isset($params[$useParam])) $subParams[$useParam] = $params[$useParam];
}
return $subParams;
}
protected function runTriggers(array $params): array
{
$triggers = $this->getServiceConfig()->getTriggers();
foreach ($triggers as $t) {
$function = $t['function'];
unset($t['function']);
$run = true;
if (!empty($t)) {
foreach ($t as $pn => $pv) {
if (!isset($params[$pn])) {
$run = false;
break;
}
if (is_array($pv)) {
if (!in_array($params[$pn], $pv)) {
$run = false;
break;
}
} else {
if ($params[$pn] != $pv) {
$run = false;
break;
}
}
} }
if ($run) {
$params = $function($params);
}
}
return $params;
}
/**
*
* @throws Exception
*/
protected function makeCode(array $params)
{
$template = $params['template'] ?? null;
if (!$template) return $params;
$code = $this->getTemplateCode($template);
foreach ($params as $param => $value) {
if (!is_array($value) && !is_object($value)) {
$code = $this->codeSetVariable($code, $param, $value);
}
}
$code = trim($code); // éviter les espaces inutiles en début et fin de fichier
/* On débarasse le code de ses caractères inutiles !! */
$code = explode(PHP_EOL, $code);
foreach ($code as $i => $line) {
if (trim($line)) {
$code[$i] = rtrim($line);
} else {
$code[$i] = '';
}
}
$code = implode(PHP_EOL, $code);
$params['code'] = $code;
return $params;
}
public function getGenerator(string $generator): ?GeneratorInterface
{
$genClass = $generator;
if (!class_exists($generator)) {
$generator = 'UnicaenCode\\Generator\\' . $generator;
}
if (class_exists($generator)) {
return new $generator;
}
return null;
}
public function getTemplateCode(string $template): string
{
$templateDirs = $this->getServiceConfig()->getTemplateDirs();
$templateFile = null;
foreach ($templateDirs as $templateDir) {
$templateFile = $templateDir . '/' . $template . '.php'; if (file_exists($templateFile)) break;
}
if (!$templateFile) {
throw new Exception('Le modèle ' . $template . ' n\'a pas été trouvé dans les répertoires de modèles d\'UnicaenCode');
}
return file_get_contents($templateFile);
}
protected function codeSetVariable($code, $param, $value)
{
// Si c'est un tableau alors on concatène en string...
if (is_array($param)) {
$param = implode('', $param);
}
// application des paramètres simples
$code = str_replace('<' . $param . '>', $value, $code);
// Gestion des conditions
$if = '<if ' . $param . '>';
while (false !== ($begin = strpos($code, $if))) {
$endIf = '<endif ' . $param . '>';
$end = strpos($code, $endIf, $begin);
if ($value) {
$ifCode = ltrim(substr($code, $begin + strlen($if), $end - $begin - strlen($if)));
} else {
$ifCode = '';
}
$end += strlen($endIf);
if (isset($code[$begin - 1]) && isset($code[$end])) {
$b = trim($code[$begin - 1]);
$e = trim($code[$end]);
// ATTENTION : il n'y pas de support des retours chariot Windows (\n\r) car ils font 2 caractères ! ! !
if ($b === '' && $e === '') $end++; // pour éviter les retours chariot ou espaces inutiles
}
$code = substr($code, 0, $begin) . $ifCode . substr($code, $end);
}
// Gestion des conditions
$if = '<if ' . $param . ' notrim>';
while (false !== ($begin = strpos($code, $if))) {
$endIf = '<endif ' . $param . '>';
$end = strpos($code, $endIf, $begin);
if ($value) {
$ifCode = substr($code, $begin + strlen($if), $end - $begin - strlen($if));
} else {
$ifCode = '';
}
$end += strlen($endIf);
if (isset($code[$begin - 1]) && isset($code[$end])) {
$b = trim($code[$begin - 1]);
$e = trim($code[$end]);
// ATTENTION : il n'y pas de support des retours chariot Windows (\n\r) car ils font 2 caractères ! ! !
if ($b === '' && $e === '') $end++; // pour éviter les retours chariot ou espaces inutiles
}
$code = substr($code, 0, $begin) . $ifCode . substr($code, $end); }
// restitution
return $code;
}
/**
* @param array $params
*
* @return $this
*/
protected function echo(array $params): self
{
$expanded = $params['expanded'] ?? true;
$filename = $params['filename'] ?? 'Code source (fichier sans nom)';
$code = $params['code'] ?? null;
$write = $params['write'] ?? true;
$id = uniqid('bloc_code_');
?>
<a role="button" data-toggle="collapse" href="#<?= $id ?>" aria-expanded="<?= $expanded ? 'true' : 'false' ?>" aria-controls="collapseExample">
<?= $filename ?>
</a>
<?php if (!$write): ?>
<span class="label label-warning">A TRAITER MANUELLEMENT</span>
<?php endif; ?>
<br/>
<div class="collapse<?= $expanded ? ' in' : '' ?>" id="<?= $id ?>">
<?php Util::highlight($code, 'php', true, ['show-line-numbers' => true]); ?>
</div>
<?php
return $this;
}
/**
* @param array $params
*
* @return $this
*/
protected function write(array $params): self
{
$write = $params['write'] ?? true;
$filename = $params['filename'] ?? null;
$code = $params['code'] ?? null;
if (!$filename){
return $this;
}
$outputDir = $params['outputDir'] ?? getcwd();
$filename = $outputDir . '/' . $filename;
if (!$write){
echo 'A TRAITER MANUELLEMENT : '.$filename."\n";
return $this;
}
$parts = explode('/', substr($filename, 1));
array_pop($parts);
$dir = '';
foreach ($parts as $part) {
if (!is_dir(($dir .= "/$part"))) {
mkdir($dir);
chmod($dir, 0777); }
}
file_put_contents($filename, $code);
echo 'FICHIER ECRIT : '.$filename."\n";
chmod($filename, 0777);
return $this;
}
}