/** * Generator inner branches loop handler. * * @param Branch $branch Branch for looping its inner branches * @param bool $conditionStarted Return variable showing if inner branching has been started * @param string $pathValue Current routing logic $path variable name */ protected function generatorBranchesLoop(Branch $branch, &$conditionStarted, $pathValue) { // First stage - open condition // If we have started condition branching but this branch has parameters if ($conditionStarted && $branch->isParametrized()) { $this->generator->endIfCondition()->defIfCondition($branch->toLogicConditionCode($pathValue)); } elseif (!$conditionStarted) { // This is first inner branch // Start new condition $this->generator->defIfCondition($branch->toLogicConditionCode($pathValue)); // Set flag that condition has started $conditionStarted = true; } else { // This is regular branching $this->generator->defElseIfCondition($branch->toLogicConditionCode($pathValue)); } // Second stage receive parameters if ($branch->isParametrized()) { // Store parameter value received from condition $this->generator->newLine($branch->storeMatchedParameter()); } /** * Optimization to remove nested string operations - we create temporary $path variables */ $pathVariable = '$path' . mt_rand(0, 99999); // Do not output new $path variable creation if this is logic end if (count($branch->branches)) { $this->generator->newLine($pathVariable . ' = ' . $branch->removeMatchedPathCode($pathValue) . ';'); } // We should subtract part of $path var to remove this parameter // Go deeper in recursion $this->innerGenerate2($branch, $pathVariable, false); }
/** * Generate routing conditions logic. * * @param Branch $parent Current branch in resursion * @param string $pathValue Current $path value in routing logic * @param bool $conditionStarted Flag that condition started */ protected function innerGenerate2(Branch $parent, $pathValue = '$path', $conditionStarted = false) { // Iterate inner branches foreach ($parent->branches as $branch) { // First stage - open condition // If we have started condition branching but this branch has parameters if ($conditionStarted && $branch->isParametrized()) { $this->generator->endIfCondition()->defIfCondition($branch->toLogicConditionCode($pathValue)); } elseif (!$conditionStarted) { // This is first inner branch // Start new condition $this->generator->defIfCondition($branch->toLogicConditionCode($pathValue)); // Set flag that condition has started $conditionStarted = true; } else { // This is regular branching $this->generator->defElseIfCondition($branch->toLogicConditionCode($pathValue)); } // Second stage receive parameters if ($branch->isParametrized()) { // Store parameter value received from condition $this->generator->newLine($branch->storeMatchedParameter()); } /** * Optimization to remove nested string operations - we create temporary $path variables */ $pathVariable = '$path' . rand(0, 99999); // Do not output new $path variable creation if this is logic end if (sizeof($branch->branches)) { $this->generator->newLine($pathVariable . ' = ' . $branch->removeMatchedPathCode($pathValue) . ';'); } // We should subtract part of $path var to remove this parameter // Go deeper in recursion $this->innerGenerate2($branch, $pathVariable, false); } // Return route if branch has it if ($parent->hasRoute()) { // If we had other inner branch for this parent branch - we need to add else if (sizeof($parent->branches)) { $this->generator->defElseCondition(); } $this->generator->newLine($parent->returnRouteCode()); } // Close first condition if ($conditionStarted) { $this->generator->endIfCondition(); } }
/** * Build resolving method injection declaration. * * @param array $dependencies Collection of method dependencies * @param string $methodName Method name * @param string $containerVariable Container declaration variable name * @param string $reflectionVariable Reflection class variable name * @param bool $isPublic Flag if method is public */ protected function buildResolverMethodDeclaration(array $dependencies, string $methodName, string $containerVariable, string $reflectionVariable, bool $isPublic) { // Get method arguments $argumentsCount = count($dependencies); $this->generator->comment('Invoke ' . $methodName . '() and pass dependencies(y)'); if ($isPublic) { $this->generator->newLine($containerVariable . '->' . $methodName . '(')->increaseIndentation(); } else { $this->generator->newLine('$method = ' . $reflectionVariable . '->getMethod(\'' . $methodName . '\');')->newLine('$method->setAccessible(true);')->newLine('$method->invoke(')->increaseIndentation()->newLine($containerVariable . ','); } $i = 0; // Iterate method arguments foreach ($dependencies as $argument => $dependency) { // Add dependencies $this->buildResolverArgument($dependency); // Add comma if this is not last dependency if (++$i < $argumentsCount) { $this->generator->text(','); } } $this->generator->decreaseIndentation()->newLine(');'); }
/** * Create View class ancestor. * * @param Metadata $metadata View file metadata * @param string $path Entry path for generated classes and folders * @param null|callable $viewHandler View code handler */ protected function generateViewClass(Metadata $metadata, $path, $viewHandler = null) { $metadataParentClass = eval('return ' . $metadata->parentClass . ';'); // Read view file $viewCode = trim(file_get_contents($metadata->path)); // If we have external handler - pass view code to it for conversion if (is_callable($viewHandler)) { $viewCode = call_user_func($viewHandler, $viewCode); } // Convert to string for defining $viewCode = '<<<\'EOT\'' . "\n" . $viewCode . "\n" . 'EOT'; $parentClass = !isset($metadata->parentClass) ? $this->parentViewClass : $metadataParentClass; $this->generator->defNamespace($metadata->namespace)->multiComment(array('Class for view "' . $metadata->path . '" rendering'))->defClass($metadata->className, '\\' . $parentClass)->commentVar('string', 'Path to view file')->defClassVar('$file', 'protected', $metadata->path)->commentVar('string', 'Parent block name')->defClassVar('$parentBlock', 'protected', $metadata->parentBlock)->commentVar('array', 'Blocks list')->defClassVar('$blocks', 'protected', $metadata->blocks)->commentVar('string', 'View source code')->defClassVar('$source', 'protected', $viewCode); //->commentVar('array', 'Collection of view variables') //->defClassVar('$variables', 'public static', array_keys($metadata->variables)) //->commentVar('array', 'Collection of view variable types') //->defClassVar('$types', 'public static', $metadata->types) // Iterate all view variables foreach (array_keys($metadata->variables) as $name) { $type = array_key_exists($name, $metadata->types) ? $metadata->types[$name] : 'mixed'; $static = array_key_exists($name, $metadata->static) ? ' static' : ''; $this->generator->commentVar($type, 'View variable')->defClassVar('$' . $name, 'public' . $static); // Do not generate setters for static variables if ($static !== ' static') { $this->generator->text($this->generateViewVariableSetter($name, $metadata->originalVariables[$name], $type)); } } // Iterate namespace and create folder structure $path .= '/' . str_replace('\\', '/', $metadata->namespace); if (!is_dir($path)) { mkdir($path, 0777, true); } $newClassFile = $path . '/' . $metadata->className . '.php'; file_put_contents($newClassFile, '<?php' . $this->generator->endClass()->flush()); // Store path to generated class $metadata->generatedPath = $newClassFile; // Make generated cache files accessible chmod($newClassFile, 0777); }
/** Generate database table relations */ public function relations($cachePath = '') { // Generate unique file name $relations_file = $cachePath . '/relations/' . md5(serialize(TableRelation::$instances)) . '.php'; // Relations file does not exists - create it if (!file_exists($relations_file)) { // Get directory path $dir = pathname($relations_file); // Create folder if (!file_exists($dir)) { mkdir($dir, 0777, TRUE); } else { File::clear($dir); } // Processed permanent table relations $db_relations = array(); // Iterate permanent relations foreach (TableRelation::$instances as $row) { // Create relations data for specific table if (!isset($db_relations[$row->parent])) { $db_relations[$row->parent] = array(); } // Define child relation table name $child_relation = !isset($row->alias[0]) ? $row->child : $row->alias; $row->parent = self::$prefix . $row->parent; $row->child = self::$prefix . $row->child; // Save relation data $db_relations[$row->parent][$child_relation] = $row; } // TODO: Pass generator and others via constructor DI // Create code generator instance $g = new Generator('samson\\activerecord'); $g->multicomment(array('Static ActiveRecord generated table relations')); // Array of "FROM" sql statements for related tables $sql_from = array(); // Array of "SELECT" sql statements for related tables $sql_select = array(); // Array related tables columns names and aliases $relations = array(); // Array of table aliases $aliases = array(); // Array of table relation type $types = array(); // Array of columns map $map = array(); // Iterate grouped relations foreach ($db_relations as $parent => $relation) { // Check if parent class in relation exists if (class_exists(__NAMESPACE__ . '\\' . $parent, false)) { // Iterate table permanent relations foreach ($relation as $r_table => $i) { // Array of "SELECT" sql statements for this related tables $_sql_select = array(); // Array related tables columns names and aliases for this related tables $_relations = array(); // Parent table name $r_table_name = isset($i->alias[0]) ? $i->alias : $i->child; // Define start of join sql statement $_sql_from = 'LEFT JOIN `' . $i->child . '`'; // If relation alias is defined if (isset($i->alias[0])) { $_sql_from = 'LEFT JOIN `' . $i->child . '` AS ' . $i->alias; } // Parent table name $ptable = $i->parent; // If parent field not specified - use parent table primary field if (!isset($i->parent_field)) { $pfield = $primary_field; } else { // Define if parent field name has table name in it $tableinpf = strpos($i->parent_field, '.'); // Get parent table field name $pfield = $tableinpf !== false ? substr($i->parent_field, $tableinpf + 1) : $i->parent_field; // Parent table field $ptable = $tableinpf !== false ? dbMySQLConnector::$prefix . substr($i->parent_field, 0, $tableinpf) : $i->parent; } // If no "." symbol in parent field name append parent table name $pf = '`' . $ptable . '`.`' . $pfield . '`'; // If child field not specified if (!isset($i->child_field[0])) { $cf = '`' . (isset($i->alias[0]) ? $i->alias : $i->child) . '`.`' . $pfield . '`'; } else { $cf = strpos($i->child_field, '.') === false ? '`' . (isset($i->alias[0]) ? $i->alias : $i->child) . '`.' . $i->child_field : $i->child_field; } // Build columns metadata $this->__build_columns_map($i->child, $_sql_select, $map, $_relations, $i->alias); // Array of "SELECT" sql statements to all related tables $sql_select[$r_table_name] = implode(',', $_sql_select); $relations[$r_table_name] = $_relations; $sql_from[$r_table_name] = $_sql_from . ' ON ' . $pf . ' = ' . $cf; $aliases[$r_table_name] = $i->child; $types[$r_table_name] = $i->type; } // Remove prefix $class_name = str_replace(self::$prefix, '', $parent); // Generate code for this table $g->newline()->comment('Relation data for table "' . $parent . '"')->defarraymerge($class_name . '::$_sql_from', $sql_from)->defarraymerge($class_name . '::$_sql_select', $sql_select)->defarraymerge($class_name . '::$_map', $map)->defvar($class_name . '::$_relation_alias', $aliases)->defvar($class_name . '::$_relation_type', $types)->defvar($class_name . '::$_relations', $relations); } } // Save file to wwwrot $g->write($relations_file); // Evaluate relations code eval($g->code); } else { include $relations_file; } }