/**
  * Loads the schema into memory according to the config (e.g. only includes
  * databases and tables the config allows).
  * 
  * This function is mostly the first pass in schema generator. A good second
  * pass would be to attempt to link any relationships not possible through
  * FK detection. See {@link linkRelationships()}.
  *
  * @return void
  **/
 public function loadSchema()
 {
     $dsn = $this->config->getDsn();
     // Load the driver-specific classes
     $driver = ucfirst(strtolower($dsn['driver']));
     $this->loadDrivers(dirname(__FILE__) . '/drivers/mysql/', $driver);
     // Construct the server/schema class and start loading databases according to the configuration options.
     $serverClass = $driver . 'Server';
     $server = new $serverClass($dsn);
     $dbNames = $server->getAvailableDatabaseNames();
     foreach ($dbNames as $dbName) {
         if ($this->config->shouldProcessDatabase($dbName)) {
             if ($this->verbose) {
                 echo 'Scanning database `' . $dbName . "`\n";
             }
             $database = $server->loadDatabase($dbName);
             foreach ($database->getAvailableTableNames() as $tableName) {
                 if ($this->config->shouldProcessTable($dbName, $tableName)) {
                     if ($this->verbose) {
                         echo "\tScanning table `" . $tableName . "`\n";
                     }
                     $table = $database->loadTable($tableName);
                     $pkOverride = $this->config->getPrimaryKeyOverride($dbName, $tableName);
                     if (!empty($pkOverride)) {
                         // Config could be an array or single string, so convert it as needed:
                         if (!is_array($pkOverride)) {
                             $pkOverride = array($pkOverride);
                         }
                         // Remove any existing PK
                         foreach ($table->getPrimaryKey() as $column) {
                             $column->setIsPrimaryKey(false);
                         }
                         // Now set the PK override
                         foreach ($pkOverride as $columnName) {
                             $column = $table->getColumn($columnName);
                             $column->setIsPrimaryKey(true);
                         }
                     }
                 } else {
                     if ($this->verbose) {
                         echo "\tSkipping table `" . $tableName . "`\n";
                     }
                 }
             }
         } else {
             if ($this->verbose) {
                 echo 'Skipping database `' . $dbName . "`\n";
             }
         }
     }
     $this->setSchema($server);
 }
Example #2
0
 /**
  * Writes all the classes to disk, skipping starter classes if they already exist.
  *
  * @return boolean - true if no errors, false if not (use {@link getErrorMessages()})
  * @author Anthony Bush
  **/
 public function writeClasses($classes)
 {
     $this->errorMessages = array();
     // Write all classes to disk, but only write non-starter classes or the starter classes that haven't been written already.
     foreach ($classes as $class) {
         $fileName = $this->config->getClassFileName($class);
         if (!$class->isStarterClass() || !file_exists($fileName)) {
             $this->writeToFile($fileName, $class->getContents());
         }
     }
     // Return error status
     if (!empty($this->errorMessages)) {
         return false;
     } else {
         return true;
     }
 }
 /**
  * Scans the specified database for the specified table name (using the
  * match_table_name_prefixes config setting to scan for more table names
  * in the event the given one is not found.)
  * 
  * This is support method for {@link findTable()}.
  *
  * @return mixed - SchemaTable if found, null if not.
  * @author Anthony Bush
  **/
 protected function findTableInDatabase($tableNameMatch, SchemaDatabase $database)
 {
     $tableNamePrefixes = $this->config->getTableNamePrefixes($database);
     $tableNames = array($tableNameMatch);
     if (!is_null($tableNamePrefixes)) {
         foreach ($tableNamePrefixes as $prefix) {
             $tableNames[] = $prefix . $tableNameMatch;
         }
     }
     $table = null;
     foreach ($tableNames as $tableName) {
         $table = $database->getTable($tableName);
         if (!is_null($table)) {
             break;
         }
     }
     return $table;
 }
