khaihihi
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
\define('YoastSEO_Vendor\\SQL_UNKNOWN_QUERY_TYPE', 1);
|
||||
\define('YoastSEO_Vendor\\SQL_SELECT', 2);
|
||||
\define('YoastSEO_Vendor\\SQL_INSERT', 4);
|
||||
\define('YoastSEO_Vendor\\SQL_UPDATE', 8);
|
||||
\define('YoastSEO_Vendor\\SQL_DELETE', 16);
|
||||
\define('YoastSEO_Vendor\\SQL_ALTER', 32);
|
||||
\define('YoastSEO_Vendor\\SQL_DROP', 64);
|
||||
\define('YoastSEO_Vendor\\SQL_CREATE', 128);
|
||||
\define('YoastSEO_Vendor\\SQL_SHOW', 256);
|
||||
\define('YoastSEO_Vendor\\SQL_RENAME', 512);
|
||||
\define('YoastSEO_Vendor\\SQL_SET', 1024);
|
||||
/**
|
||||
* Ruckusing_Adapter_Base
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Adapter_Base
|
||||
{
|
||||
/**
|
||||
* dsn
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_dsn;
|
||||
/**
|
||||
* db
|
||||
*
|
||||
*/
|
||||
private $_db;
|
||||
/**
|
||||
* connection to db
|
||||
*
|
||||
* @var object|mysqli
|
||||
*/
|
||||
protected $_conn;
|
||||
/**
|
||||
* logger
|
||||
*
|
||||
* @var Ruckusing_Util_Logger
|
||||
*/
|
||||
public $logger;
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Adapter_Base
|
||||
*
|
||||
* @param array $dsn The current dsn
|
||||
*
|
||||
* @return Ruckusing_Adapter_Base
|
||||
*/
|
||||
public function __construct($dsn)
|
||||
{
|
||||
$this->set_dsn($dsn);
|
||||
}
|
||||
/**
|
||||
* Set a dsn
|
||||
*
|
||||
* @param object $dsn The current dsn
|
||||
*/
|
||||
public function set_dsn($dsn)
|
||||
{
|
||||
$this->_dsn = $dsn;
|
||||
}
|
||||
/**
|
||||
* Get the current dsn
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_dsn()
|
||||
{
|
||||
return $this->_dsn;
|
||||
}
|
||||
/**
|
||||
* Set a db
|
||||
*
|
||||
* @param array $db The current db
|
||||
*/
|
||||
public function set_db($db)
|
||||
{
|
||||
$this->_db = $db;
|
||||
}
|
||||
/**
|
||||
* Get the current db
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_db()
|
||||
{
|
||||
return $this->_db;
|
||||
}
|
||||
/**
|
||||
* Set a logger
|
||||
*
|
||||
* @param Ruckusing_Util_Logger $logger The current logger
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function set_logger($logger)
|
||||
{
|
||||
if (!$logger instanceof \YoastSEO_Vendor\Ruckusing_Util_Logger) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Logger parameter must be instance of Ruckusing_Util_Logger', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$this->logger = $logger;
|
||||
}
|
||||
/**
|
||||
* Get the current logger
|
||||
*
|
||||
* @param $logger
|
||||
* @return Ruckusing_Util_Logger
|
||||
*/
|
||||
public function get_logger($logger)
|
||||
{
|
||||
return $this->logger;
|
||||
}
|
||||
/**
|
||||
* Check table exists
|
||||
*
|
||||
* @param string $tbl the table name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_table($tbl)
|
||||
{
|
||||
return $this->table_exists($tbl);
|
||||
}
|
||||
/**
|
||||
* Allows to override hardcoded schema table name constant in case of parallel migrations.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_schema_version_table_name()
|
||||
{
|
||||
if (isset($this->_dsn['schema_version_table_name'])) {
|
||||
return $this->_dsn['schema_version_table_name'];
|
||||
}
|
||||
return \YoastSEO_Vendor\RUCKUSING_TS_SCHEMA_TBL_NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Adapter_ColumnDefinition
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Adapter_ColumnDefinition
|
||||
{
|
||||
/**
|
||||
* adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* type
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $type;
|
||||
/**
|
||||
* properties
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $properties;
|
||||
/**
|
||||
* options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_options = array();
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Adapter_ColumnDefinition
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter
|
||||
* @param string $name the name of the column
|
||||
* @param string $type the type of the column
|
||||
* @param array $options the column options
|
||||
*
|
||||
* @return Ruckusing_Adapter_ColumnDefinition
|
||||
*/
|
||||
public function __construct($adapter, $name, $type, $options = array())
|
||||
{
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Invalid Adapter instance.', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
if (empty($name) || !\is_string($name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid 'name' parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($type) || !\is_string($type)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid 'type' parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->_options = $options;
|
||||
}
|
||||
/**
|
||||
* sql version
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function to_sql()
|
||||
{
|
||||
$column_sql = \sprintf("%s %s", $this->_adapter->identifier($this->name), $this->sql_type());
|
||||
$column_sql .= $this->_adapter->add_column_options($this->type, $this->_options);
|
||||
return $column_sql;
|
||||
}
|
||||
/**
|
||||
* sql string version
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->to_sql();
|
||||
}
|
||||
/**
|
||||
* sql version
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function sql_type()
|
||||
{
|
||||
return $this->_adapter->type_to_sql($this->type, $this->_options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Adapter_Interface
|
||||
*
|
||||
* Interface of adapters
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
interface Ruckusing_Adapter_Interface
|
||||
{
|
||||
/**
|
||||
* get the current database name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_database_name();
|
||||
/**
|
||||
* Quote a raw string.
|
||||
*
|
||||
* @param string $value Raw string
|
||||
* @param string $column the column name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function quote($value, $column = null);
|
||||
/**
|
||||
* supports migrations ?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supports_migrations();
|
||||
/**
|
||||
* native database types
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function native_database_types();
|
||||
/**
|
||||
* schema
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function schema($output_file);
|
||||
/**
|
||||
* execute
|
||||
*
|
||||
* @param string $query Query SQL
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute($query);
|
||||
/**
|
||||
* Quote a raw string.
|
||||
*
|
||||
* @param string $str Raw string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function quote_string($str);
|
||||
//database level operations
|
||||
/**
|
||||
* database exists
|
||||
*
|
||||
* @param string $db The database name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function database_exists($db);
|
||||
/**
|
||||
* create table
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param array $options Options for definition table
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function create_table($table_name, $options = array());
|
||||
/**
|
||||
* drop database
|
||||
*
|
||||
* @param string $db The database name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function drop_database($db);
|
||||
//table level opertions
|
||||
/**
|
||||
* table exists ?
|
||||
*
|
||||
* @param string $tbl Table name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function table_exists($tbl);
|
||||
/**
|
||||
* drop table
|
||||
*
|
||||
* @param string $tbl The table name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function drop_table($tbl);
|
||||
/**
|
||||
* rename table
|
||||
*
|
||||
* @param string $name The old name of table
|
||||
* @param string $new_name The new name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function rename_table($name, $new_name);
|
||||
//column level operations
|
||||
/**
|
||||
* rename column
|
||||
*
|
||||
* @param string $table_name The table name where is the column
|
||||
* @param string $column_name The old column name
|
||||
* @param string $new_column_name The new column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function rename_column($table_name, $column_name, $new_column_name);
|
||||
/**
|
||||
* add column
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param string $column_name The column name
|
||||
* @param string $type The type generic of the column
|
||||
* @param array $options The options definition of the column
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_column($table_name, $column_name, $type, $options = array());
|
||||
/**
|
||||
* remove column
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param string $column_name The column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove_column($table_name, $column_name);
|
||||
/**
|
||||
* change column
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param string $column_name The column name
|
||||
* @param string $type The type generic of the column
|
||||
* @param array $options The options definition of the column
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function change_column($table_name, $column_name, $type, $options = array());
|
||||
/**
|
||||
* remove index
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param string $column_name The column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove_index($table_name, $column_name);
|
||||
/**
|
||||
* add index
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param string $column_name The column name
|
||||
* @param array $options The options definition of the index
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_index($table_name, $column_name, $options = array());
|
||||
/**
|
||||
* add timestamps
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param string $created_column_name Created at column name
|
||||
* @param string $updated_column_name Updated at column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_timestamps($table_name, $created_column_name, $updated_column_name);
|
||||
/**
|
||||
* remove timestamps
|
||||
*
|
||||
* @param string $table_name The table name
|
||||
* @param string $created_column_name Created at column name
|
||||
* @param string $updated_column_name Updated at column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove_timestamps($table_name, $created_column_name, $updated_column_name);
|
||||
/**
|
||||
* Wrapper to execute a query
|
||||
*
|
||||
* @param string $query query to run
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function query($query);
|
||||
/**
|
||||
* Wrapper to execute multiple queries
|
||||
*
|
||||
* @param string $queries queries to run
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function multi_query($queries);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage MySQL
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Adapter_MySQL_TableDefinition
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage MySQL
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Adapter_MySQL_TableDefinition
|
||||
{
|
||||
/**
|
||||
* adapter MySQL
|
||||
*
|
||||
* @var Ruckusing_Adapter_Mysql_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_name;
|
||||
/**
|
||||
* options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_options;
|
||||
/**
|
||||
* sql
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_sql = "";
|
||||
/**
|
||||
* initialized
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_initialized = \false;
|
||||
/**
|
||||
* Columns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_columns = array();
|
||||
/**
|
||||
* Table definition
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_table_def;
|
||||
/**
|
||||
* primary keys
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_primary_keys = array();
|
||||
/**
|
||||
* auto generate id
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_auto_generate_id = \true;
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Adapters_MySQL_Adapter
|
||||
*
|
||||
* @param Ruckusing_Adapter_MySQL_Base $adapter the current adapter
|
||||
* @param string $name the table name
|
||||
* @param array $options the options
|
||||
*
|
||||
* @throws Ruckusing_Exception
|
||||
* @return Ruckusing_Adapter_MySQL_TableDefinition
|
||||
*/
|
||||
public function __construct($adapter, $name, $options = array())
|
||||
{
|
||||
//sanity check
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_MySQL_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid MySQL Adapter instance.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
if (!$name) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid 'name' parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
$this->_name = $name;
|
||||
$this->_options = $options;
|
||||
$this->init_sql($name, $options);
|
||||
$this->_table_def = new \YoastSEO_Vendor\Ruckusing_Adapter_TableDefinition($this->_adapter, $this->_options);
|
||||
if (\array_key_exists('id', $options)) {
|
||||
if (\is_bool($options['id']) && $options['id'] == \false) {
|
||||
$this->_auto_generate_id = \false;
|
||||
}
|
||||
//if its a string then we want to auto-generate an integer-based
|
||||
//primary key with this name
|
||||
if (\is_string($options['id'])) {
|
||||
$this->_auto_generate_id = \true;
|
||||
$this->_primary_keys[] = $options['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
public function primary_key($name, $auto_increment)
|
||||
{
|
||||
$options = array('auto_increment' => $auto_increment);
|
||||
$this->column($name, "primary_key", $options);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Create a column
|
||||
*
|
||||
* @param string $column_name the column name
|
||||
* @param string $type the column type
|
||||
* @param array $options
|
||||
*/
|
||||
public function column($column_name, $type, $options = array())
|
||||
{
|
||||
//if there is already a column by the same name then silently fail
|
||||
//and continue
|
||||
if ($this->_table_def->included($column_name) == \true) {
|
||||
return;
|
||||
}
|
||||
$column_options = array();
|
||||
if (\array_key_exists('primary_key', $options)) {
|
||||
if ($options['primary_key'] == \true) {
|
||||
$this->_primary_keys[] = $column_name;
|
||||
}
|
||||
}
|
||||
if (\array_key_exists('auto_increment', $options)) {
|
||||
if ($options['auto_increment'] == \true) {
|
||||
$column_options['auto_increment'] = \true;
|
||||
}
|
||||
}
|
||||
$column_options = \array_merge($column_options, $options);
|
||||
$column = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this->_adapter, $column_name, $type, $column_options);
|
||||
$this->_columns[] = $column;
|
||||
}
|
||||
//column
|
||||
/**
|
||||
* Shortcut to create timestamps columns (default created_at, updated_at)
|
||||
*
|
||||
* @param string $created_column_name Created at column name
|
||||
* @param string $updated_column_name Updated at column name
|
||||
*
|
||||
*/
|
||||
public function timestamps($created_column_name = "created_at", $updated_column_name = "updated_at")
|
||||
{
|
||||
$this->column($created_column_name, "datetime");
|
||||
$this->column($updated_column_name, "timestamp", array("null" => \false, 'default' => 'CURRENT_TIMESTAMP', 'extra' => 'ON UPDATE CURRENT_TIMESTAMP'));
|
||||
}
|
||||
/**
|
||||
* Get all primary keys
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function keys()
|
||||
{
|
||||
if (\count($this->_primary_keys) > 0) {
|
||||
$lead = ' PRIMARY KEY (';
|
||||
$quoted = array();
|
||||
foreach ($this->_primary_keys as $key) {
|
||||
$quoted[] = \sprintf("%s", $this->_adapter->identifier($key));
|
||||
}
|
||||
$primary_key_sql = ",\n" . $lead . \implode(",", $quoted) . ")";
|
||||
return $primary_key_sql;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Table definition
|
||||
*
|
||||
* @param boolean $wants_sql
|
||||
*
|
||||
* @throws Ruckusing_Exception
|
||||
* @return boolean | string
|
||||
*/
|
||||
public function finish($wants_sql = \false)
|
||||
{
|
||||
if ($this->_initialized == \false) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Table Definition: '%s' has not been initialized", $this->_name), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_TABLE_DEFINITION);
|
||||
}
|
||||
$opt_str = '';
|
||||
if (\is_array($this->_options) && \array_key_exists('options', $this->_options)) {
|
||||
$opt_str = $this->_options['options'];
|
||||
} else {
|
||||
if (isset($this->_adapter->db_info['charset'])) {
|
||||
$opt_str = " DEFAULT CHARSET=" . $this->_adapter->db_info['charset'];
|
||||
} else {
|
||||
$opt_str = " DEFAULT CHARSET=utf8";
|
||||
}
|
||||
}
|
||||
$close_sql = \sprintf(") %s;", $opt_str);
|
||||
$create_table_sql = $this->_sql;
|
||||
if ($this->_auto_generate_id === \true) {
|
||||
$this->_primary_keys[] = 'id';
|
||||
$primary_id = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this->_adapter, 'id', 'integer', array('unsigned' => \true, 'null' => \false, 'auto_increment' => \true));
|
||||
$create_table_sql .= $primary_id->to_sql() . ",\n";
|
||||
}
|
||||
$create_table_sql .= $this->columns_to_str();
|
||||
$create_table_sql .= $this->keys() . $close_sql;
|
||||
if ($wants_sql) {
|
||||
return $create_table_sql;
|
||||
} else {
|
||||
return $this->_adapter->execute_ddl($create_table_sql);
|
||||
}
|
||||
}
|
||||
//finish
|
||||
/**
|
||||
* get all columns
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function columns_to_str()
|
||||
{
|
||||
$str = "";
|
||||
$fields = array();
|
||||
$len = \count($this->_columns);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$c = $this->_columns[$i];
|
||||
$fields[] = $c->__toString();
|
||||
}
|
||||
return \join(",\n", $fields);
|
||||
}
|
||||
/**
|
||||
* Init create sql
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $options
|
||||
* @throws Exception
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function init_sql($name, $options)
|
||||
{
|
||||
//are we forcing table creation? If so, drop it first
|
||||
if (\array_key_exists('force', $options) && $options['force'] == \true) {
|
||||
try {
|
||||
$this->_adapter->drop_table($name);
|
||||
} catch (\YoastSEO_Vendor\Ruckusing_Exception $e) {
|
||||
if ($e->getCode() != \YoastSEO_Vendor\Ruckusing_Exception::MISSING_TABLE) {
|
||||
throw $e;
|
||||
}
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
$temp = "";
|
||||
if (\array_key_exists('temporary', $options)) {
|
||||
$temp = " TEMPORARY";
|
||||
}
|
||||
$create_sql = \sprintf("CREATE%s TABLE ", $temp);
|
||||
$create_sql .= \sprintf("%s (\n", $this->_adapter->identifier($name));
|
||||
$this->_sql .= $create_sql;
|
||||
$this->_initialized = \true;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage PgSQL
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Adapter_PgSQL_TableDefinition
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage PgSQL
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Adapter_PgSQL_TableDefinition extends \YoastSEO_Vendor\Ruckusing_Adapter_TableDefinition
|
||||
{
|
||||
/**
|
||||
* adapter PgSQL
|
||||
*
|
||||
* @var Ruckusing_Adapter_Pgsql_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* Name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_name;
|
||||
/**
|
||||
* options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_options;
|
||||
/**
|
||||
* sql
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_sql = "";
|
||||
/**
|
||||
* initialized
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_initialized = \false;
|
||||
/**
|
||||
* Columns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_columns = array();
|
||||
/**
|
||||
* Table definition
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_table_def;
|
||||
/**
|
||||
* primary keys
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_primary_keys = array();
|
||||
/**
|
||||
* auto generate id
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_auto_generate_id = \true;
|
||||
/**
|
||||
* Creates an instance of Ruckusing_PostgresTableDefinition
|
||||
*
|
||||
* @param Ruckusing_Adapter_PgSQL_Base $adapter the current adapter
|
||||
* @param string $name the table name
|
||||
* @param array $options
|
||||
*
|
||||
* @throws Ruckusing_Exception
|
||||
* @return \Ruckusing_Adapter_PgSQL_TableDefinition
|
||||
*/
|
||||
public function __construct($adapter, $name, $options = array())
|
||||
{
|
||||
//sanity check
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_PgSQL_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid Postgres Adapter instance.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
if (!$name) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid 'name' parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
$this->_name = $name;
|
||||
$this->_options = $options;
|
||||
$this->init_sql($name, $options);
|
||||
$this->_table_def = new \YoastSEO_Vendor\Ruckusing_Adapter_TableDefinition($this->_adapter, $this->_options);
|
||||
if (\array_key_exists('id', $options)) {
|
||||
if (\is_bool($options['id']) && $options['id'] == \false) {
|
||||
$this->_auto_generate_id = \false;
|
||||
}
|
||||
//if its a string then we want to auto-generate an integer-based primary key with this name
|
||||
if (\is_string($options['id'])) {
|
||||
$this->_auto_generate_id = \true;
|
||||
$this->_primary_keys[] = $options['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a column
|
||||
*
|
||||
* @param string $column_name the column name
|
||||
* @param string $type the column type
|
||||
* @param array $options
|
||||
*/
|
||||
public function column($column_name, $type, $options = array())
|
||||
{
|
||||
//if there is already a column by the same name then silently fail and continue
|
||||
if ($this->_table_def->included($column_name) == \true) {
|
||||
return;
|
||||
}
|
||||
$column_options = array();
|
||||
if (\array_key_exists('primary_key', $options)) {
|
||||
if ($options['primary_key'] == \true) {
|
||||
$this->_primary_keys[] = $column_name;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (array_key_exists('auto_increment', $options)) {
|
||||
if ($options['auto_increment'] == true) {
|
||||
$column_options['auto_increment'] = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
$column_options = \array_merge($column_options, $options);
|
||||
$column = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this->_adapter, $column_name, $type, $column_options);
|
||||
$this->_columns[] = $column;
|
||||
}
|
||||
//column
|
||||
/**
|
||||
* Shortcut to create timestamps columns (default created_at, updated_at)
|
||||
*
|
||||
* @param string $created_column_name Created at column name
|
||||
* @param string $updated_column_name Updated at column name
|
||||
*
|
||||
*/
|
||||
public function timestamps($created_column_name = "created_at", $updated_column_name = "updated_at")
|
||||
{
|
||||
$this->column($created_column_name, "datetime", array("null" => \false));
|
||||
$this->column($updated_column_name, "datetime", array("null" => \false));
|
||||
}
|
||||
/**
|
||||
* Get all primary keys
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function keys()
|
||||
{
|
||||
if (\count($this->_primary_keys) > 0) {
|
||||
$lead = ' PRIMARY KEY (';
|
||||
$quoted = array();
|
||||
foreach ($this->_primary_keys as $key) {
|
||||
$quoted[] = \sprintf("%s", $this->_adapter->identifier($key));
|
||||
}
|
||||
$primary_key_sql = ",\n" . $lead . \implode(",", $quoted) . ")";
|
||||
return $primary_key_sql;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Table definition
|
||||
*
|
||||
* @param boolean $wants_sql
|
||||
*
|
||||
* @throws Ruckusing_Exception
|
||||
* @return boolean | string
|
||||
*/
|
||||
public function finish($wants_sql = \false)
|
||||
{
|
||||
if ($this->_initialized == \false) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Table Definition: '%s' has not been initialized", $this->_name), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_TABLE_DEFINITION);
|
||||
}
|
||||
if (\is_array($this->_options) && \array_key_exists('options', $this->_options)) {
|
||||
$opt_str = $this->_options['options'];
|
||||
} else {
|
||||
$opt_str = null;
|
||||
}
|
||||
$close_sql = \sprintf(") %s;", $opt_str);
|
||||
$create_table_sql = $this->_sql;
|
||||
if ($this->_auto_generate_id === \true) {
|
||||
$this->_primary_keys[] = 'id';
|
||||
$primary_id = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this->_adapter, 'id', 'primary_key');
|
||||
$create_table_sql .= $primary_id->to_sql() . ",\n";
|
||||
}
|
||||
$create_table_sql .= $this->columns_to_str();
|
||||
$create_table_sql .= $this->keys() . $close_sql;
|
||||
if ($wants_sql) {
|
||||
return $create_table_sql;
|
||||
} else {
|
||||
return $this->_adapter->execute_ddl($create_table_sql);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* get all columns
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function columns_to_str()
|
||||
{
|
||||
$str = "";
|
||||
$fields = array();
|
||||
$len = \count($this->_columns);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$c = $this->_columns[$i];
|
||||
$fields[] = $c->__toString();
|
||||
}
|
||||
return \join(",\n", $fields);
|
||||
}
|
||||
/**
|
||||
* Init create sql
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $options
|
||||
* @throws Exception
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function init_sql($name, $options)
|
||||
{
|
||||
//are we forcing table creation? If so, drop it first
|
||||
if (\array_key_exists('force', $options) && $options['force'] == \true) {
|
||||
try {
|
||||
$this->_adapter->drop_table($name);
|
||||
} catch (\YoastSEO_Vendor\Ruckusing_Exception $e) {
|
||||
if ($e->getCode() != \YoastSEO_Vendor\Ruckusing_Exception::MISSING_TABLE) {
|
||||
throw $e;
|
||||
}
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
$temp = "";
|
||||
$create_sql = \sprintf("CREATE%s TABLE ", $temp);
|
||||
$create_sql .= \sprintf("%s (\n", $this->_adapter->identifier($name));
|
||||
$this->_sql .= $create_sql;
|
||||
$this->_initialized = \true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,791 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage Sqlite3
|
||||
* @author Andrzej Oczkowicz <andrzejoczkowicz % gmail . com>
|
||||
* @author Piotr Olaszewski <piotroo89 % gmail dot com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
\define('YoastSEO_Vendor\\SQLITE3_MAX_IDENTIFIER_LENGTH', 64);
|
||||
/**
|
||||
* Ruckusing_Adapter_Sqlite3_Base
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage Sqlite3
|
||||
* @author Piotr Olaszewski <piotroo89 % gmail dot com>
|
||||
* @author Andrzej Oczkowicz <andrzejoczkowicz % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Adapter_Sqlite3_Base extends \YoastSEO_Vendor\Ruckusing_Adapter_Base implements \YoastSEO_Vendor\Ruckusing_Adapter_Interface
|
||||
{
|
||||
/**
|
||||
* @var SQLite3
|
||||
*/
|
||||
private $sqlite3;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $db_info;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $_in_transaction;
|
||||
/**
|
||||
* @param array $dsn
|
||||
* @param $logger
|
||||
*/
|
||||
public function __construct($dsn, $logger)
|
||||
{
|
||||
parent::__construct($dsn);
|
||||
$this->connect($dsn);
|
||||
$this->set_logger($logger);
|
||||
$this->_in_transaction = \false;
|
||||
}
|
||||
/**
|
||||
* @param $dsn
|
||||
*/
|
||||
private function connect($dsn)
|
||||
{
|
||||
$this->db_connect($dsn);
|
||||
}
|
||||
/**
|
||||
* @param $dsn
|
||||
* @return bool
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function db_connect($dsn)
|
||||
{
|
||||
if (!\class_exists('SQLite3')) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("\nIt appears you have not compiled PHP with SQLite3 support: missing class SQLite3", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
$db_info = $this->get_dsn();
|
||||
if ($db_info) {
|
||||
$this->db_info = $db_info;
|
||||
try {
|
||||
$this->sqlite3 = new \SQLite3($db_info['database']);
|
||||
} catch (\Exception $e) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Could not connect to the DB, check database name.\nReason: " . $e->getMessage(), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG, $e);
|
||||
}
|
||||
return \true;
|
||||
} else {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("\n\nCould not extract DB connection information from: {$dsn}\n\n", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function create_schema_version_table()
|
||||
{
|
||||
if (!$this->has_table($this->get_schema_version_table_name())) {
|
||||
$t = $this->create_table($this->get_schema_version_table_name(), array('id' => \false));
|
||||
$t->column('version', 'string');
|
||||
$t->finish();
|
||||
$this->add_index($this->get_schema_version_table_name(), 'version', array('unique' => \true));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_database_name()
|
||||
{
|
||||
return $this->db_info['database'];
|
||||
}
|
||||
/**
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
public function identifier($string)
|
||||
{
|
||||
return '"' . $string . '"';
|
||||
}
|
||||
/**
|
||||
* @param string $value
|
||||
* @param null $column
|
||||
* @return string
|
||||
*/
|
||||
public function quote($value, $column = null)
|
||||
{
|
||||
return "'{$value}'";
|
||||
}
|
||||
/**
|
||||
* @param string $query
|
||||
* @return array|bool|int
|
||||
*/
|
||||
public function query($query)
|
||||
{
|
||||
$this->logger->log($query);
|
||||
$query_type = $this->determine_query_type($query);
|
||||
$data = array();
|
||||
if ($query_type == \YoastSEO_Vendor\SQL_SELECT || $query_type == \YoastSEO_Vendor\SQL_SHOW) {
|
||||
$SqliteResult = $this->executeQuery($query);
|
||||
while ($row = $SqliteResult->fetchArray(\SQLITE3_ASSOC)) {
|
||||
$data[] = $row;
|
||||
}
|
||||
return $data;
|
||||
} else {
|
||||
$this->executeQuery($query);
|
||||
if ($query_type == \YoastSEO_Vendor\SQL_INSERT) {
|
||||
return $this->sqlite3->lastInsertRowID();
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Execute several queries
|
||||
*
|
||||
* @param string $queries queries to run
|
||||
*
|
||||
* @throws Ruckusing_Exception
|
||||
* @return boolean
|
||||
*/
|
||||
public function multi_query($queries)
|
||||
{
|
||||
$res = $this->sqlite3->exec($queries);
|
||||
if ($this->isError($res)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error executing 'query' with:\n%s\n\nReason: %s\n\n", $queries, $this->lastErrorMsg()), \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param $query
|
||||
* @return SQLite3Result
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function executeQuery($query)
|
||||
{
|
||||
$SqliteResult = $this->sqlite3->query($query);
|
||||
if ($this->isError($SqliteResult)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, $this->lastErrorMsg()), \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
|
||||
}
|
||||
return $SqliteResult;
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function start_transaction()
|
||||
{
|
||||
if ($this->inTransaction() === \false) {
|
||||
$this->beginTransaction();
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function commit_transaction()
|
||||
{
|
||||
if ($this->inTransaction()) {
|
||||
$this->commit();
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function rollback_transaction()
|
||||
{
|
||||
if ($this->inTransaction()) {
|
||||
$this->rollback();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param $query
|
||||
* @return int
|
||||
*/
|
||||
private function determine_query_type($query)
|
||||
{
|
||||
$query = \strtolower(\trim($query));
|
||||
$match = array();
|
||||
\preg_match('/^(\\w)*/i', $query, $match);
|
||||
$type = $match[0];
|
||||
switch ($type) {
|
||||
case 'select':
|
||||
return \YoastSEO_Vendor\SQL_SELECT;
|
||||
case 'update':
|
||||
return \YoastSEO_Vendor\SQL_UPDATE;
|
||||
case 'delete':
|
||||
return \YoastSEO_Vendor\SQL_DELETE;
|
||||
case 'insert':
|
||||
return \YoastSEO_Vendor\SQL_INSERT;
|
||||
case 'alter':
|
||||
return \YoastSEO_Vendor\SQL_ALTER;
|
||||
case 'drop':
|
||||
return \YoastSEO_Vendor\SQL_DROP;
|
||||
case 'create':
|
||||
return \YoastSEO_Vendor\SQL_CREATE;
|
||||
case 'pragma':
|
||||
return \YoastSEO_Vendor\SQL_SHOW;
|
||||
default:
|
||||
return \YoastSEO_Vendor\SQL_UNKNOWN_QUERY_TYPE;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_migrations()
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function native_database_types()
|
||||
{
|
||||
$types = array('primary_key' => array('name' => 'integer'), 'string' => array('name' => 'varchar', 'limit' => 255), 'text' => array('name' => 'text'), 'tinytext' => array('name' => 'text'), 'mediumtext' => array('name' => 'text'), 'integer' => array('name' => 'integer'), 'tinyinteger' => array('name' => 'smallint'), 'smallinteger' => array('name' => 'smallint'), 'mediuminteger' => array('name' => 'integer'), 'biginteger' => array('name' => 'bigint'), 'float' => array('name' => 'float'), 'decimal' => array('name' => 'decimal', 'scale' => 0, 'precision' => 10), 'datetime' => array('name' => 'datetime'), 'timestamp' => array('name' => 'datetime'), 'time' => array('name' => 'time'), 'date' => array('name' => 'date'), 'binary' => array('name' => 'blob'), 'tinybinary' => array('name' => "blob"), 'mediumbinary' => array('name' => "blob"), 'longbinary' => array('name' => "blob"), 'boolean' => array('name' => 'boolean'));
|
||||
return $types;
|
||||
}
|
||||
/**
|
||||
* @param $output_file
|
||||
* @return string
|
||||
*/
|
||||
public function schema($output_file)
|
||||
{
|
||||
$command = \sprintf("sqlite3 '%s' .schema > '%s'", $this->db_info['database'], $output_file);
|
||||
return \system($command);
|
||||
}
|
||||
/**
|
||||
* @param $db
|
||||
* @param array $options
|
||||
* @return bool
|
||||
*/
|
||||
public function create_database($db, $options = array())
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param string $query
|
||||
* @return array|bool|int|null
|
||||
*/
|
||||
public function execute($query)
|
||||
{
|
||||
return $this->query($query);
|
||||
}
|
||||
/**
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
public function quote_string($str)
|
||||
{
|
||||
return $this->sqlite3->escapeString($str);
|
||||
}
|
||||
/**
|
||||
* @param string $db
|
||||
* @return bool
|
||||
*/
|
||||
public function database_exists($db)
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @param array $options
|
||||
* @return Ruckusing_Adapter_Sqlite3_TableDefinition
|
||||
*/
|
||||
public function create_table($table_name, $options = array())
|
||||
{
|
||||
return new \YoastSEO_Vendor\Ruckusing_Adapter_Sqlite3_TableDefinition($this, $table_name, $options);
|
||||
}
|
||||
/**
|
||||
* @param string $databaseName
|
||||
* @return bool
|
||||
*/
|
||||
public function drop_database($databaseName)
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param $feature
|
||||
*/
|
||||
public function log_unsupported_feature($feature)
|
||||
{
|
||||
$this->logger->log(\sprintf("WARNING: Unsupported SQLite3 feature: %s", $feature));
|
||||
}
|
||||
/**
|
||||
* @param string $tbl
|
||||
* @param bool $reload_tables
|
||||
* @return bool
|
||||
*/
|
||||
public function table_exists($tbl, $reload_tables = \false)
|
||||
{
|
||||
$query = \sprintf("SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name=%s;", $this->quote_column_name($tbl));
|
||||
$table = $this->select_one($query);
|
||||
return \is_array($table) && \sizeof($table) > 0;
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @return bool
|
||||
*/
|
||||
public function drop_table($table_name)
|
||||
{
|
||||
$ddl = \sprintf("DROP TABLE IF EXISTS %s", $this->quote_table_name($table_name));
|
||||
$this->execute_ddl($ddl);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
public function quote_table_name($string)
|
||||
{
|
||||
return '"' . $string . '"';
|
||||
}
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $new_name
|
||||
* @return bool
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function rename_table($name, $new_name)
|
||||
{
|
||||
if (empty($name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing original column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($new_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing new column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$sql = \sprintf("ALTER TABLE %s RENAME TO %s", $this->identifier($name), $this->identifier($new_name));
|
||||
return $this->execute_ddl($sql);
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @param string $new_column_name
|
||||
* @return bool
|
||||
*/
|
||||
public function rename_column($table_name, $column_name, $new_column_name)
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
public function quote_column_name($string)
|
||||
{
|
||||
return '"' . $string . '"';
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @param string $type
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function add_column($table_name, $column_name, $type, $options = array())
|
||||
{
|
||||
if (empty($table_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($column_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($type)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing type parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$defaultOptions = array('limit' => null, 'precision' => null, 'scale' => null);
|
||||
$options = \array_merge($defaultOptions, $options);
|
||||
$sql = \sprintf("ALTER TABLE %s ADD COLUMN %s %s", $this->quote_table_name($table_name), $this->quote_column_name($column_name), $this->type_to_sql($type, $options));
|
||||
$sql .= $this->add_column_options($type, $options);
|
||||
return $this->execute_ddl($sql);
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
*/
|
||||
public function remove_column($table_name, $column_name)
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @param string $type
|
||||
* @param array $options
|
||||
*/
|
||||
public function change_column($table_name, $column_name, $type, $options = array())
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function remove_index($table_name, $column_name, $options = array())
|
||||
{
|
||||
if (empty($table_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($column_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
//did the user specify an index name?
|
||||
if (\is_array($options) && \array_key_exists('name', $options)) {
|
||||
$index_name = $options['name'];
|
||||
} else {
|
||||
$index_name = \YoastSEO_Vendor\Ruckusing_Util_Naming::index_name($table_name, $column_name);
|
||||
}
|
||||
$sql = \sprintf("DROP INDEX %s", $this->quote_column_name($index_name));
|
||||
return $this->execute_ddl($sql);
|
||||
}
|
||||
/**
|
||||
* @param string $table_name
|
||||
* @param string $column_name
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function add_index($table_name, $column_name, $options = array())
|
||||
{
|
||||
if (empty($table_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($column_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
//unique index?
|
||||
if (\is_array($options) && \array_key_exists('unique', $options) && $options['unique'] === \true) {
|
||||
$unique = \true;
|
||||
} else {
|
||||
$unique = \false;
|
||||
}
|
||||
//did the user specify an index name?
|
||||
if (\is_array($options) && \array_key_exists('name', $options)) {
|
||||
$index_name = $options['name'];
|
||||
} else {
|
||||
$index_name = \YoastSEO_Vendor\Ruckusing_Util_Naming::index_name($table_name, $column_name);
|
||||
}
|
||||
if (\strlen($index_name) > \YoastSEO_Vendor\SQLITE3_MAX_IDENTIFIER_LENGTH) {
|
||||
$msg = "The auto-generated index name is too long for Postgres (max is 64 chars). ";
|
||||
$msg .= "Considering using 'name' option parameter to specify a custom name for this index.";
|
||||
$msg .= " Note: you will also need to specify";
|
||||
$msg .= " this custom name in a drop_index() - if you have one.";
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception($msg, \YoastSEO_Vendor\Ruckusing_Exception::INVALID_INDEX_NAME);
|
||||
}
|
||||
if (!\is_array($column_name)) {
|
||||
$column_names = array($column_name);
|
||||
} else {
|
||||
$column_names = $column_name;
|
||||
}
|
||||
$cols = array();
|
||||
foreach ($column_names as $name) {
|
||||
$cols[] = $this->quote_column_name($name);
|
||||
}
|
||||
$sql = \sprintf("CREATE %sINDEX %s ON %s(%s)", $unique ? "UNIQUE " : "", $this->quote_column_name($index_name), $this->quote_column_name($table_name), \join(", ", $cols));
|
||||
return $this->execute_ddl($sql);
|
||||
}
|
||||
/**
|
||||
* @param $table_name
|
||||
* @param $created_column_name
|
||||
* @param $updated_column_name
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_timestamps($table_name, $created_column_name, $updated_column_name)
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
}
|
||||
/**
|
||||
* @param $table_name
|
||||
* @param $created_column_name
|
||||
* @param $updated_column_name
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove_timestamps($table_name, $created_column_name, $updated_column_name)
|
||||
{
|
||||
$this->log_unsupported_feature(__FUNCTION__);
|
||||
}
|
||||
/**
|
||||
* @param $type
|
||||
* @param $options
|
||||
* @param bool $performing_change
|
||||
* @return string
|
||||
*/
|
||||
public function add_column_options($type, $options, $performing_change = \false)
|
||||
{
|
||||
if (!\is_array($options)) {
|
||||
return '';
|
||||
}
|
||||
$sql = "";
|
||||
if (!$performing_change) {
|
||||
if (\array_key_exists('default', $options) && $options['default'] !== null) {
|
||||
if (\is_int($options['default'])) {
|
||||
$default_format = '%d';
|
||||
} elseif (\is_bool($options['default'])) {
|
||||
$default_format = "'%d'";
|
||||
} elseif ($options['default'] == 'CURRENT_TIMESTAMP') {
|
||||
$default_format = "%s";
|
||||
} else {
|
||||
$default_format = "'%s'";
|
||||
}
|
||||
$default_value = \sprintf($default_format, $options['default']);
|
||||
$sql .= \sprintf(" DEFAULT %s", $default_value);
|
||||
}
|
||||
if (\array_key_exists('null', $options) && $options['null'] === \false) {
|
||||
$sql .= " NOT NULL";
|
||||
}
|
||||
if (\array_key_exists('extra', $options)) {
|
||||
$sql .= \sprintf(" %s", $this->quote_string($options['extra']));
|
||||
}
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
/**
|
||||
* @param $type
|
||||
* @param array $options
|
||||
* @return string
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function type_to_sql($type, $options = array())
|
||||
{
|
||||
$natives = $this->native_database_types();
|
||||
if (!\array_key_exists($type, $natives)) {
|
||||
$error = \sprintf("Error: I don't know what column type of '%s' maps to for SQLite3.", $type);
|
||||
$error .= "\nYou provided: {$type}\n";
|
||||
$error .= "Valid types are: \n";
|
||||
$error .= \implode(', ', \array_diff(\array_keys($natives), array('primary_key')));
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception($error, \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$native_type = $natives[$type];
|
||||
$column_type_sql = $native_type['name'];
|
||||
$optionsLimit = isset($options['limit']) ? $options['limit'] : null;
|
||||
$nativeLimit = isset($native_type['limit']) ? $native_type['limit'] : null;
|
||||
$limit = $optionsLimit ? $optionsLimit : $nativeLimit;
|
||||
if ($limit !== null) {
|
||||
$column_type_sql .= \sprintf("(%d)", $limit);
|
||||
}
|
||||
return $column_type_sql;
|
||||
}
|
||||
/**
|
||||
* @param $table
|
||||
* @param $column
|
||||
* @return array|null
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function column_info($table, $column)
|
||||
{
|
||||
if (empty($table)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($column)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing original column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
try {
|
||||
$pragmaTable = $this->query('pragma table_info(' . $table . ')');
|
||||
$data = array();
|
||||
$pragmaColumnInfo = $this->extract_column_info($pragmaTable, $column);
|
||||
if (\is_array($pragmaColumnInfo)) {
|
||||
$data['type'] = $pragmaColumnInfo['type'];
|
||||
$data['name'] = $column;
|
||||
$data['field'] = $column;
|
||||
$data['null'] = $pragmaColumnInfo['notnull'] == 0;
|
||||
$data['default'] = $pragmaColumnInfo['dflt_value'];
|
||||
}
|
||||
return $data;
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param $pragmaTable
|
||||
* @param $columnName
|
||||
* @return null
|
||||
*/
|
||||
private function extract_column_info($pragmaTable, $columnName)
|
||||
{
|
||||
foreach ($pragmaTable as $columnInfo) {
|
||||
if ($columnInfo['name'] == $columnName) {
|
||||
return $columnInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @param $ddl
|
||||
* @return bool
|
||||
*/
|
||||
public function execute_ddl($ddl)
|
||||
{
|
||||
$this->query($ddl);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param $table_name
|
||||
* @return array
|
||||
*/
|
||||
public function indexes($table_name)
|
||||
{
|
||||
$sql = \sprintf("PRAGMA INDEX_LIST(%s);", $this->quote_table_name($table_name));
|
||||
$result = $this->select_all($sql);
|
||||
$indexes = array();
|
||||
foreach ($result as $row) {
|
||||
$indexes[] = array('name' => $row['name'], 'unique' => $row['unique'] ? \true : \false);
|
||||
}
|
||||
return $indexes;
|
||||
}
|
||||
/**
|
||||
* @param $SQLite3Result
|
||||
* @return bool
|
||||
*/
|
||||
private function isError($SQLite3Result)
|
||||
{
|
||||
return $SQLite3Result === \FALSE;
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function lastErrorMsg()
|
||||
{
|
||||
return $this->sqlite3->lastErrorMsg();
|
||||
}
|
||||
/**
|
||||
* @param $table
|
||||
* @return array
|
||||
*/
|
||||
public function primary_keys($table)
|
||||
{
|
||||
$result = $this->query('pragma table_info(' . $table . ')');
|
||||
$primary_keys = array();
|
||||
foreach ($result as $row) {
|
||||
if ($row['pk']) {
|
||||
$primary_keys[] = array('name' => $row['name'], 'type' => $row['type']);
|
||||
}
|
||||
}
|
||||
return $primary_keys;
|
||||
}
|
||||
/**
|
||||
* @param $query
|
||||
* @return array
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function select_one($query)
|
||||
{
|
||||
$this->logger->log($query);
|
||||
$query_type = $this->determine_query_type($query);
|
||||
if ($query_type == \YoastSEO_Vendor\SQL_SELECT || $query_type == \YoastSEO_Vendor\SQL_SHOW) {
|
||||
$res = $this->executeQuery($query);
|
||||
if ($this->isError($res)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, $this->lastErrorMsg()), \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
|
||||
}
|
||||
return $res->fetchArray(\SQLITE3_ASSOC);
|
||||
} else {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Query for select_one() is not one of SELECT or SHOW: {$query}", \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param $query
|
||||
* @return array|bool|int
|
||||
*/
|
||||
public function select_all($query)
|
||||
{
|
||||
return $this->query($query);
|
||||
}
|
||||
/**
|
||||
* @param $column_name
|
||||
* @param $type
|
||||
* @param null $options
|
||||
* @return string
|
||||
*/
|
||||
public function column_definition($column_name, $type, $options = null)
|
||||
{
|
||||
$col = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this, $column_name, $type, $options);
|
||||
return $col->__toString();
|
||||
}
|
||||
/**
|
||||
* @param $table_name
|
||||
* @param $column_name
|
||||
* @param array $options
|
||||
* @return bool
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function has_index($table_name, $column_name, $options = array())
|
||||
{
|
||||
if (empty($table_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (empty($column_name)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
if (\is_array($options) && \array_key_exists('name', $options)) {
|
||||
$index_name = $options['name'];
|
||||
} else {
|
||||
$index_name = \YoastSEO_Vendor\Ruckusing_Util_Naming::index_name($table_name, $column_name);
|
||||
}
|
||||
$indexes = $this->indexes($table_name);
|
||||
foreach ($indexes as $idx) {
|
||||
if ($idx['name'] == $index_name) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* @param $version
|
||||
* @return bool
|
||||
*/
|
||||
public function set_current_version($version)
|
||||
{
|
||||
$sql = \sprintf("INSERT INTO %s (version) VALUES ('%s')", $this->get_schema_version_table_name(), $version);
|
||||
return $this->execute_ddl($sql);
|
||||
}
|
||||
/**
|
||||
* @param $version
|
||||
* @return bool
|
||||
*/
|
||||
public function remove_version($version)
|
||||
{
|
||||
$sql = \sprintf("DELETE FROM %s WHERE version = '%s'", $this->get_schema_version_table_name(), $version);
|
||||
return $this->execute_ddl($sql);
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function inTransaction()
|
||||
{
|
||||
return $this->_in_transaction;
|
||||
}
|
||||
/**
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function beginTransaction()
|
||||
{
|
||||
if ($this->_in_transaction) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Transaction already started', \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
|
||||
}
|
||||
$this->execute_ddl("BEGIN");
|
||||
$this->_in_transaction = \true;
|
||||
}
|
||||
/**
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function commit()
|
||||
{
|
||||
if ($this->_in_transaction === \false) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Transaction not started', \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
|
||||
}
|
||||
$this->execute_ddl("COMMIT");
|
||||
$this->_in_transaction = \false;
|
||||
}
|
||||
/**
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function rollback()
|
||||
{
|
||||
if ($this->_in_transaction === \false) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Transaction not started', \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
|
||||
}
|
||||
$this->execute_ddl("ROLLBACK");
|
||||
$this->_in_transaction = \false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage Sqlite3
|
||||
* @author Piotr Olaszewski <piotroo89 % gmail dot com>
|
||||
* @author Andrzej Oczkowicz <andrzejoczkowicz % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Adapter_Sqlite3_TableDefinition
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @subpackage Sqlite3
|
||||
* @author Piotr Olaszewski <piotroo89 % gmail dot com>
|
||||
* @author Andrzej Oczkowicz <andrzejoczkowicz % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Adapter_Sqlite3_TableDefinition extends \YoastSEO_Vendor\Ruckusing_Adapter_TableDefinition
|
||||
{
|
||||
/**
|
||||
* @var Ruckusing_Adapter_Sqlite3_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
private $_name;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_options;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $_sql = "";
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $_initialized = \false;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_columns = array();
|
||||
/**
|
||||
* @var Ruckusing_Adapter_TableDefinition
|
||||
*/
|
||||
private $_table_def;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_primary_keys = array();
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $_auto_generate_id = \true;
|
||||
/**
|
||||
* @param Ruckusing_Adapter_Base $adapter
|
||||
* @param $name
|
||||
* @param array $options
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function __construct($adapter, $name, $options = array())
|
||||
{
|
||||
//sanity check
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_Sqlite3_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid Postgres Adapter instance.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
if (!$name) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Invalid 'name' parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
$this->_name = $name;
|
||||
$this->_options = $options;
|
||||
$this->init_sql($name, $options);
|
||||
$this->_table_def = new \YoastSEO_Vendor\Ruckusing_Adapter_TableDefinition($this->_adapter, $this->_options);
|
||||
if (\array_key_exists('id', $options)) {
|
||||
if (\is_bool($options['id']) && $options['id'] == \false) {
|
||||
$this->_auto_generate_id = \false;
|
||||
}
|
||||
//if its a string then we want to auto-generate an integer-based primary key with this name
|
||||
if (\is_string($options['id'])) {
|
||||
$this->_auto_generate_id = \true;
|
||||
$this->_primary_keys[] = $options['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param $name
|
||||
* @param $options
|
||||
* @throws Exception
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
private function init_sql($name, $options)
|
||||
{
|
||||
//are we forcing table creation? If so, drop it first
|
||||
if (\array_key_exists('force', $options) && $options['force']) {
|
||||
try {
|
||||
$this->_adapter->drop_table($name);
|
||||
} catch (\YoastSEO_Vendor\Ruckusing_Exception $e) {
|
||||
if ($e->getCode() != \YoastSEO_Vendor\Ruckusing_Exception::MISSING_TABLE) {
|
||||
throw $e;
|
||||
}
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
$temp = "";
|
||||
$create_sql = \sprintf("CREATE%s TABLE ", $temp);
|
||||
$create_sql .= \sprintf("%s (\n", $this->_adapter->identifier($name));
|
||||
$this->_sql .= $create_sql;
|
||||
$this->_initialized = \true;
|
||||
}
|
||||
/**
|
||||
* @param $column_name
|
||||
* @param $type
|
||||
* @param array $options
|
||||
*/
|
||||
public function column($column_name, $type, $options = array())
|
||||
{
|
||||
//if there is already a column by the same name then silently fail and continue
|
||||
if ($this->_table_def->included($column_name)) {
|
||||
return;
|
||||
}
|
||||
$column_options = array();
|
||||
if (\array_key_exists('primary_key', $options)) {
|
||||
if ($options['primary_key'] == \true) {
|
||||
$this->_primary_keys[] = $column_name;
|
||||
}
|
||||
}
|
||||
$column_options = \array_merge($column_options, $options);
|
||||
$column = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this->_adapter, $column_name, $type, $column_options);
|
||||
$this->_columns[] = $column;
|
||||
}
|
||||
/**
|
||||
* @param bool $wants_sql
|
||||
* @return bool|string
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function finish($wants_sql = \false)
|
||||
{
|
||||
if (!$this->_initialized) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Table Definition: '%s' has not been initialized", $this->_name), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_TABLE_DEFINITION);
|
||||
}
|
||||
if (\is_array($this->_options) && \array_key_exists('options', $this->_options)) {
|
||||
$opt_str = $this->_options['options'];
|
||||
} else {
|
||||
$opt_str = null;
|
||||
}
|
||||
$close_sql = \sprintf(") %s;", $opt_str);
|
||||
$create_table_sql = $this->_sql;
|
||||
if ($this->_auto_generate_id === \true) {
|
||||
$this->_primary_keys[] = 'id';
|
||||
$primary_id = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this->_adapter, 'id', 'primary_key');
|
||||
$create_table_sql .= $primary_id->to_sql() . ",\n";
|
||||
}
|
||||
$create_table_sql .= $this->columns_to_str();
|
||||
$create_table_sql .= $this->keys() . $close_sql;
|
||||
if ($wants_sql) {
|
||||
return $create_table_sql;
|
||||
} else {
|
||||
return $this->_adapter->execute_ddl($create_table_sql);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function columns_to_str()
|
||||
{
|
||||
$fields = array();
|
||||
$len = \count($this->_columns);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$c = $this->_columns[$i];
|
||||
$fields[] = $c->__toString();
|
||||
}
|
||||
return \join(",\n", $fields);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function timestamps($created_column_name = "created_at", $updated_column_name = "updated_at")
|
||||
{
|
||||
$this->column($created_column_name, "datetime", array("null" => \false, 'default' => 'CURRENT_TIMESTAMP'));
|
||||
$this->column($updated_column_name, "datetime", array("null" => \false, 'default' => 'CURRENT_TIMESTAMP'));
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function keys()
|
||||
{
|
||||
if (\count($this->_primary_keys) > 0) {
|
||||
$lead = ' PRIMARY KEY (';
|
||||
$quoted = array();
|
||||
foreach ($this->_primary_keys as $key) {
|
||||
$quoted[] = \sprintf("%s", $this->_adapter->identifier($key));
|
||||
}
|
||||
$primary_key_sql = ",\n" . $lead . \implode(",", $quoted) . ")";
|
||||
return $primary_key_sql;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Adapter_TableDefinition
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Adapter
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Adapter_TableDefinition
|
||||
{
|
||||
/**
|
||||
* columns
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_columns = array();
|
||||
/**
|
||||
* adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Adapter_TableDefinition
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter the current adapter
|
||||
*
|
||||
* @return Ruckusing_Adapter_TableDefinition
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Invalid Adapter instance.', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
}
|
||||
/**
|
||||
* __call
|
||||
*
|
||||
* @param string $name The method name
|
||||
* @param array $args The parameters of method called
|
||||
*
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function __call($name, $args)
|
||||
{
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Method unknown (' . $name . ')', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_MIGRATION_METHOD);
|
||||
}
|
||||
/**
|
||||
* Determine whether or not the given column already exists in our
|
||||
* table definition.
|
||||
*
|
||||
* This method is lax enough that it can take either a string column name
|
||||
* or a Ruckusing_Adapter_ColumnDefinition object.
|
||||
*
|
||||
* @param string $_column the name of the column
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function included($column)
|
||||
{
|
||||
$k = \count($this->_columns);
|
||||
for ($i = 0; $i < $k; $i++) {
|
||||
$col = $this->_columns[$i];
|
||||
if (\is_string($column) && $col->name == $column) {
|
||||
return \true;
|
||||
}
|
||||
if ($column instanceof \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition && $col->name == $column->name) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* Get list of columns
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function to_sql()
|
||||
{
|
||||
return \join(",", $this->_columns);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @author Salimane Adjao Moustapha <me@salimane.com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Exception
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @author Salimane Adjao Moustapha <me@salimane.com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Exception extends \Exception
|
||||
{
|
||||
const MISSING_SCHEMA_INFO_TABLE = 100;
|
||||
const INVALID_INDEX_NAME = 101;
|
||||
const MISSING_TABLE = 102;
|
||||
const INVALID_ADAPTER = 103;
|
||||
const INVALID_ARGUMENT = 104;
|
||||
const INVALID_TABLE_DEFINITION = 105;
|
||||
const INVALID_TASK = 106;
|
||||
const INVALID_LOG = 107;
|
||||
const INVALID_CONFIG = 108;
|
||||
const INVALID_TARGET_MIGRATION = 109;
|
||||
const INVALID_MIGRATION_DIR = 110;
|
||||
const INVALID_FRAMEWORK = 111;
|
||||
const QUERY_ERROR = 112;
|
||||
const INVALID_MIGRATION_METHOD = 113;
|
||||
const MIGRATION_FAILED = 114;
|
||||
const MIGRATION_NOT_SUPPORTED = 115;
|
||||
const INVALID_DB_DIR = 116;
|
||||
/**
|
||||
* Redefine the exception so message isn't optional
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $code[optional]
|
||||
* @param Exception $previous[optional]
|
||||
*
|
||||
* @return Ruckusing_Exception
|
||||
*/
|
||||
public function __construct($message, $code = 0, \Exception $previous = null)
|
||||
{
|
||||
// make sure everything is assigned properly
|
||||
if (\version_compare(\PHP_VERSION, '5.3.0', '>=')) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
} else {
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* custom string representation of object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return "\n" . \basename($this->file) . "({$this->line}) : {$this->message}\n";
|
||||
}
|
||||
/**
|
||||
* Custom error handler
|
||||
*
|
||||
* @param integer $code
|
||||
* @param string $message
|
||||
* @param string $file
|
||||
* @param integer $line
|
||||
*/
|
||||
public static function errorHandler($code, $message, $file, $line)
|
||||
{
|
||||
\file_put_contents('php://stderr', "\n" . \basename($file) . "({$line}) : {$message}\n\n");
|
||||
if ($code != \E_WARNING && $code != \E_NOTICE) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Custom exception handler
|
||||
*
|
||||
* @param Exception $exception
|
||||
*/
|
||||
public static function exceptionHandler($exception)
|
||||
{
|
||||
\file_put_contents('php://stderr', "\n" . \basename($exception->getFile()) . "({$exception->getLine()}) : {$exception->getMessage()}\n\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_FrameworkRunner
|
||||
*
|
||||
* Primary work-horse class. This class bootstraps the framework by loading
|
||||
* all adapters and tasks.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_FrameworkRunner
|
||||
{
|
||||
/**
|
||||
* reference to our DB connection
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_db = null;
|
||||
/**
|
||||
* The currently active config
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_active_db_config;
|
||||
/**
|
||||
* Available DB config (e.g. test,development, production)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_config = array();
|
||||
/**
|
||||
* Task manager
|
||||
*
|
||||
* @var Ruckusing_Task_Manager
|
||||
*/
|
||||
private $_task_mgr = null;
|
||||
/**
|
||||
* adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* current task name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_cur_task_name = "";
|
||||
/**
|
||||
* task options
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_task_options = "";
|
||||
/**
|
||||
* Environment
|
||||
* default is development
|
||||
* but can also be one 'test', 'production', etc...
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_env = "development";
|
||||
/**
|
||||
* set up some defaults
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_opt_map = array('env' => 'development');
|
||||
/**
|
||||
* Flag to display help of task
|
||||
* @see Ruckusing_FrameworkRunner::parse_args
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_showhelp = \false;
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Adapters_Base
|
||||
*
|
||||
* @param array $config The current config
|
||||
* @param array $argv the supplied command line arguments
|
||||
* @param Ruckusing_Util_Logger An optional custom logger
|
||||
*
|
||||
* @return Ruckusing_FrameworkRunner
|
||||
*/
|
||||
public function __construct($config, $argv, \YoastSEO_Vendor\Ruckusing_Util_Logger $log = null)
|
||||
{
|
||||
\set_error_handler(array('\YoastSEO_Vendor\Ruckusing_Exception', 'errorHandler'), \E_ALL);
|
||||
\set_exception_handler(array('\YoastSEO_Vendor\Ruckusing_Exception', 'exceptionHandler'));
|
||||
//parse arguments
|
||||
$this->parse_args($argv);
|
||||
//set config variables
|
||||
$this->_config = $config;
|
||||
//verify config array
|
||||
$this->verify_db_config();
|
||||
//initialize logger
|
||||
$this->logger = $log;
|
||||
$this->initialize_logger();
|
||||
//include all adapters
|
||||
$this->load_all_adapters(\YoastSEO_Vendor\RUCKUSING_BASE . \DIRECTORY_SEPARATOR . 'lib' . \DIRECTORY_SEPARATOR . 'Ruckusing' . \DIRECTORY_SEPARATOR . 'Adapter');
|
||||
//initialize logger
|
||||
$this->initialize_db();
|
||||
//initialize tasks
|
||||
$this->init_tasks();
|
||||
}
|
||||
/**
|
||||
* Execute the current task
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$output = '';
|
||||
if (empty($this->_cur_task_name)) {
|
||||
if (isset($_SERVER["argv"][1]) && \stripos($_SERVER["argv"][1], '=') === \false) {
|
||||
$output .= \sprintf("\n\tWrong Task format: %s\n", $_SERVER["argv"][1]);
|
||||
}
|
||||
$output .= $this->help();
|
||||
} else {
|
||||
if ($this->_task_mgr->has_task($this->_cur_task_name)) {
|
||||
if ($this->_showhelp) {
|
||||
$output .= $this->_task_mgr->help($this->_cur_task_name);
|
||||
} else {
|
||||
$output .= $this->_task_mgr->execute($this, $this->_cur_task_name, $this->_task_options);
|
||||
}
|
||||
} else {
|
||||
$output .= \sprintf("\n\tTask not found: %s\n", $this->_cur_task_name);
|
||||
$output .= $this->help();
|
||||
}
|
||||
}
|
||||
if ($this->logger) {
|
||||
$this->logger->close();
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* Get the current adapter
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_adapter()
|
||||
{
|
||||
return $this->_adapter;
|
||||
}
|
||||
/**
|
||||
* Initialize the task manager
|
||||
*/
|
||||
public function init_tasks()
|
||||
{
|
||||
$this->_task_mgr = new \YoastSEO_Vendor\Ruckusing_Task_Manager($this->_adapter, $this->_config);
|
||||
}
|
||||
/**
|
||||
* Get the current migration dir
|
||||
*
|
||||
* @param string $key the module key name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function migrations_directory($key = '')
|
||||
{
|
||||
$migration_dir = '';
|
||||
if ($key) {
|
||||
if (!isset($this->_config['migrations_dir'][$key])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("No module %s migration_dir set in config", $key), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
$migration_dir = $this->_config['migrations_dir'][$key] . \DIRECTORY_SEPARATOR;
|
||||
} elseif (\is_array($this->_config['migrations_dir'])) {
|
||||
$migration_dir = $this->_config['migrations_dir']['default'] . \DIRECTORY_SEPARATOR;
|
||||
} else {
|
||||
$migration_dir = $this->_config['migrations_dir'] . \DIRECTORY_SEPARATOR;
|
||||
}
|
||||
if (\array_key_exists('directory', $this->_config['db'][$this->_env])) {
|
||||
return $migration_dir . $this->_config['db'][$this->_env]['directory'];
|
||||
}
|
||||
return $migration_dir . $this->_config['db'][$this->_env]['database'];
|
||||
}
|
||||
/**
|
||||
* Get all migrations directory
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function migrations_directories()
|
||||
{
|
||||
$folder = $this->_config['db'][$this->_env]['database'];
|
||||
if (\array_key_exists('directory', $this->_config['db'][$this->_env])) {
|
||||
$folder = $this->_config['db'][$this->_env]['directory'];
|
||||
}
|
||||
$result = array();
|
||||
if (\is_array($this->_config['migrations_dir'])) {
|
||||
foreach ($this->_config['migrations_dir'] as $name => $path) {
|
||||
$result[$name] = $path . \DIRECTORY_SEPARATOR . $folder;
|
||||
}
|
||||
} else {
|
||||
$result['default'] = $this->_config['migrations_dir'] . \DIRECTORY_SEPARATOR . $folder;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* Get the current db schema dir
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function db_directory()
|
||||
{
|
||||
$path = $this->_config['db_dir'] . \DIRECTORY_SEPARATOR;
|
||||
if (\array_key_exists('directory', $this->_config['db'][$this->_env])) {
|
||||
return $path . $this->_config['db'][$this->_env]['directory'];
|
||||
}
|
||||
return $path . $this->_config['db'][$this->_env]['database'];
|
||||
}
|
||||
/**
|
||||
* Initialize the db
|
||||
*/
|
||||
public function initialize_db()
|
||||
{
|
||||
$db = $this->_config['db'][$this->_env];
|
||||
$adapter = $this->get_adapter_class($db['type']);
|
||||
if (empty($adapter)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("No adapter available for DB type: %s", $db['type']), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
//construct our adapter
|
||||
$this->_adapter = new $adapter($db, $this->logger);
|
||||
}
|
||||
/**
|
||||
* Initialize the logger
|
||||
*/
|
||||
public function initialize_logger()
|
||||
{
|
||||
if (!$this->logger) {
|
||||
if (\is_dir($this->_config['log_dir']) && !\is_writable($this->_config['log_dir'])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("\n\nCannot write to log directory: " . $this->_config['log_dir'] . "\n\nCheck permissions.\n\n", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_LOG);
|
||||
} elseif (!\is_dir($this->_config['log_dir'])) {
|
||||
//try and create the log directory
|
||||
\mkdir($this->_config['log_dir'], 0755, \true);
|
||||
}
|
||||
$log_name = \sprintf("%s.log", $this->_env);
|
||||
$this->logger = \YoastSEO_Vendor\Ruckusing_Util_Logger::instance($this->_config['log_dir'] . \DIRECTORY_SEPARATOR . $log_name);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* $argv is our complete command line argument set.
|
||||
* PHP gives us:
|
||||
* [0] = the actual file name we're executing
|
||||
* [1..N] = all other arguments
|
||||
*
|
||||
* Our task name should be at slot [1]
|
||||
* Anything else are additional parameters that we can pass
|
||||
* to our task and they can deal with them as they see fit.
|
||||
*
|
||||
* @param array $argv the current command line arguments
|
||||
*/
|
||||
private function parse_args($argv)
|
||||
{
|
||||
$num_args = \count($argv);
|
||||
$options = array();
|
||||
for ($i = 0; $i < $num_args; $i++) {
|
||||
$arg = $argv[$i];
|
||||
if (\stripos($arg, ':') !== \false) {
|
||||
$this->_cur_task_name = $arg;
|
||||
} elseif ($arg == 'help') {
|
||||
$this->_showhelp = \true;
|
||||
continue;
|
||||
} elseif (\stripos($arg, '=') !== \false) {
|
||||
list($key, $value) = \explode('=', $arg);
|
||||
$key = \strtolower($key);
|
||||
// Allow both upper and lower case parameters
|
||||
$options[$key] = $value;
|
||||
if ($key == 'env') {
|
||||
$this->_env = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->_task_options = $options;
|
||||
}
|
||||
/**
|
||||
* Update the local schema to handle multiple records versus the prior architecture
|
||||
* of storing a single version. In addition take all existing migration files
|
||||
* and register them in our new table, as they have already been executed.
|
||||
*/
|
||||
public function update_schema_for_timestamps()
|
||||
{
|
||||
//only create the table if it doesnt already exist
|
||||
$this->_adapter->create_schema_version_table();
|
||||
//insert all existing records into our new table
|
||||
$migrator_util = new \YoastSEO_Vendor\Ruckusing_Util_Migrator($this->_adapter);
|
||||
$files = $migrator_util->get_migration_files($this->migrations_directories(), 'up');
|
||||
foreach ($files as $file) {
|
||||
if ((int) $file['version'] >= \PHP_INT_MAX) {
|
||||
//its new style like '20081010170207' so its not a candidate
|
||||
continue;
|
||||
}
|
||||
//query old table, if it less than or equal to our max version, then its a candidate for insertion
|
||||
$query_sql = \sprintf("SELECT version FROM %s WHERE version >= %d", \YoastSEO_Vendor\RUCKUSING_SCHEMA_TBL_NAME, $file['version']);
|
||||
$existing_version_old_style = $this->_adapter->select_one($query_sql);
|
||||
if (\count($existing_version_old_style) > 0) {
|
||||
//make sure it doesnt exist in our new table, who knows how it got inserted?
|
||||
$new_vers_sql = \sprintf("SELECT version FROM %s WHERE version = %d", $this->_adapter->get_schema_version_table_name(), $file['version']);
|
||||
$existing_version_new_style = $this->_adapter->select_one($new_vers_sql);
|
||||
if (empty($existing_version_new_style)) {
|
||||
// use sprintf & %d to force it to be stripped of any leading zeros, we *know* this represents an old version style
|
||||
// so we dont have to worry about PHP and integer overflow
|
||||
$insert_sql = \sprintf("INSERT INTO %s (version) VALUES (%d)", $this->_adapter->get_schema_version_table_name(), $file['version']);
|
||||
$this->_adapter->query($insert_sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set an option
|
||||
*
|
||||
* @param string $key the key to set
|
||||
* @param string $value the value to set
|
||||
*/
|
||||
private function set_opt($key, $value)
|
||||
{
|
||||
if (!$key) {
|
||||
return;
|
||||
}
|
||||
$this->_opt_map[$key] = $value;
|
||||
}
|
||||
/**
|
||||
* Verify db config
|
||||
*/
|
||||
private function verify_db_config()
|
||||
{
|
||||
if (!\array_key_exists($this->_env, $this->_config['db'])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: '%s' DB is not configured", $this->_env), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
$env = $this->_env;
|
||||
$this->_active_db_config = $this->_config['db'][$this->_env];
|
||||
if (!\array_key_exists("type", $this->_active_db_config)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: 'type' is not set for '%s' DB", $this->_env), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (!\array_key_exists("host", $this->_active_db_config)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: 'host' is not set for '%s' DB", $this->_env), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (!\array_key_exists("database", $this->_active_db_config)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: 'database' is not set for '%s' DB", $this->_env), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (!\array_key_exists("user", $this->_active_db_config)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: 'user' is not set for '%s' DB", $this->_env), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (!\array_key_exists("password", $this->_active_db_config)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: 'password' is not set for '%s' DB", $this->_env), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (empty($this->_config['migrations_dir'])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error: 'migrations_dir' is not set in config.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (\is_array($this->_config['migrations_dir'])) {
|
||||
if (!isset($this->_config['migrations_dir']['default'])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error: 'migrations_dir' 'default' key is not set in config.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
} elseif (empty($this->_config['migrations_dir']['default'])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error: 'migrations_dir' 'default' key is empty in config.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
} else {
|
||||
$names = $paths = array();
|
||||
foreach ($this->_config['migrations_dir'] as $name => $path) {
|
||||
if (isset($names[$name])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error: 'migrations_dir' '{$name}' key is defined multiples times in config.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (isset($paths[$path])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error: 'migrations_dir' '{$paths[$path]}' and '{$name}' keys defined the same path in config.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
$names[$name] = $path;
|
||||
$paths[$path] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($this->_task_options['module']) && !isset($this->_config['migrations_dir'][$this->_task_options['module']])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: module name %s is not set in 'migrations_dir' option in config.", $this->_task_options['module']), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (empty($this->_config['db_dir'])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error: 'db_dir' is not set in config.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
if (empty($this->_config['log_dir'])) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error: 'log_dir' is not set in config.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get the adapter class
|
||||
*
|
||||
* @param string $db_type the database type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_adapter_class($db_type)
|
||||
{
|
||||
$adapter_class = null;
|
||||
switch ($db_type) {
|
||||
case 'mysql':
|
||||
$adapter_class = "\YoastSEO_Vendor\Ruckusing_Adapter_MySQL_Base";
|
||||
break;
|
||||
case 'pgsql':
|
||||
$adapter_class = "\YoastSEO_Vendor\Ruckusing_Adapter_PgSQL_Base";
|
||||
break;
|
||||
case 'sqlite':
|
||||
$adapter_class = "\YoastSEO_Vendor\Ruckusing_Adapter_Sqlite3_Base";
|
||||
break;
|
||||
}
|
||||
return $adapter_class;
|
||||
}
|
||||
/**
|
||||
* DB adapters are classes in lib/Ruckusing/Adapter
|
||||
* and they follow the file name syntax of "<DB Name>/Base.php".
|
||||
*
|
||||
* See the function "get_adapter_class" in this class for examples.
|
||||
*
|
||||
* @param string $adapter_dir the adapter dir
|
||||
*/
|
||||
private function load_all_adapters($adapter_dir)
|
||||
{
|
||||
if (!\is_dir($adapter_dir)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Adapter dir: %s does not exist", $adapter_dir), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
return \false;
|
||||
}
|
||||
$files = \scandir($adapter_dir);
|
||||
foreach ($files as $f) {
|
||||
//skip over invalid files
|
||||
if ($f == '.' || $f == ".." || !\is_dir($adapter_dir . \DIRECTORY_SEPARATOR . $f)) {
|
||||
continue;
|
||||
}
|
||||
$adapter_class_path = $adapter_dir . \DIRECTORY_SEPARATOR . $f . \DIRECTORY_SEPARATOR . 'Base.php';
|
||||
if (\file_exists($adapter_class_path)) {
|
||||
require_once $adapter_class_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help()
|
||||
{
|
||||
// TODO: dynamically list all available tasks
|
||||
$output = <<<USAGE
|
||||
|
||||
\tUsage: php {$_SERVER['argv'][0]} <task> [help] [task parameters] [env=environment]
|
||||
|
||||
\thelp: Display this message
|
||||
|
||||
\tenv: The env command line parameter can be used to specify a different
|
||||
\tdatabase to run against, as specific in the configuration file
|
||||
\t(config/database.inc.php).
|
||||
\tBy default, env is "development"
|
||||
|
||||
\ttask: In a nutshell, task names are pseudo-namespaced. The tasks that come
|
||||
\twith the framework are namespaced to "db" (e.g. the tasks are "db:migrate",
|
||||
\t"db:setup", etc).
|
||||
\tAll tasks available actually :
|
||||
|
||||
\t- db:setup : A basic task to initialize your DB for migrations is
|
||||
\tavailable. One should always run this task when first starting out.
|
||||
|
||||
\t- db:generate : A generic task which acts as a Generator for migrations.
|
||||
|
||||
\t- db:migrate : The primary purpose of the framework is to run migrations,
|
||||
\tand the execution of migrations is all handled by just a regular ol' task.
|
||||
|
||||
\t- db:version : It is always possible to ask the framework (really the DB)
|
||||
\twhat version it is currently at.
|
||||
|
||||
\t- db:status : With this taks you'll get an overview of the already
|
||||
\texecuted migrations and which will be executed when running db:migrate
|
||||
|
||||
\t- db:schema : It can be beneficial to get a dump of the DB in raw SQL
|
||||
\tformat which represents the current version.
|
||||
|
||||
USAGE;
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Migration
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Migration_Base
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Migration
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Migration_Base
|
||||
{
|
||||
/**
|
||||
* adapter
|
||||
*
|
||||
* @var \Ruckusing_Adapter_Base|\Ruckusing_Adapter_MySQL_Base|\Ruckusing_Adapter_PgSQL_Base|\Ruckusing_Adapter_Sqlite3_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter the current adapter
|
||||
*
|
||||
* @return \Ruckusing_Migration_Base
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
$this->set_adapter($adapter);
|
||||
}
|
||||
/**
|
||||
* __call
|
||||
*
|
||||
* @param string $name The method name
|
||||
* @param array $args The parameters of method called
|
||||
*
|
||||
* @throws Ruckusing_Exception
|
||||
*/
|
||||
public function __call($name, $args)
|
||||
{
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Method unknown (' . $name . ')', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_MIGRATION_METHOD);
|
||||
}
|
||||
/**
|
||||
* Set an adapter
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter the adapter to set
|
||||
* @throws Ruckusing_Exception
|
||||
* @return $this
|
||||
*/
|
||||
public function set_adapter($adapter)
|
||||
{
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Adapter must be implement Ruckusing_Adapter_Base!', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Get the current adapter
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_adapter()
|
||||
{
|
||||
return $this->_adapter;
|
||||
}
|
||||
/**
|
||||
* Create a database
|
||||
*
|
||||
* @param string $name the name of the database
|
||||
* @param array $options
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function create_database($name, $options = null)
|
||||
{
|
||||
return $this->_adapter->create_database($name, $options);
|
||||
}
|
||||
/**
|
||||
* Drop a database
|
||||
*
|
||||
* @param string $name the name of the database
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function drop_database($name)
|
||||
{
|
||||
return $this->_adapter->drop_database($name);
|
||||
}
|
||||
/**
|
||||
* Drop a table
|
||||
*
|
||||
* @param string $tbl the name of the table
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function drop_table($tbl)
|
||||
{
|
||||
return $this->_adapter->drop_table($tbl);
|
||||
}
|
||||
/**
|
||||
* Rename a table
|
||||
*
|
||||
* @param string $name the name of the table
|
||||
* @param string $new_name the new name of the table
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function rename_table($name, $new_name)
|
||||
{
|
||||
return $this->_adapter->rename_table($name, $new_name);
|
||||
}
|
||||
/**
|
||||
* Rename a column
|
||||
*
|
||||
* @param string $tbl_name the name of the table
|
||||
* @param string $column_name the column name
|
||||
* @param string $new_column_name the new column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function rename_column($tbl_name, $column_name, $new_column_name)
|
||||
{
|
||||
return $this->_adapter->rename_column($tbl_name, $column_name, $new_column_name);
|
||||
}
|
||||
/**
|
||||
* Add a column
|
||||
*
|
||||
* @param string $table_name the name of the table
|
||||
* @param string $column_name the column name
|
||||
* @param string $type the column type
|
||||
* @param array|string $options
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_column($table_name, $column_name, $type, $options = array())
|
||||
{
|
||||
return $this->_adapter->add_column($table_name, $column_name, $type, $options);
|
||||
}
|
||||
/**
|
||||
* Remove a column
|
||||
*
|
||||
* @param string $table_name the name of the table
|
||||
* @param string $column_name the column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove_column($table_name, $column_name)
|
||||
{
|
||||
return $this->_adapter->remove_column($table_name, $column_name);
|
||||
}
|
||||
/**
|
||||
* Change a column
|
||||
*
|
||||
* @param string $table_name the name of the table
|
||||
* @param string $column_name the column name
|
||||
* @param string $type the column type
|
||||
* @param array|string $options
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function change_column($table_name, $column_name, $type, $options = array())
|
||||
{
|
||||
return $this->_adapter->change_column($table_name, $column_name, $type, $options);
|
||||
}
|
||||
/**
|
||||
* Add an index
|
||||
*
|
||||
* @param string $table_name the name of the table
|
||||
* @param string $column_name the column name
|
||||
* @param array|string $options
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_index($table_name, $column_name, $options = array())
|
||||
{
|
||||
return $this->_adapter->add_index($table_name, $column_name, $options);
|
||||
}
|
||||
/**
|
||||
* Remove an index
|
||||
*
|
||||
* @param string $table_name the name of the table
|
||||
* @param string $column_name the column name
|
||||
* @param array|string $options
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove_index($table_name, $column_name, $options = array())
|
||||
{
|
||||
return $this->_adapter->remove_index($table_name, $column_name, $options);
|
||||
}
|
||||
/**
|
||||
* Add timestamps
|
||||
*
|
||||
* @param string $table_name the name of the table
|
||||
* @param string $created_column_name Created at column name
|
||||
* @param string $updated_column_name Updated at column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function add_timestamps($table_name, $created_column_name = "created_at", $updated_column_name = "updated_at")
|
||||
{
|
||||
return $this->_adapter->add_timestamps($table_name, $created_column_name, $updated_column_name);
|
||||
}
|
||||
/**
|
||||
* Remove timestamps
|
||||
*
|
||||
* @param string $table_name the name of the table
|
||||
* @param string $created_column_name Created at column name
|
||||
* @param string $updated_column_name Updated at column name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove_timestamps($table_name, $created_column_name = "created_at", $updated_column_name = "updated_at")
|
||||
{
|
||||
return $this->_adapter->remove_timestamps($table_name, $created_column_name, $updated_column_name);
|
||||
}
|
||||
/**
|
||||
* Create a table
|
||||
* @param string $table_name the name of the table
|
||||
* @param array|string $options
|
||||
* @return bool|Ruckusing_Adapter_MySQL_TableDefinition|Ruckusing_Adapter_PgSQL_TableDefinition|Ruckusing_Adapter_Sqlite3_TableDefinition
|
||||
*/
|
||||
public function create_table($table_name, $options = array())
|
||||
{
|
||||
return $this->_adapter->create_table($table_name, $options);
|
||||
}
|
||||
/**
|
||||
* Execute a query
|
||||
*
|
||||
* @param string $query the query or queries to run
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function execute($query)
|
||||
{
|
||||
return $this->_adapter->multi_query($query);
|
||||
}
|
||||
/**
|
||||
* Select one query
|
||||
*
|
||||
* @param string $sql the query to run
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function select_one($sql)
|
||||
{
|
||||
return $this->_adapter->select_one($sql);
|
||||
}
|
||||
/**
|
||||
* Select all query
|
||||
*
|
||||
* @param string $sql the query to run
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function select_all($sql)
|
||||
{
|
||||
return $this->_adapter->select_all($sql);
|
||||
}
|
||||
/**
|
||||
* Execute a query
|
||||
*
|
||||
* @param string $sql the query to run
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
return $this->_adapter->query($sql);
|
||||
}
|
||||
/**
|
||||
* Quote a string
|
||||
*
|
||||
* @param string $str the string to quote
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function quote_string($str)
|
||||
{
|
||||
return $this->_adapter->quote_string($str);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Implementation of Ruckusing_BaseMigration.
|
||||
* Fix for backward compatibility, take care of old migrations files
|
||||
* before switch to new structure
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Migration
|
||||
* @author (c) Cody Caughlan <codycaughlan % gmail . com>
|
||||
*/
|
||||
class Ruckusing_BaseMigration extends \YoastSEO_Vendor\Ruckusing_Migration_Base
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Task
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Task_Base
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Task
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Task_Base
|
||||
{
|
||||
/**
|
||||
* the framework
|
||||
*
|
||||
* @var Ruckusing_FrameworkRunner
|
||||
*/
|
||||
private $_framework;
|
||||
/**
|
||||
* the adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* the migration directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_migrationDir;
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Task_Base
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Ruckusing_Task_Base
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
$this->setAdapter($adapter);
|
||||
}
|
||||
/**
|
||||
* Get the current framework
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_framework()
|
||||
{
|
||||
return $this->_framework;
|
||||
}
|
||||
/**
|
||||
* Set the current framework
|
||||
*
|
||||
* @param Ruckusing_FrameworkRunner $fw the framework being set
|
||||
*/
|
||||
public function set_framework($fw)
|
||||
{
|
||||
if (!$fw instanceof \YoastSEO_Vendor\Ruckusing_FrameworkRunner) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Framework must be instance of Ruckusing_FrameworkRunner!', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_FRAMEWORK);
|
||||
}
|
||||
$this->_framework = $fw;
|
||||
}
|
||||
/**
|
||||
* set adapter
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter the current adapter
|
||||
*
|
||||
* @return Ruckusing_Task_Base
|
||||
*/
|
||||
public function setAdapter($adapter)
|
||||
{
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Adapter must be implement Ruckusing_Adapter_Base!', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Get the current adapter
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_adapter()
|
||||
{
|
||||
return $this->_adapter;
|
||||
}
|
||||
/**
|
||||
* set migration directories
|
||||
*
|
||||
* @param string $migrationDir Directory of migrations
|
||||
*
|
||||
* @return Ruckusing_Task_Base
|
||||
*/
|
||||
public function setMigrationsDirectory($migrationDir)
|
||||
{
|
||||
$this->_migrationDir = $migrationDir;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Task
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Task_Interface
|
||||
* Interface that all tasks must implement.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Task
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
interface Ruckusing_Task_Interface
|
||||
{
|
||||
/**
|
||||
* execute the task
|
||||
*
|
||||
* @param array $args Argument to the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function execute($args);
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help();
|
||||
/**
|
||||
* Set the migrations directory
|
||||
*
|
||||
* @param string $migrationDir The migration directory path
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMigrationsDirectory($migrationDir);
|
||||
/**
|
||||
* Set the current adapter
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter the current adapter
|
||||
*
|
||||
* @return Ruckusing_Task_Interface
|
||||
*/
|
||||
public function setAdapter($adapter);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Task
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
\define('YoastSEO_Vendor\\RUCKUSING_TASK_DIR', \YoastSEO_Vendor\RUCKUSING_BASE . \DIRECTORY_SEPARATOR . 'lib' . \DIRECTORY_SEPARATOR . 'Task');
|
||||
/**
|
||||
* Ruckusing_Task_Manager
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Task
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Task_Manager
|
||||
{
|
||||
/**
|
||||
* adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter;
|
||||
/**
|
||||
* tasks
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_tasks = array();
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Task_Manager
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
* @param Associative Array $config Extra configuration
|
||||
*
|
||||
* @return Ruckusing_Task_Manager
|
||||
*/
|
||||
public function __construct($adapter, $config = null)
|
||||
{
|
||||
$this->setAdapter($adapter);
|
||||
$this->_config = $config;
|
||||
$this->load_all_tasks(\YoastSEO_Vendor\RUCKUSING_TASK_DIR);
|
||||
if (\is_array($config) && \array_key_exists('tasks_dir', $config)) {
|
||||
$this->load_all_tasks($config['tasks_dir']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* set adapter
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter the current adapter
|
||||
*
|
||||
* @return Ruckusing_Util_Migrator
|
||||
*/
|
||||
public function setAdapter($adapter)
|
||||
{
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Adapter must be implement Ruckusing_Adapter_Base!', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Get the current adapter
|
||||
*
|
||||
* @return object $adapter The current adapter being used
|
||||
*/
|
||||
public function get_adapter()
|
||||
{
|
||||
return $this->_adapter;
|
||||
}
|
||||
/**
|
||||
* Searches for the given task, and if found
|
||||
* returns it. Otherwise null is returned.
|
||||
*
|
||||
* @param string $key The task name
|
||||
*
|
||||
* @return object | null
|
||||
*/
|
||||
public function get_task($key)
|
||||
{
|
||||
if (!$this->has_task($key)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Task '{$key}' is not registered.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
return $this->_tasks[$key];
|
||||
}
|
||||
/**
|
||||
* Check if a task exists
|
||||
*
|
||||
* @param string $key The task name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_task($key)
|
||||
{
|
||||
return \array_key_exists($key, $this->_tasks);
|
||||
}
|
||||
/**
|
||||
* Register a new task name under the specified key.
|
||||
* $obj is a class which implements the ITask interface
|
||||
* and has an execute() method defined.
|
||||
*
|
||||
* @param string $key the task name
|
||||
* @param object $obj the task object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function register_task($key, $obj)
|
||||
{
|
||||
if (\array_key_exists($key, $this->_tasks)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Task key '%s' is already defined!", $key), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
return \false;
|
||||
}
|
||||
if (!$obj instanceof \YoastSEO_Vendor\Ruckusing_Task_Interface) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf('Task (' . $key . ') does not implement Ruckusing_Task_Interface', $key), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
return \false;
|
||||
}
|
||||
$this->_tasks[$key] = $obj;
|
||||
return \true;
|
||||
}
|
||||
//---------------------
|
||||
// PRIVATE METHODS
|
||||
//---------------------
|
||||
/**
|
||||
* Load all taks
|
||||
*
|
||||
* @param string $task_dir the task dir path
|
||||
*/
|
||||
private function load_all_tasks($task_dir)
|
||||
{
|
||||
if (!\is_dir($task_dir)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Task dir: %s does not exist", $task_dir), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
return \false;
|
||||
}
|
||||
$namespaces = \scandir($task_dir);
|
||||
foreach ($namespaces as $namespace) {
|
||||
if ($namespace == '.' || $namespace == '..' || !\is_dir($task_dir . \DIRECTORY_SEPARATOR . $namespace)) {
|
||||
continue;
|
||||
}
|
||||
$files = \scandir($task_dir . \DIRECTORY_SEPARATOR . $namespace);
|
||||
$regex = '/^(\\w+)\\.php$/';
|
||||
foreach ($files as $file) {
|
||||
//skip over invalid files
|
||||
if ($file == '.' || $file == ".." || !\preg_match($regex, $file, $matches)) {
|
||||
continue;
|
||||
}
|
||||
require_once $task_dir . \DIRECTORY_SEPARATOR . $namespace . \DIRECTORY_SEPARATOR . $file;
|
||||
$klass = \YoastSEO_Vendor\Ruckusing_Util_Naming::class_from_file_name($task_dir . \DIRECTORY_SEPARATOR . $namespace . \DIRECTORY_SEPARATOR . $file);
|
||||
$task_name = \YoastSEO_Vendor\Ruckusing_Util_Naming::task_from_class_name($klass);
|
||||
$this->register_task($task_name, new $klass($this->get_adapter()));
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Execute a task
|
||||
*
|
||||
* @param object $framework The current framework
|
||||
* @param string $task_name the task to execute
|
||||
* @param array $options
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function execute($framework, $task_name, $options)
|
||||
{
|
||||
$task = $this->get_task($task_name);
|
||||
$task->set_framework($framework);
|
||||
return $task->execute($options);
|
||||
}
|
||||
/**
|
||||
* Get display help of task
|
||||
*
|
||||
* @param string $task_name The task name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help($task_name)
|
||||
{
|
||||
$task = $this->get_task($task_name);
|
||||
return $task->help();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Util
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Util_Logger
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Util
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Util_Logger
|
||||
{
|
||||
/**
|
||||
* Instance of logger
|
||||
*
|
||||
* @var Ruckusing_Util_Logger
|
||||
*/
|
||||
private static $_instance;
|
||||
/**
|
||||
* file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_file = '';
|
||||
/**
|
||||
* File descriptor
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
private $_fp;
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Util_Logger
|
||||
*
|
||||
* @param string $file the path to log to
|
||||
*
|
||||
* @return Ruckusing_Util_Logger
|
||||
*/
|
||||
public function __construct($file)
|
||||
{
|
||||
$this->_file = $file;
|
||||
$this->_fp = \fopen($this->_file, "a+");
|
||||
}
|
||||
/**
|
||||
* Close the file descriptor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
/**
|
||||
* Singleton for the instance
|
||||
*
|
||||
* @param string $logfile the path to log to
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function instance($logfile)
|
||||
{
|
||||
if (self::$_instance !== \NULL) {
|
||||
return $instance;
|
||||
}
|
||||
$instance = new \YoastSEO_Vendor\Ruckusing_Util_Logger($logfile);
|
||||
return $instance;
|
||||
}
|
||||
/**
|
||||
* Log a message
|
||||
*
|
||||
* @param string $msg message to log
|
||||
*/
|
||||
public function log($msg)
|
||||
{
|
||||
if ($this->_fp) {
|
||||
$ts = \date('M d H:i:s', \time());
|
||||
$line = \sprintf("%s [info] %s\n", $ts, $msg);
|
||||
\fwrite($this->_fp, $line);
|
||||
} else {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error: logfile '%s' not open for writing!", $this->_file), \YoastSEO_Vendor\Ruckusing_Exception::INVALID_LOG);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Close the log file handler
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->_fp) {
|
||||
$closed = \fclose($this->_fp);
|
||||
if ($closed) {
|
||||
$this->_fp = null;
|
||||
self::$_instance = null;
|
||||
} else {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Error closing the log file', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_LOG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Util
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Implementation of Ruckusing_Util_Migrator
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Util
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Util_Migrator
|
||||
{
|
||||
/**
|
||||
* adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* migrations
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_migrations = array();
|
||||
/**
|
||||
* Creates an instance of Ruckusing_Util_Migrator
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Ruckusing_Util_Migrator
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
$this->setAdapter($adapter);
|
||||
}
|
||||
/**
|
||||
* set adapter
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter the current adapter
|
||||
*
|
||||
* @return Ruckusing_Util_Migrator
|
||||
*/
|
||||
public function setAdapter($adapter)
|
||||
{
|
||||
if (!$adapter instanceof \YoastSEO_Vendor\Ruckusing_Adapter_Base) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Adapter must be implement Ruckusing_Adapter_Base!', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ADAPTER);
|
||||
}
|
||||
$this->_adapter = $adapter;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Return the max version number from the DB, or "0" in the case of no versions available.
|
||||
* We must use strings because our date/timestamp when treated as an integer would cause overflow.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_max_version()
|
||||
{
|
||||
// We only want one row but we cannot assume that we are using MySQL and use a LIMIT statement
|
||||
// as it is not part of the SQL standard. Thus we have to select all rows and use PHP to return
|
||||
// the record we need
|
||||
$versions_nested = $this->_adapter->select_all(\sprintf("SELECT version FROM %s", $this->_adapter->get_schema_version_table_name()));
|
||||
$versions = array();
|
||||
foreach ($versions_nested as $v) {
|
||||
$versions[] = $v['version'];
|
||||
}
|
||||
$num_versions = \count($versions);
|
||||
if ($num_versions) {
|
||||
\sort($versions);
|
||||
//sorts lowest-to-highest (ascending)
|
||||
return (string) $versions[$num_versions - 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This methods calculates the actual set of migrations that should be performed, taking into account
|
||||
* the current version, the target version and the direction (up/down). When going up this method will
|
||||
* skip migrations that have not been executed, when going down this method will only include migrations
|
||||
* that have been executed.
|
||||
*
|
||||
* @param array $directories the migration dirs
|
||||
* @param string $direction up/down
|
||||
* @param string $destination the version to migrate to
|
||||
* @param boolean $use_cache the current logger
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_runnable_migrations($directories, $direction, $destination = null, $use_cache = \true)
|
||||
{
|
||||
// cache migration lookups and early return if we've seen this requested set
|
||||
if ($use_cache == \true) {
|
||||
$key = $direction . '-' . $destination;
|
||||
if (\array_key_exists($key, $this->_migrations)) {
|
||||
return $this->_migrations[$key];
|
||||
}
|
||||
}
|
||||
$runnable = array();
|
||||
$migrations = array();
|
||||
$migrations = $this->get_migration_files($directories, $direction);
|
||||
$current = $this->find_version($migrations, $this->get_max_version());
|
||||
$target = $this->find_version($migrations, $destination);
|
||||
if (\is_null($target) && !\is_null($destination) && $destination > 0) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Could not find target version {$destination} in set of migrations.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_TARGET_MIGRATION);
|
||||
}
|
||||
$start = $direction == 'up' ? 0 : \array_search($current, $migrations);
|
||||
$start = $start !== \false ? $start : 0;
|
||||
$finish = \array_search($target, $migrations);
|
||||
$finish = $finish !== \false ? $finish : \count($migrations) - 1;
|
||||
$item_length = $finish - $start + 1;
|
||||
$runnable = \array_slice($migrations, $start, $item_length);
|
||||
//dont include first item if going down but not if going all the way to the bottom
|
||||
if ($direction == 'down' && \count($runnable) > 0 && $target != null) {
|
||||
\array_pop($runnable);
|
||||
}
|
||||
$executed = $this->get_executed_migrations();
|
||||
$to_execute = array();
|
||||
foreach ($runnable as $migration) {
|
||||
//Skip ones that we have already executed
|
||||
if ($direction == 'up' && \in_array($migration['version'], $executed)) {
|
||||
continue;
|
||||
}
|
||||
//Skip ones that we never executed
|
||||
if ($direction == 'down' && !\in_array($migration['version'], $executed)) {
|
||||
continue;
|
||||
}
|
||||
$to_execute[] = $migration;
|
||||
}
|
||||
if ($use_cache == \true) {
|
||||
$this->_migrations[$key] = $to_execute;
|
||||
}
|
||||
return $to_execute;
|
||||
}
|
||||
/**
|
||||
* Generate a timestamp for the current time in UTC format
|
||||
* Returns a string like '20090122193325'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generate_timestamp()
|
||||
{
|
||||
return \gmdate('YmdHis', \time());
|
||||
}
|
||||
/**
|
||||
* If we are going UP then log this version as executed, if going DOWN then delete
|
||||
* this version from our set of executed migrations.
|
||||
*
|
||||
* @param object $version the version
|
||||
* @param object $direction up/down
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function resolve_current_version($version, $direction)
|
||||
{
|
||||
if ($direction === 'up') {
|
||||
$this->_adapter->set_current_version($version);
|
||||
}
|
||||
if ($direction === 'down') {
|
||||
$this->_adapter->remove_version($version);
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
/**
|
||||
* Returns an array of strings which represent version numbers that we *have* migrated
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_executed_migrations()
|
||||
{
|
||||
return $this->executed_migrations();
|
||||
}
|
||||
/**
|
||||
* Return a set of migration files, according to the given direction.
|
||||
* If nested, then return a complex array with the migration parts broken up into parts
|
||||
* which make analysis much easier.
|
||||
*
|
||||
* @param array $directories the migration dirs
|
||||
* @param string $direction the direction up/down
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_migration_files($directories, $direction)
|
||||
{
|
||||
$valid_files = array();
|
||||
foreach ($directories as $name => $path) {
|
||||
if (!\is_dir($path)) {
|
||||
if (\mkdir($path, 0755, \true) === \FALSE) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("\n\tUnable to create migrations directory at %s, check permissions?", $path, \YoastSEO_Vendor\Ruckusing_Exception::INVALID_MIGRATION_DIR);
|
||||
}
|
||||
}
|
||||
$files = \scandir($path);
|
||||
$file_cnt = \count($files);
|
||||
if ($file_cnt > 0) {
|
||||
for ($i = 0; $i < $file_cnt; $i++) {
|
||||
if (\preg_match('/^(\\d+)_(.*)\\.php$/', $files[$i], $matches)) {
|
||||
if (\count($matches) == 3) {
|
||||
$valid_files[] = array('name' => $files[$i], 'module' => $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
\usort($valid_files, array("\YoastSEO_Vendor\Ruckusing_Util_Migrator", "migration_compare"));
|
||||
//sorts in place
|
||||
if ($direction == 'down') {
|
||||
$valid_files = \array_reverse($valid_files);
|
||||
}
|
||||
//user wants a nested structure
|
||||
$files = array();
|
||||
$cnt = \count($valid_files);
|
||||
for ($i = 0; $i < $cnt; $i++) {
|
||||
$migration = $valid_files[$i];
|
||||
if (\preg_match('/^(\\d+)_(.*)\\.php$/', $migration['name'], $matches)) {
|
||||
$files[] = array('version' => $matches[1], 'class' => $matches[2], 'file' => $matches[0], 'module' => $migration['module']);
|
||||
}
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
/**
|
||||
* Find the specified structure (representing a migration) that matches the given version
|
||||
*
|
||||
* @param array $migrations the list of migrations
|
||||
* @param string $version the version being searched
|
||||
* @param boolean $only_index whether to only return the index of the version
|
||||
*
|
||||
* @return null | integer | array
|
||||
*/
|
||||
public function find_version($migrations, $version, $only_index = \false)
|
||||
{
|
||||
$len = \count($migrations);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if ($migrations[$i]['version'] == $version) {
|
||||
return $only_index ? $i : $migrations[$i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//== Private methods
|
||||
/**
|
||||
* Custom comparator for migration sorting
|
||||
*
|
||||
* @param array $a first migration structure
|
||||
* @param array $b second migration structure
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private static function migration_compare($a, $b)
|
||||
{
|
||||
return \strcmp($a["name"], $b["name"]);
|
||||
}
|
||||
/**
|
||||
* Find the index of the migration in the set of migrations that match the given version
|
||||
*
|
||||
* @param array $migrations the list of migrations
|
||||
* @param string $version the version being searched
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
private function find_version_index($migrations, $version)
|
||||
{
|
||||
//edge case
|
||||
if (\is_null($version)) {
|
||||
return null;
|
||||
}
|
||||
$len = \count($migrations);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if ($migrations[$i]['version'] == $version) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Query the database and return a list of migration versions that *have* been executed
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function executed_migrations()
|
||||
{
|
||||
$query_sql = \sprintf('SELECT version FROM %s', $this->_adapter->get_schema_version_table_name());
|
||||
$versions = $this->_adapter->select_all($query_sql);
|
||||
$executed = array();
|
||||
foreach ($versions as $v) {
|
||||
$executed[] = $v['version'];
|
||||
}
|
||||
\sort($executed);
|
||||
return $executed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Util
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Ruckusing_Util_Naming
|
||||
* This utility class maps class names between their task names, back and forth.
|
||||
*
|
||||
* This framework relies on conventions which allow us to make certain
|
||||
* assumptions.
|
||||
*
|
||||
* Example valid task names are "db:version" which maps to a PHP class called DB_Version.
|
||||
*
|
||||
* Namely, underscores are converted to colons, the first part of the task name is upper-cased
|
||||
* and the first character of the second part is capitalized.
|
||||
*
|
||||
* Using this convention one can easily go back and forth between task names and PHP Class names.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Ruckusing_Util
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Ruckusing_Util_Naming
|
||||
{
|
||||
/**
|
||||
* prefix of class name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CLASS_NS_PREFIX = '\YoastSEO_Vendor\Task_';
|
||||
/**
|
||||
* Get the corresponding task from a class name
|
||||
*
|
||||
* @param string $klass the class name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function task_from_class_name($klass)
|
||||
{
|
||||
if (!\preg_match('/' . preg_quote(self::CLASS_NS_PREFIX) . '/', $klass)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('The class name must start with ' . self::CLASS_NS_PREFIX, \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
//strip namespace
|
||||
$klass = \str_replace(self::CLASS_NS_PREFIX, '', $klass);
|
||||
$klass = \strtolower($klass);
|
||||
$klass = \str_replace("_", ":", $klass);
|
||||
return $klass;
|
||||
}
|
||||
/**
|
||||
* Convert a task to its corresponding class name
|
||||
*
|
||||
* @param string $task the task name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function task_to_class_name($task)
|
||||
{
|
||||
if (\false === \stripos($task, ':')) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception('Task name (' . $task . ') must be contains ":"', \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$parts = \explode(":", $task);
|
||||
return self::CLASS_NS_PREFIX . \ucfirst($parts[0]) . '_' . \ucfirst($parts[1]);
|
||||
}
|
||||
/**
|
||||
* Find class from filename
|
||||
*
|
||||
* @param string $file_name the migration filename
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function class_from_file_name($file_name)
|
||||
{
|
||||
//we could be given either a string or an absolute path
|
||||
//deal with it appropriately
|
||||
// normalize directory separators first
|
||||
$file_name = \str_replace(array('/', '\\'), \DIRECTORY_SEPARATOR, $file_name);
|
||||
$parts = \explode(\DIRECTORY_SEPARATOR, $file_name);
|
||||
$namespace = $parts[\count($parts) - 2];
|
||||
$file_name = \substr($parts[\count($parts) - 1], 0, -4);
|
||||
return self::CLASS_NS_PREFIX . \ucfirst($namespace) . '_' . \ucfirst($file_name);
|
||||
}
|
||||
/**
|
||||
* Find class from migration file
|
||||
*
|
||||
* @param string $file_name the migration filename
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function class_from_migration_file($file_name)
|
||||
{
|
||||
$className = \false;
|
||||
if (\preg_match('/^(\\d+)_(.*)\\.php$/', $file_name, $matches)) {
|
||||
if (\count($matches) == 3) {
|
||||
$className = $matches[2];
|
||||
}
|
||||
}
|
||||
return $className;
|
||||
}
|
||||
/**
|
||||
* Transform to camelcase
|
||||
*
|
||||
* @param string $str the task name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function camelcase($str)
|
||||
{
|
||||
$str = \preg_replace('/\\s+/', '_', $str);
|
||||
$parts = \explode("_", $str);
|
||||
//if there were no spaces in the input string
|
||||
//then assume its already camel-cased
|
||||
if (\count($parts) == 0) {
|
||||
return $str;
|
||||
}
|
||||
$cleaned = "";
|
||||
foreach ($parts as $word) {
|
||||
$cleaned .= \ucfirst($word);
|
||||
}
|
||||
return $cleaned;
|
||||
}
|
||||
/**
|
||||
* Get an index name
|
||||
*
|
||||
* @param string $table_name the table name
|
||||
* @param string $column_name the column name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function index_name($table_name, $column_name)
|
||||
{
|
||||
$name = \sprintf("idx_%s", self::underscore($table_name));
|
||||
//if the column parameter is an array then the user wants to create a multi-column
|
||||
//index
|
||||
if (\is_array($column_name)) {
|
||||
$column_str = \join("_and_", $column_name);
|
||||
} else {
|
||||
$column_str = $column_name;
|
||||
}
|
||||
$name .= \sprintf("_%s", $column_str);
|
||||
return $name;
|
||||
}
|
||||
/**
|
||||
* Finds underscore
|
||||
*
|
||||
* @param string $str the task name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function underscore($str)
|
||||
{
|
||||
$underscored = \preg_replace('/\\W/', '_', $str);
|
||||
return \preg_replace('/\\_{2,}/', '_', $underscored);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Task_DB_Generate
|
||||
* generic task which acts as a Generator for migrations.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @author Salimane Adjao Moustapha <me@salimane.com>
|
||||
*/
|
||||
class Task_Db_Generate extends \YoastSEO_Vendor\Ruckusing_Task_Base implements \YoastSEO_Vendor\Ruckusing_Task_Interface
|
||||
{
|
||||
/**
|
||||
* Current Adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* Creates an instance of Task_DB_Generate
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Task_DB_Generate
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
parent::__construct($adapter);
|
||||
$this->_adapter = $adapter;
|
||||
}
|
||||
/**
|
||||
* Primary task entry point
|
||||
*
|
||||
* @param array $args The current supplied options.
|
||||
*/
|
||||
public function execute($args)
|
||||
{
|
||||
$output = '';
|
||||
// Add support for old migration style
|
||||
if (!\is_array($args) || !\array_key_exists('name', $args)) {
|
||||
$cargs = $this->parse_args($_SERVER['argv']);
|
||||
//input sanity check
|
||||
if (!\is_array($cargs) || !\array_key_exists('name', $cargs)) {
|
||||
$output .= $this->help();
|
||||
return $output;
|
||||
}
|
||||
$migration_name = $cargs['name'];
|
||||
} else {
|
||||
$migration_name = $args['name'];
|
||||
}
|
||||
if (!\array_key_exists('module', $args)) {
|
||||
$args['module'] = '';
|
||||
}
|
||||
//clear any filesystem stats cache
|
||||
\clearstatcache();
|
||||
$framework = $this->get_framework();
|
||||
$migrations_dir = $framework->migrations_directory($args['module']);
|
||||
if (!\is_dir($migrations_dir)) {
|
||||
$output .= "\n\tMigrations directory (" . $migrations_dir . " doesn't exist, attempting to create.\n";
|
||||
if (\mkdir($migrations_dir, 0755, \true) === \FALSE) {
|
||||
$output .= "\n\tUnable to create migrations directory at " . $migrations_dir . ", check permissions?\n";
|
||||
} else {
|
||||
$output .= "\n\tCreated OK\n";
|
||||
}
|
||||
}
|
||||
//generate a complete migration file
|
||||
$next_version = \YoastSEO_Vendor\Ruckusing_Util_Migrator::generate_timestamp();
|
||||
$class = \YoastSEO_Vendor\Ruckusing_Util_Naming::camelcase($migration_name);
|
||||
if (!self::classNameIsCorrect($class)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Bad migration name,PHP class can't be named as {$class}.Please, choose another name.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$all_dirs = $framework->migrations_directories();
|
||||
if ($re = self::classNameIsDuplicated($class, $all_dirs)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("This migration name is already used in the \"{$re}\" directory. Please, choose another name.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
|
||||
}
|
||||
$file_name = $next_version . '_' . $class . '.php';
|
||||
//check to make sure our destination directory is writable
|
||||
if (!\is_writable($migrations_dir)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("ERROR: migration directory '" . $migrations_dir . "' is not writable by the current user. Check permissions and try again.", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_MIGRATION_DIR);
|
||||
}
|
||||
//write it out!
|
||||
$full_path = $migrations_dir . \DIRECTORY_SEPARATOR . $file_name;
|
||||
$template_str = self::get_template($class);
|
||||
$file_result = \file_put_contents($full_path, $template_str);
|
||||
if ($file_result === \FALSE) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("Error writing to migrations directory/file. Do you have sufficient privileges?", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_MIGRATION_DIR);
|
||||
} else {
|
||||
$output .= "\n\tCreated migration: {$file_name}\n\n";
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* Parse command line arguments.
|
||||
*
|
||||
* @param array $argv The current supplied command line arguments.
|
||||
*
|
||||
* @return array ('name' => 'name')
|
||||
*/
|
||||
public function parse_args($argv)
|
||||
{
|
||||
foreach ($argv as $i => $arg) {
|
||||
if (\strpos($arg, '=') !== \FALSE) {
|
||||
unset($argv[$i]);
|
||||
}
|
||||
}
|
||||
$num_args = \count($argv);
|
||||
if ($num_args < 3) {
|
||||
return array();
|
||||
}
|
||||
$migration_name = $argv[2];
|
||||
return array('name' => $migration_name);
|
||||
}
|
||||
/**
|
||||
* Indicate if a class name is already used
|
||||
*
|
||||
* @param string $classname The class name to test
|
||||
* @param string $migrationsDirs The array with directories of migration files (in simplest case - just array with one element)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function classNameIsDuplicated($classname, $migrationsDirs)
|
||||
{
|
||||
$migrationFiles = \YoastSEO_Vendor\Ruckusing_Util_Migrator::get_migration_files($migrationsDirs, 'up');
|
||||
$classname = \strtolower($classname);
|
||||
foreach ($migrationFiles as $file) {
|
||||
if (\strtolower($file['class']) == $classname) {
|
||||
return $file['module'];
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* Indicate if a class name is correct or not.
|
||||
*
|
||||
* @param string $classname The class name to test
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function classNameIsCorrect($classname)
|
||||
{
|
||||
$correct_class_name_regex = '/^[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*$/';
|
||||
if (\preg_match($correct_class_name_regex, $classname)) {
|
||||
return \true;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* generate a migration template string
|
||||
*
|
||||
* @param string $klass class name to create
|
||||
* @return string
|
||||
*/
|
||||
public static function get_template($klass)
|
||||
{
|
||||
$template = <<<TPL
|
||||
<?php
|
||||
|
||||
class {$klass} extends Ruckusing_Migration_Base
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
}//up()
|
||||
|
||||
public function down()
|
||||
{
|
||||
}//down()
|
||||
}
|
||||
|
||||
TPL;
|
||||
return $template;
|
||||
}
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help()
|
||||
{
|
||||
$output = <<<USAGE
|
||||
|
||||
\tTask: db:generate <migration name>
|
||||
|
||||
\tGenerator for migrations.
|
||||
|
||||
\t<migration name> is a descriptive name of the migration,
|
||||
\tjoined with underscores. e.g.: add_index_to_users | create_users_table
|
||||
|
||||
\tExample :
|
||||
|
||||
\t\tphp {$_SERVER['argv'][0]} db:generate add_index_to_users
|
||||
|
||||
USAGE;
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
\define('YoastSEO_Vendor\\STYLE_REGULAR', 1);
|
||||
\define('YoastSEO_Vendor\\STYLE_OFFSET', 2);
|
||||
/**
|
||||
* Task_DB_Migrate.
|
||||
* This is the primary work-horse method, it runs all migrations available,
|
||||
* up to the current version.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Task_Db_Migrate extends \YoastSEO_Vendor\Ruckusing_Task_Base implements \YoastSEO_Vendor\Ruckusing_Task_Interface
|
||||
{
|
||||
/**
|
||||
* migrator util
|
||||
*
|
||||
* @var Ruckusing_Util_Migrator
|
||||
*/
|
||||
private $_migrator_util = null;
|
||||
/**
|
||||
* Current Adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* migrator directories
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_migratorDirs = null;
|
||||
/**
|
||||
* The task arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $_task_args = array();
|
||||
/**
|
||||
* debug
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_debug = \false;
|
||||
/**
|
||||
* Return executed string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_return = '';
|
||||
/**
|
||||
* Creates an instance of Task_DB_Migrate
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Task_DB_Migrate
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
parent::__construct($adapter);
|
||||
$this->_adapter = $adapter;
|
||||
$this->_migrator_util = new \YoastSEO_Vendor\Ruckusing_Util_Migrator($this->_adapter);
|
||||
}
|
||||
/**
|
||||
* Primary task entry point
|
||||
*
|
||||
* @param array $args The current supplied options.
|
||||
*/
|
||||
public function execute($args)
|
||||
{
|
||||
if (!$this->_adapter->supports_migrations()) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("This database does not support migrations.", \YoastSEO_Vendor\Ruckusing_Exception::MIGRATION_NOT_SUPPORTED);
|
||||
}
|
||||
$this->_task_args = $args;
|
||||
$this->_return .= "Started: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
$this->_return .= "[db:migrate]: \n";
|
||||
try {
|
||||
// Check that the schema_version table exists, and if not, automatically create it
|
||||
$this->verify_environment();
|
||||
$target_version = null;
|
||||
$style = \YoastSEO_Vendor\STYLE_REGULAR;
|
||||
//did the user specify an explicit version?
|
||||
if (\array_key_exists('version', $this->_task_args)) {
|
||||
$target_version = \trim($this->_task_args['version']);
|
||||
}
|
||||
// did the user specify a relative offset, e.g. "-2" or "+3" ?
|
||||
if ($target_version !== null) {
|
||||
if (\preg_match('/^([\\-\\+])(\\d+)$/', $target_version, $matches)) {
|
||||
if (\count($matches) == 3) {
|
||||
$direction = $matches[1] == '-' ? 'down' : 'up';
|
||||
$steps = \intval($matches[2]);
|
||||
$style = \YoastSEO_Vendor\STYLE_OFFSET;
|
||||
}
|
||||
}
|
||||
}
|
||||
//determine our direction and target version
|
||||
$current_version = $this->_migrator_util->get_max_version();
|
||||
if ($style == \YoastSEO_Vendor\STYLE_REGULAR) {
|
||||
if (\is_null($target_version)) {
|
||||
$this->prepare_to_migrate($target_version, 'up');
|
||||
} elseif ($current_version > $target_version) {
|
||||
$this->prepare_to_migrate($target_version, 'down');
|
||||
} else {
|
||||
$this->prepare_to_migrate($target_version, 'up');
|
||||
}
|
||||
}
|
||||
if ($style == \YoastSEO_Vendor\STYLE_OFFSET) {
|
||||
$this->migrate_from_offset($steps, $current_version, $direction);
|
||||
}
|
||||
// Completed - display accumulated output
|
||||
if (!empty($output)) {
|
||||
$this->_return .= "\n\n";
|
||||
}
|
||||
} catch (\YoastSEO_Vendor\Ruckusing_Exception $ex) {
|
||||
if ($ex->getCode() == \YoastSEO_Vendor\Ruckusing_Exception::MISSING_SCHEMA_INFO_TABLE) {
|
||||
$this->_return .= "\tSchema info table does not exist. I tried creating it but failed. Check permissions.";
|
||||
} else {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
$this->_return .= "\n\nFinished: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
return $this->_return;
|
||||
}
|
||||
/**
|
||||
* Migrate to a specific version using steps from current version
|
||||
*
|
||||
* @param integer $steps number of versions to jump to
|
||||
* @param string $current_version current version
|
||||
* @param $string $direction direction to migrate to 'up'/'down'
|
||||
*/
|
||||
private function migrate_from_offset($steps, $current_version, $direction)
|
||||
{
|
||||
$migrations = $this->_migrator_util->get_migration_files($this->_migratorDirs, $direction);
|
||||
$current_index = $this->_migrator_util->find_version($migrations, $current_version, \true);
|
||||
$current_index = $current_index !== null ? $current_index : -1;
|
||||
if ($this->_debug == \true) {
|
||||
$this->_return .= \print_r($migrations, \true);
|
||||
$this->_return .= "\ncurrent_index: " . $current_index . "\n";
|
||||
$this->_return .= "\ncurrent_version: " . $current_version . "\n";
|
||||
$this->_return .= "\nsteps: " . $steps . " {$direction}\n";
|
||||
}
|
||||
// If we are not at the bottom then adjust our index (to satisfy array_slice)
|
||||
if ($current_index == -1 && $direction === 'down') {
|
||||
$available = array();
|
||||
} else {
|
||||
if ($direction === 'up') {
|
||||
$current_index += 1;
|
||||
} else {
|
||||
$current_index += $steps;
|
||||
}
|
||||
// check to see if we have enough migrations to run - the user
|
||||
// might have asked to run more than we have available
|
||||
$available = \array_slice($migrations, $current_index, $steps);
|
||||
}
|
||||
$target = \end($available);
|
||||
if ($this->_debug == \true) {
|
||||
$this->_return .= "\n------------- TARGET ------------------\n";
|
||||
$this->_return .= \print_r($target, \true);
|
||||
}
|
||||
$this->prepare_to_migrate(isset($target['version']) ? $target['version'] : null, $direction);
|
||||
}
|
||||
/**
|
||||
* Prepare to do a migration
|
||||
*
|
||||
* @param string $destination version to migrate to
|
||||
* @param $string $direction direction to migrate to 'up'/'down'
|
||||
*/
|
||||
private function prepare_to_migrate($destination, $direction)
|
||||
{
|
||||
try {
|
||||
$this->_return .= "\tMigrating " . \strtoupper($direction);
|
||||
if (!\is_null($destination)) {
|
||||
$this->_return .= " to: {$destination}\n";
|
||||
} else {
|
||||
$this->_return .= ":\n";
|
||||
}
|
||||
$migrations = $this->_migrator_util->get_runnable_migrations($this->_migratorDirs, $direction, $destination);
|
||||
if (\count($migrations) == 0) {
|
||||
$this->_return .= "\nNo relevant migrations to run. Exiting...\n";
|
||||
return;
|
||||
}
|
||||
$result = $this->run_migrations($migrations, $direction, $destination);
|
||||
} catch (\Exception $ex) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Run migrations
|
||||
*
|
||||
* @param array $migrations nigrations to run
|
||||
* @param $string $target_method direction to migrate to 'up'/'down'
|
||||
* @param string $destination version to migrate to
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function run_migrations($migrations, $target_method, $destination)
|
||||
{
|
||||
$last_version = -1;
|
||||
foreach ($migrations as $file) {
|
||||
$full_path = $this->_migratorDirs[$file['module']] . \DIRECTORY_SEPARATOR . $file['file'];
|
||||
if (\is_file($full_path) && \is_readable($full_path)) {
|
||||
require_once $full_path;
|
||||
$klass = \YoastSEO_Vendor\Ruckusing_Util_Naming::class_from_migration_file($file['file']);
|
||||
$obj = new $klass($this->_adapter);
|
||||
$start = $this->start_timer();
|
||||
try {
|
||||
//start transaction
|
||||
$this->_adapter->start_transaction();
|
||||
$result = $obj->{$target_method}();
|
||||
//successfully ran migration, update our version and commit
|
||||
$this->_migrator_util->resolve_current_version($file['version'], $target_method);
|
||||
$this->_adapter->commit_transaction();
|
||||
} catch (\YoastSEO_Vendor\Ruckusing_Exception $e) {
|
||||
$this->_adapter->rollback_transaction();
|
||||
//wrap the caught exception in our own
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("%s - %s", $file['class'], $e->getMessage()), \YoastSEO_Vendor\Ruckusing_Exception::MIGRATION_FAILED);
|
||||
}
|
||||
$end = $this->end_timer();
|
||||
$diff = $this->diff_timer($start, $end);
|
||||
$this->_return .= \sprintf("========= %s ======== (%.2f)\n", $file['class'], $diff);
|
||||
$last_version = $file['version'];
|
||||
$exec = \true;
|
||||
}
|
||||
}
|
||||
//update the schema info
|
||||
$result = array('last_version' => $last_version);
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* Start Timer
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function start_timer()
|
||||
{
|
||||
return \microtime(\true);
|
||||
}
|
||||
/**
|
||||
* End Timer
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function end_timer()
|
||||
{
|
||||
return \microtime(\true);
|
||||
}
|
||||
/**
|
||||
* Calculate the time difference
|
||||
*
|
||||
* @param int $s the start time
|
||||
* @param int $e the end time
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function diff_timer($s, $e)
|
||||
{
|
||||
return $e - $s;
|
||||
}
|
||||
/**
|
||||
* Check the environment and create the migration dir if it doesn't exists
|
||||
*/
|
||||
private function verify_environment()
|
||||
{
|
||||
if (!$this->_adapter->table_exists($this->_adapter->get_schema_version_table_name())) {
|
||||
$this->_return .= "\n\tSchema version table does not exist. Auto-creating.";
|
||||
$this->auto_create_schema_info_table();
|
||||
}
|
||||
$this->_migratorDirs = $this->get_framework()->migrations_directories();
|
||||
// create the migrations directory if it doesnt exist
|
||||
foreach ($this->_migratorDirs as $name => $path) {
|
||||
if (!\is_dir($path)) {
|
||||
$this->_return .= \sprintf("\n\tMigrations directory (%s) doesn't exist, attempting to create.", $path);
|
||||
if (\mkdir($path, 0755, \true) === \FALSE) {
|
||||
$this->_return .= \sprintf("\n\tUnable to create migrations directory at %s, check permissions?", $path);
|
||||
} else {
|
||||
$this->_return .= \sprintf("\n\tCreated OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create the schema
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function auto_create_schema_info_table()
|
||||
{
|
||||
try {
|
||||
$this->_return .= \sprintf("\n\tCreating schema version table: %s", $this->_adapter->get_schema_version_table_name() . "\n\n");
|
||||
$this->_adapter->create_schema_version_table();
|
||||
return \true;
|
||||
} catch (\Exception $e) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("\nError auto-creating 'schema_info' table: " . $e->getMessage() . "\n\n", \YoastSEO_Vendor\Ruckusing_Exception::MIGRATION_FAILED);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help()
|
||||
{
|
||||
$output = <<<USAGE
|
||||
|
||||
\tTask: db:migrate [VERSION]
|
||||
|
||||
\tThe primary purpose of the framework is to run migrations, and the
|
||||
\texecution of migrations is all handled by just a regular ol' task.
|
||||
|
||||
\tVERSION can be specified to go up (or down) to a specific
|
||||
\tversion, based on the current version. If not specified,
|
||||
\tall migrations greater than the current database version
|
||||
\twill be executed.
|
||||
|
||||
\tExample A: The database is fresh and empty, assuming there
|
||||
\tare 5 actual migrations, but only the first two should be run.
|
||||
|
||||
\t\tphp {$_SERVER['argv'][0]} db:migrate VERSION=20101006114707
|
||||
|
||||
\tExample B: The current version of the DB is 20101006114707
|
||||
\tand we want to go down to 20100921114643
|
||||
|
||||
\t\tphp {$_SERVER['argv'][0]} db:migrate VERSION=20100921114643
|
||||
|
||||
\tExample C: You can also use relative number of revisions
|
||||
\t(positive migrate up, negative migrate down).
|
||||
|
||||
\t\tphp {$_SERVER['argv'][0]} db:migrate VERSION=-2
|
||||
|
||||
USAGE;
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Task_DB_Schema
|
||||
* generic task which dumps the schema of the DB
|
||||
* as a text file.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Task_Db_Schema extends \YoastSEO_Vendor\Ruckusing_Task_Base implements \YoastSEO_Vendor\Ruckusing_Task_Interface
|
||||
{
|
||||
/**
|
||||
* Current Adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* Return executed string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_return = '';
|
||||
/**
|
||||
* Creates an instance of Task_DB_Schema
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Task_DB_Schema
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
parent::__construct($adapter);
|
||||
$this->_adapter = $adapter;
|
||||
}
|
||||
/**
|
||||
* Primary task entry point
|
||||
*
|
||||
* @param array $args The current supplied options.
|
||||
*/
|
||||
public function execute($args)
|
||||
{
|
||||
$this->_return .= "Started: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
$this->_return .= "[db:schema]: \n";
|
||||
//write to disk
|
||||
$schema_file = $this->db_dir() . '/schema.txt';
|
||||
$schema = $this->_adapter->schema($schema_file);
|
||||
$this->_return .= "\tSchema written to: {$schema_file}\n\n";
|
||||
$this->_return .= "\n\nFinished: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
return $this->_return;
|
||||
}
|
||||
/**
|
||||
* Get the db dir, check and create the db dir if it doesn't exists
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function db_dir()
|
||||
{
|
||||
// create the db directory if it doesnt exist
|
||||
$db_directory = $this->get_framework()->db_directory();
|
||||
if (!\is_dir($db_directory)) {
|
||||
$this->_return .= \sprintf("\n\tDB Schema directory (%s doesn't exist, attempting to create.\n", $db_directory);
|
||||
if (\mkdir($db_directory, 0755, \true) === \FALSE) {
|
||||
$this->_return .= \sprintf("\n\tUnable to create migrations directory at %s, check permissions?\n", $db_directory);
|
||||
} else {
|
||||
$this->_return .= \sprintf("\n\tCreated OK\n\n");
|
||||
}
|
||||
}
|
||||
//check to make sure our destination directory is writable
|
||||
if (!\is_writable($db_directory)) {
|
||||
throw new \YoastSEO_Vendor\Ruckusing_Exception("ERROR: DB Schema directory '" . $db_directory . "' is not writable by the current user. Check permissions and try again.\n", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_DB_DIR);
|
||||
}
|
||||
return $db_directory;
|
||||
}
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help()
|
||||
{
|
||||
$output = <<<USAGE
|
||||
|
||||
\tTask: db:schema
|
||||
|
||||
\tIt can be beneficial to get a dump of the DB in raw SQL format which represents
|
||||
\tthe current version.
|
||||
|
||||
\tNote: This dump only contains the actual schema (e.g. the DML needed to
|
||||
\treconstruct the DB), but not any actual data.
|
||||
|
||||
\tIn MySQL terms, this task would not be the same as running the mysqldump command
|
||||
\t(which by defaults does include any data in the tables).
|
||||
|
||||
USAGE;
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Task_DB_Setup.
|
||||
* This is a generic task which initializes a table to hold migration version information.
|
||||
* This task is non-destructive and will only create the table if it does not already exist, otherwise
|
||||
* no other actions are performed.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Task_Db_Setup extends \YoastSEO_Vendor\Ruckusing_Task_Base implements \YoastSEO_Vendor\Ruckusing_Task_Interface
|
||||
{
|
||||
/**
|
||||
* Current Adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* Creates an instance of Task_DB_Setup
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Task_DB_Setup
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
parent::__construct($adapter);
|
||||
$this->_adapter = $adapter;
|
||||
}
|
||||
/**
|
||||
* Primary task entry point
|
||||
*
|
||||
* @param array $args The current supplied options.
|
||||
*/
|
||||
public function execute($args)
|
||||
{
|
||||
$output = "Started: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
$output .= "[db:setup]: \n";
|
||||
//it doesnt exist, create it
|
||||
if (!$this->_adapter->table_exists($this->_adapter->get_schema_version_table_name())) {
|
||||
$output .= \sprintf("\tCreating table: %s", $this->_adapter->get_schema_version_table_name());
|
||||
$this->_adapter->create_schema_version_table();
|
||||
$output .= "\n\tDone.\n";
|
||||
} else {
|
||||
$output .= \sprintf("\tNOTICE: table '%s' already exists. Nothing to do.", $this->_adapter->get_schema_version_table_name());
|
||||
}
|
||||
$output .= "\n\nFinished: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help()
|
||||
{
|
||||
$output = <<<USAGE
|
||||
|
||||
\tTask: db:setup
|
||||
|
||||
\tA basic task to initialize your DB for migrations is available. One should
|
||||
\talways run this task when first starting out.
|
||||
|
||||
\tThis task does not take arguments.
|
||||
|
||||
USAGE;
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Task_DB_Status.
|
||||
* Prints out a list of migrations that have and haven't been applied
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Task_Db_Status extends \YoastSEO_Vendor\Ruckusing_Task_Base implements \YoastSEO_Vendor\Ruckusing_Task_Interface
|
||||
{
|
||||
/**
|
||||
* Current Adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* Creates an instance of Task_DB_Status
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Task_DB_Status
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
parent::__construct($adapter);
|
||||
$this->_adapter = $adapter;
|
||||
}
|
||||
/**
|
||||
* Primary task entry point
|
||||
*
|
||||
* @param array $args The current supplied options.
|
||||
*/
|
||||
public function execute($args)
|
||||
{
|
||||
$output = "Started: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
$output .= "[db:status]: \n";
|
||||
$util = new \YoastSEO_Vendor\Ruckusing_Util_Migrator($this->_adapter);
|
||||
$migrations = $util->get_executed_migrations();
|
||||
$files = $util->get_migration_files($this->get_framework()->migrations_directories(), 'up');
|
||||
$applied = array();
|
||||
$not_applied = array();
|
||||
foreach ($files as $file) {
|
||||
if (\in_array($file['version'], $migrations)) {
|
||||
$applied[] = $file['class'] . ' [ ' . $file['version'] . ' ]';
|
||||
} else {
|
||||
$not_applied[] = $file['class'] . ' [ ' . $file['version'] . ' ]';
|
||||
}
|
||||
}
|
||||
if (\count($applied) > 0) {
|
||||
$output .= $this->_displayMigrations($applied, 'APPLIED');
|
||||
}
|
||||
if (\count($not_applied) > 0) {
|
||||
$output .= $this->_displayMigrations($not_applied, 'NOT APPLIED');
|
||||
}
|
||||
$output .= "\n\nFinished: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* display migrations results
|
||||
*
|
||||
* @param array $migrations The migrations
|
||||
* @param string $title The title of section
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function _displayMigrations($migrations, $title)
|
||||
{
|
||||
$output = "\n\n===================== {$title} =======================\n";
|
||||
foreach ($migrations as $a) {
|
||||
$output .= "\t" . $a . "\n";
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help()
|
||||
{
|
||||
$output = <<<USAGE
|
||||
|
||||
\tTask: db:status
|
||||
|
||||
\tWith this task you'll get an overview of the already executed migrations and
|
||||
\twhich will be executed when running db:migrate.
|
||||
|
||||
\tThis task does not take arguments.
|
||||
|
||||
USAGE;
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
/**
|
||||
* Ruckusing
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
/**
|
||||
* Task_DB_Version.
|
||||
* This task retrieves the current version of the schema.
|
||||
*
|
||||
* @category Ruckusing
|
||||
* @package Task
|
||||
* @subpackage Db
|
||||
* @author Cody Caughlan <codycaughlan % gmail . com>
|
||||
* @link https://github.com/ruckus/ruckusing-migrations
|
||||
*/
|
||||
class Task_Db_Version extends \YoastSEO_Vendor\Ruckusing_Task_Base implements \YoastSEO_Vendor\Ruckusing_Task_Interface
|
||||
{
|
||||
/**
|
||||
* Current Adapter
|
||||
*
|
||||
* @var Ruckusing_Adapter_Base
|
||||
*/
|
||||
private $_adapter = null;
|
||||
/**
|
||||
* Creates an instance of Task_DB_Version
|
||||
*
|
||||
* @param Ruckusing_Adapter_Base $adapter The current adapter being used
|
||||
*
|
||||
* @return Task_DB_Version
|
||||
*/
|
||||
public function __construct($adapter)
|
||||
{
|
||||
parent::__construct($adapter);
|
||||
$this->_adapter = $adapter;
|
||||
}
|
||||
/**
|
||||
* Primary task entry point
|
||||
*
|
||||
* @param array $args The current supplied options.
|
||||
*/
|
||||
public function execute($args)
|
||||
{
|
||||
$output = "Started: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
$output .= "[db:version]: \n";
|
||||
if (!$this->_adapter->table_exists($this->_adapter->get_schema_version_table_name())) {
|
||||
//it doesnt exist, create it
|
||||
$output .= "\tSchema version table (" . $this->_adapter->get_schema_version_table_name() . ") does not exist. Do you need to run 'db:setup'?";
|
||||
} else {
|
||||
//it exists, read the version from it
|
||||
// We only want one row but we cannot assume that we are using MySQL and use a LIMIT statement
|
||||
// as it is not part of the SQL standard. Thus we have to select all rows and use PHP to return
|
||||
// the record we need
|
||||
$versions_nested = $this->_adapter->select_all(\sprintf("SELECT version FROM %s", $this->_adapter->get_schema_version_table_name()));
|
||||
$versions = array();
|
||||
foreach ($versions_nested as $v) {
|
||||
$versions[] = $v['version'];
|
||||
}
|
||||
$num_versions = \count($versions);
|
||||
if ($num_versions > 0) {
|
||||
\sort($versions);
|
||||
//sorts lowest-to-highest (ascending)
|
||||
$version = (string) $versions[$num_versions - 1];
|
||||
$output .= \sprintf("\tCurrent version: %s", $version);
|
||||
} else {
|
||||
$output .= \sprintf("\tNo migrations have been executed.");
|
||||
}
|
||||
}
|
||||
$output .= "\n\nFinished: " . \date('Y-m-d g:ia T') . "\n\n";
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* Return the usage of the task
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function help()
|
||||
{
|
||||
$output = <<<USAGE
|
||||
|
||||
\tTask: db:version
|
||||
|
||||
\tIt is always possible to ask the framework (really the DB) what version it is
|
||||
\tcurrently at.
|
||||
|
||||
\tThis task does not take arguments.
|
||||
|
||||
USAGE;
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace YoastSEO_Vendor;
|
||||
|
||||
\define('YoastSEO_Vendor\\RUCKUSING_VERSION', '1.1.0');
|
||||
Reference in New Issue
Block a user