Example #4
0
    /**
     * Generates the starter collection class
     *
     * @return string the generated PHP code
     * @author Anthony Bush
     **/
    protected function generateStarterCollection($table)
    {
        $dbName = $table->getDatabase()->getDatabaseName();
        $tableName = $table->getTableName();
        $starterObjectClassName = $this->config->getStarterObjectClassName($table);
        $starterCollectionClassName = $this->config->getStarterCollectionClassName($table);
        $baseObjectClassName = $this->config->getBaseObjectClassName($table);
        $baseCollectionClassName = $this->config->getBaseCollectionClassName($table);
        $phpdocTags = $this->generatePhpdocTags($table);
        $phpdocTags[] = '@see ' . $baseCollectionClassName . ', CoughCollection';
        ob_start();
        echo "<?php\n\n";
        ?>
/**
* This is the starter class for <?php 
        echo $baseCollectionClassName;
        ?>
.
 *
 * <?php 
        echo implode("\n * ", $phpdocTags) . "\n";
        ?>
 **/
class <?php 
        echo $starterCollectionClassName;
        ?>
 extends <?php 
        echo $baseCollectionClassName;
        ?>
 {
}
<?php 
        echo "\n?>";
        // Add the class
        $class = new CoughClass();
        $class->setContents(ob_get_clean());
        $class->setIsStarterClass(true);
        $class->setIsCollectionClass(true);
        $class->setClassName($starterCollectionClassName);
        $class->setDatabaseName($dbName);
        $class->setTableName($tableName);
        $this->addGeneratedClass($class);
    }
 public function testConfigs()
 {
     $configPaths = glob(dirname(__FILE__) . '/test_configs/test_config*/');
     foreach ($configPaths as $configPath) {
         // 1. Setup DB
         $this->executeSqlFile($configPath . 'db_setup/db_setup.sql');
         // 2. Generate
         // Which config to use?
         $schemaGeneratorConfigFile = $configPath . 'generator_config/database_schema_generator.inc.php';
         $coughGeneratorConfigFile = $configPath . 'generator_config/cough_generator.inc.php';
         // Get the database config
         $schemaGeneratorConfig = DatabaseSchemaGeneratorConfig::constructFromFile($schemaGeneratorConfigFile);
         // Load the schema into memory
         $schemaGenerator = new DatabaseSchemaGenerator($schemaGeneratorConfig);
         $schema = $schemaGenerator->generateSchema();
         // Manipulate the schema (to add any missed relationships, e.g.)
         $manipulator = new SchemaManipulator($schemaGeneratorConfig);
         $manipulator->manipulateSchema($schema);
         // Get the cough generator config
         $outputDir = $configPath . 'output/';
         include $coughGeneratorConfigFile;
         $coughGeneratorConfig = new CoughGeneratorConfig($config);
         // Generate files into memory
         $coughGenerator = new CoughGenerator($coughGeneratorConfig);
         $classes = $coughGenerator->generateCoughClasses($schema);
         // Write files to disk
         $coughWriter = new CoughWriter($coughGeneratorConfig);
         $this->assertTrue($coughWriter->writeClasses($classes), 'Unable to write classes to disk.');
         // 3. Perform comparison
         $diffCommand = 'diff -r ' . escapeshellarg($configPath . 'expected_output') . ' ' . escapeshellarg($configPath . 'output');
         $diffOutput = shell_exec($diffCommand);
         $message = "Generated output does not match; diff files using:\n" . $diffCommand . "\n\n" . "DIFF OUTPUT:\n" . "==============================================\n" . $diffOutput . "\n" . "==============================================\n\n";
         if (!empty($diffOutput)) {
             $message .= "<: " . substr_count($diffOutput, "\n<") . "\n";
             $message .= ">: " . substr_count($diffOutput, "\n>") . "\n\n";
         }
         $this->assertTrue(empty($diffOutput), $message);
         // 4. Clean up files
         if (empty($diffOutput)) {
             $this->removeGeneratedClasses($configPath . 'output/');
         }
         // 5. Clean up DB
         $this->executeSqlFile($configPath . 'db_setup/db_teardown.sql');
         $this->dropAllTables();
     }
 }
 protected function showStatusFromConfigPath($configPath)
 {
     try {
         if (!file_exists($configPath)) {
             echo 'Config path does not exist: ' . $configPath . "\n";
             return;
         }
         if (!is_dir($configPath)) {
             echo 'Config path is not a directory: ' . $configPath . "\n";
             return;
         }
         // Which config to use?
         $schemaGeneratorConfigFile = $configPath . 'database_schema_generator.inc.php';
         $coughGeneratorConfigFile = $configPath . 'cough_generator.inc.php';
         // Get the database config
         $schemaGeneratorConfig = DatabaseSchemaGeneratorConfig::constructFromFile($schemaGeneratorConfigFile);
         // Load the schema into memory
         $schemaGenerator = new DatabaseSchemaGenerator($schemaGeneratorConfig);
         if ($this->verbose) {
             $schemaGenerator->enableVerbose();
         }
         $schema = $schemaGenerator->generateSchema();
         // Dump some verbose messages.
         // echo "\n";
         // $schema->outputRelationshipCounts();
         // echo "\n" . 'About to run the SchemaManipulator and re-output the relationships.' . "\n";
         // Manipulate the schema (to add any missed relationships, e.g.)
         $manipulator = new SchemaManipulator($schemaGeneratorConfig);
         if ($this->verbose) {
             $manipulator->enableVerbose();
         }
         $manipulator->manipulateSchema($schema);
         // Dump some verbose messages again to see if the manipulator added anything.
         // echo "\n";
         // $schema->outputRelationshipCounts();
         // Get the cough generator config
         $coughGeneratorConfig = CoughGeneratorConfig::constructFromFile($coughGeneratorConfigFile);
         // Generate files into memory
         $coughGenerator = new CoughGenerator($coughGeneratorConfig);
         $classes = $coughGenerator->generateCoughClasses($schema);
         // Add some spacing if verbose mode is on
         if ($this->verbose) {
             echo "\n";
         }
         // Show info...
         // Keep track of all the output file paths (we'll scan them for files that aren't being used)
         $filePaths = array();
         // Keep a map of full file name -> CoughClass object
         $generatedClasses = array();
         // Keep track of added, removed, and modified file information
         $addedFiles = array();
         $starterModifiedFiles = array();
         $generatedModifiedFiles = array();
         $removedFiles = array();
         $numFilesWithNoChange = 0;
         foreach ($classes as $class) {
             $filePaths[$coughGeneratorConfig->getClassFilePath($class)] = true;
             $fileName = $coughGeneratorConfig->getClassFileName($class);
             $generatedClasses[$fileName] = $class;
             // Go ahead and check if the file was added or modified
             if (!file_exists($fileName)) {
                 $addedFiles[] = $fileName;
             } else {
                 $currentFileContents = file_get_contents($fileName);
                 if ($currentFileContents == $class->getContents()) {
                     $numFilesWithNoChange++;
                 } else {
                     if ($class->isStarterClass()) {
                         $starterModifiedFiles[] = $fileName;
                     } else {
                         $generatedModifiedFiles[] = $fileName;
                     }
                 }
             }
         }
         // Check for removed files:
         foreach ($filePaths as $dir => $shouldScan) {
             if (file_exists($dir) && is_readable($dir)) {
                 $files = scandir($dir);
                 foreach ($files as $file) {
                     if (preg_match('|^[^.].*\\.class\\.php$|', $file)) {
                         if (!isset($generatedClasses[$dir . $file])) {
                             $removedFiles[] = $dir . $file;
                         }
                     }
                 }
             }
         }
         // Output Removed Files
         echo "\n";
         echo count($removedFiles) . ' Removed Files' . "\n";
         foreach ($removedFiles as $file) {
             echo $file . "\n";
         }
         // Output Added Files
         echo "\n";
         echo count($addedFiles) . ' Added Files' . "\n";
         foreach ($addedFiles as $file) {
             echo $file . "\n";
         }
         // Output Modified Files
         echo "\n";
         echo count($starterModifiedFiles) . ' Modified Files (which will not be modified since they are starter classes)' . "\n";
         foreach ($starterModifiedFiles as $file) {
             echo $file . "\n";
         }
         echo "\n";
         echo count($generatedModifiedFiles) . ' Modified Files (which will be overrwitten)' . "\n";
         foreach ($generatedModifiedFiles as $file) {
             echo $file . "\n";
         }
         echo "\n";
         echo $numFilesWithNoChange . ' files with no change.' . "\n";
         // Output stats
         $lineCount = 0;
         foreach ($classes as $class) {
             $lineCount += substr_count($class->getContents(), "\n");
         }
         echo "\n";
         echo 'Statistics of what will be generated:' . "\n";
         echo '-------------------------------------' . "\n";
         echo 'Number of classes: ' . count($classes) . "\n";
         echo 'Number of lines: ' . number_format($lineCount) . "\n";
         echo 'One-to-one relationships: ' . $schema->getNumberOfHasOneRelationships() . "\n";
         echo 'One-to-many relationships: ' . $schema->getNumberOfHasManyRelationships() . "\n";
         if ($this->verbose) {
             echo "\n" . 'PHP memory usage:' . "\n";
             echo number_format(memory_get_usage()) . " used\n";
             if (version_compare(phpversion(), '5.2.0', '>=')) {
                 echo number_format(memory_get_usage(true)) . " allocated\n";
             }
         }
     } catch (Exception $e) {
         echo $e->getMessage() . "\n";
     }
 }