From f7d78b0604c4007fda6bbf1aa3fb6a9f5a9ba2cc Mon Sep 17 00:00:00 2001 From: pappastech Date: Wed, 8 Jun 2011 13:22:57 +0000 Subject: [PATCH] Initial commit of release 1.0. ~ Tom git-svn-id: svn+ssh://jekkos@svn.code.sf.net/p/opensourcepos/code/@11 c3eb156b-1dc0-44e1-88ae-e38439141b53 --- system/cache/index.html | 10 + system/codeigniter/Base4.php | 69 + system/codeigniter/Base5.php | 56 + system/codeigniter/CodeIgniter.php | 280 +++ system/codeigniter/Common.php | 421 ++++ system/codeigniter/Compat.php | 93 + system/codeigniter/index.html | 10 + system/database/DB.php | 146 ++ system/database/DB_active_rec.php | 1820 +++++++++++++++ system/database/DB_cache.php | 195 ++ system/database/DB_driver.php | 1366 +++++++++++ system/database/DB_forge.php | 375 +++ system/database/DB_result.php | 342 +++ system/database/DB_utility.php | 389 ++++ system/database/drivers/index.html | 10 + system/database/drivers/mssql/index.html | 10 + .../database/drivers/mssql/mssql_driver.php | 667 ++++++ system/database/drivers/mssql/mssql_forge.php | 248 ++ .../database/drivers/mssql/mssql_result.php | 169 ++ .../database/drivers/mssql/mssql_utility.php | 123 + system/database/drivers/mysql/index.html | 10 + .../database/drivers/mysql/mysql_driver.php | 670 ++++++ system/database/drivers/mysql/mysql_forge.php | 254 ++ .../database/drivers/mysql/mysql_result.php | 169 ++ .../database/drivers/mysql/mysql_utility.php | 245 ++ system/database/drivers/mysqli/index.html | 10 + .../database/drivers/mysqli/mysqli_driver.php | 671 ++++++ .../database/drivers/mysqli/mysqli_forge.php | 254 ++ .../database/drivers/mysqli/mysqli_result.php | 169 ++ .../drivers/mysqli/mysqli_utility.php | 123 + system/database/drivers/oci8/index.html | 10 + system/database/drivers/oci8/oci8_driver.php | 780 +++++++ system/database/drivers/oci8/oci8_forge.php | 248 ++ system/database/drivers/oci8/oci8_result.php | 249 ++ system/database/drivers/oci8/oci8_utility.php | 122 + system/database/drivers/odbc/index.html | 10 + system/database/drivers/odbc/odbc_driver.php | 639 ++++++ system/database/drivers/odbc/odbc_forge.php | 266 +++ system/database/drivers/odbc/odbc_result.php | 228 ++ system/database/drivers/odbc/odbc_utility.php | 148 ++ system/database/drivers/postgre/index.html | 10 + .../drivers/postgre/postgre_driver.php | 684 ++++++ .../drivers/postgre/postgre_forge.php | 248 ++ .../drivers/postgre/postgre_result.php | 169 ++ .../drivers/postgre/postgre_utility.php | 124 + system/database/drivers/sqlite/index.html | 10 + .../database/drivers/sqlite/sqlite_driver.php | 657 ++++++ .../database/drivers/sqlite/sqlite_forge.php | 265 +++ .../database/drivers/sqlite/sqlite_result.php | 179 ++ .../drivers/sqlite/sqlite_utility.php | 141 ++ system/database/index.html | 10 + system/fonts/index.html | 10 + system/fonts/texb.ttf | Bin 0 -> 143821 bytes system/helpers/array_helper.php | 78 + system/helpers/compatibility_helper.php | 498 ++++ system/helpers/cookie_helper.php | 144 ++ system/helpers/date_helper.php | 611 +++++ system/helpers/directory_helper.php | 84 + system/helpers/download_helper.php | 100 + system/helpers/email_helper.php | 62 + system/helpers/file_helper.php | 464 ++++ system/helpers/form_helper.php | 1025 +++++++++ system/helpers/html_helper.php | 416 ++++ system/helpers/index.html | 10 + system/helpers/inflector_helper.php | 171 ++ system/helpers/language_helper.php | 58 + system/helpers/number_helper.php | 75 + system/helpers/path_helper.php | 72 + system/helpers/security_helper.php | 126 + system/helpers/smiley_helper.php | 273 +++ system/helpers/string_helper.php | 273 +++ system/helpers/text_helper.php | 462 ++++ system/helpers/typography_helper.php | 71 + system/helpers/url_helper.php | 593 +++++ system/helpers/xml_helper.php | 62 + system/index.html | 10 + system/language/english/calendar_lang.php | 51 + system/language/english/date_lang.php | 60 + system/language/english/db_lang.php | 28 + system/language/english/email_lang.php | 24 + .../language/english/form_validation_lang.php | 24 + system/language/english/ftp_lang.php | 17 + system/language/english/imglib_lang.php | 24 + system/language/english/index.html | 10 + system/language/english/number_lang.php | 10 + system/language/english/profiler_lang.php | 19 + system/language/english/scaffolding_lang.php | 17 + system/language/english/unit_test_lang.php | 24 + system/language/english/upload_lang.php | 22 + system/language/english/validation_lang.php | 24 + system/language/index.html | 10 + system/language/indonesia/calendar_lang.php | 51 + system/language/indonesia/date_lang.php | 60 + system/language/indonesia/db_lang.php | 28 + system/language/indonesia/email_lang.php | 24 + .../indonesia/form_validation_lang.php | 24 + system/language/indonesia/ftp_lang.php | 17 + system/language/indonesia/imglib_lang.php | 24 + system/language/indonesia/index.html | 10 + system/language/indonesia/number_lang.php | 10 + system/language/indonesia/profiler_lang.php | 19 + .../language/indonesia/scaffolding_lang.php | 17 + system/language/indonesia/unit_test_lang.php | 24 + system/language/indonesia/upload_lang.php | 22 + system/language/indonesia/validation_lang.php | 24 + system/language/spanish/calendar_lang.php | 51 + system/language/spanish/date_lang.php | 60 + system/language/spanish/db_lang.php | 28 + system/language/spanish/email_lang.php | 24 + .../language/spanish/form_validation_lang.php | 24 + system/language/spanish/ftp_lang.php | 17 + system/language/spanish/imglib_lang.php | 24 + system/language/spanish/index.html | 10 + system/language/spanish/number_lang.php | 10 + system/language/spanish/profiler_lang.php | 19 + system/language/spanish/scaffolding_lang.php | 17 + system/language/spanish/unit_test_lang.php | 24 + system/language/spanish/upload_lang.php | 22 + system/language/spanish/validation_lang.php | 24 + system/libraries/Benchmark.php | 113 + system/libraries/Calendar.php | 477 ++++ system/libraries/Cart.php | 550 +++++ system/libraries/Config.php | 244 ++ system/libraries/Controller.php | 127 + system/libraries/Email.php | 2041 +++++++++++++++++ system/libraries/Encrypt.php | 484 ++++ system/libraries/Exceptions.php | 174 ++ system/libraries/Form_validation.php | 1278 +++++++++++ system/libraries/Ftp.php | 618 +++++ system/libraries/Hooks.php | 226 ++ system/libraries/Image_lib.php | 1544 +++++++++++++ system/libraries/Input.php | 1067 +++++++++ system/libraries/Language.php | 123 + system/libraries/Loader.php | 1085 +++++++++ system/libraries/Log.php | 117 + system/libraries/Model.php | 83 + system/libraries/Output.php | 409 ++++ system/libraries/Pagination.php | 244 ++ system/libraries/Parser.php | 173 ++ system/libraries/Profiler.php | 392 ++++ system/libraries/Router.php | 389 ++++ system/libraries/Session.php | 758 ++++++ system/libraries/Sha1.php | 251 ++ system/libraries/Table.php | 440 ++++ system/libraries/Trackback.php | 547 +++++ system/libraries/Typography.php | 406 ++++ system/libraries/URI.php | 586 +++++ system/libraries/Unit_test.php | 347 +++ system/libraries/Upload.php | 970 ++++++++ system/libraries/User_agent.php | 502 ++++ system/libraries/Validation.php | 875 +++++++ system/libraries/Xmlrpc.php | 1421 ++++++++++++ system/libraries/Xmlrpcs.php | 536 +++++ system/libraries/Zip.php | 359 +++ system/libraries/index.html | 10 + system/logs/index.html | 10 + system/plugins/captcha_pi.php | 356 +++ system/plugins/index.html | 10 + system/plugins/js_calendar_pi.php | 629 +++++ system/scaffolding/Scaffolding.php | 291 +++ system/scaffolding/images/background.jpg | Bin 0 -> 410 bytes system/scaffolding/images/index.html | 10 + system/scaffolding/images/logo.jpg | Bin 0 -> 4518 bytes system/scaffolding/index.html | 10 + system/scaffolding/views/add.php | 32 + system/scaffolding/views/delete.php | 9 + system/scaffolding/views/edit.php | 33 + system/scaffolding/views/footer.php | 10 + system/scaffolding/views/header.php | 29 + system/scaffolding/views/index.html | 10 + system/scaffolding/views/no_data.php | 8 + system/scaffolding/views/stylesheet.css | 143 ++ system/scaffolding/views/view.php | 27 + 173 files changed, 42974 insertions(+) create mode 100644 system/cache/index.html create mode 100644 system/codeigniter/Base4.php create mode 100644 system/codeigniter/Base5.php create mode 100644 system/codeigniter/CodeIgniter.php create mode 100644 system/codeigniter/Common.php create mode 100644 system/codeigniter/Compat.php create mode 100644 system/codeigniter/index.html create mode 100644 system/database/DB.php create mode 100644 system/database/DB_active_rec.php create mode 100644 system/database/DB_cache.php create mode 100644 system/database/DB_driver.php create mode 100644 system/database/DB_forge.php create mode 100644 system/database/DB_result.php create mode 100644 system/database/DB_utility.php create mode 100644 system/database/drivers/index.html create mode 100644 system/database/drivers/mssql/index.html create mode 100644 system/database/drivers/mssql/mssql_driver.php create mode 100644 system/database/drivers/mssql/mssql_forge.php create mode 100644 system/database/drivers/mssql/mssql_result.php create mode 100644 system/database/drivers/mssql/mssql_utility.php create mode 100644 system/database/drivers/mysql/index.html create mode 100644 system/database/drivers/mysql/mysql_driver.php create mode 100644 system/database/drivers/mysql/mysql_forge.php create mode 100644 system/database/drivers/mysql/mysql_result.php create mode 100644 system/database/drivers/mysql/mysql_utility.php create mode 100644 system/database/drivers/mysqli/index.html create mode 100644 system/database/drivers/mysqli/mysqli_driver.php create mode 100644 system/database/drivers/mysqli/mysqli_forge.php create mode 100644 system/database/drivers/mysqli/mysqli_result.php create mode 100644 system/database/drivers/mysqli/mysqli_utility.php create mode 100644 system/database/drivers/oci8/index.html create mode 100644 system/database/drivers/oci8/oci8_driver.php create mode 100644 system/database/drivers/oci8/oci8_forge.php create mode 100644 system/database/drivers/oci8/oci8_result.php create mode 100644 system/database/drivers/oci8/oci8_utility.php create mode 100644 system/database/drivers/odbc/index.html create mode 100644 system/database/drivers/odbc/odbc_driver.php create mode 100644 system/database/drivers/odbc/odbc_forge.php create mode 100644 system/database/drivers/odbc/odbc_result.php create mode 100644 system/database/drivers/odbc/odbc_utility.php create mode 100644 system/database/drivers/postgre/index.html create mode 100644 system/database/drivers/postgre/postgre_driver.php create mode 100644 system/database/drivers/postgre/postgre_forge.php create mode 100644 system/database/drivers/postgre/postgre_result.php create mode 100644 system/database/drivers/postgre/postgre_utility.php create mode 100644 system/database/drivers/sqlite/index.html create mode 100644 system/database/drivers/sqlite/sqlite_driver.php create mode 100644 system/database/drivers/sqlite/sqlite_forge.php create mode 100644 system/database/drivers/sqlite/sqlite_result.php create mode 100644 system/database/drivers/sqlite/sqlite_utility.php create mode 100644 system/database/index.html create mode 100644 system/fonts/index.html create mode 100644 system/fonts/texb.ttf create mode 100644 system/helpers/array_helper.php create mode 100644 system/helpers/compatibility_helper.php create mode 100644 system/helpers/cookie_helper.php create mode 100644 system/helpers/date_helper.php create mode 100644 system/helpers/directory_helper.php create mode 100644 system/helpers/download_helper.php create mode 100644 system/helpers/email_helper.php create mode 100644 system/helpers/file_helper.php create mode 100644 system/helpers/form_helper.php create mode 100644 system/helpers/html_helper.php create mode 100644 system/helpers/index.html create mode 100644 system/helpers/inflector_helper.php create mode 100644 system/helpers/language_helper.php create mode 100644 system/helpers/number_helper.php create mode 100644 system/helpers/path_helper.php create mode 100644 system/helpers/security_helper.php create mode 100644 system/helpers/smiley_helper.php create mode 100644 system/helpers/string_helper.php create mode 100644 system/helpers/text_helper.php create mode 100644 system/helpers/typography_helper.php create mode 100644 system/helpers/url_helper.php create mode 100644 system/helpers/xml_helper.php create mode 100644 system/index.html create mode 100644 system/language/english/calendar_lang.php create mode 100644 system/language/english/date_lang.php create mode 100644 system/language/english/db_lang.php create mode 100644 system/language/english/email_lang.php create mode 100644 system/language/english/form_validation_lang.php create mode 100644 system/language/english/ftp_lang.php create mode 100644 system/language/english/imglib_lang.php create mode 100644 system/language/english/index.html create mode 100644 system/language/english/number_lang.php create mode 100644 system/language/english/profiler_lang.php create mode 100644 system/language/english/scaffolding_lang.php create mode 100644 system/language/english/unit_test_lang.php create mode 100644 system/language/english/upload_lang.php create mode 100644 system/language/english/validation_lang.php create mode 100644 system/language/index.html create mode 100644 system/language/indonesia/calendar_lang.php create mode 100644 system/language/indonesia/date_lang.php create mode 100644 system/language/indonesia/db_lang.php create mode 100644 system/language/indonesia/email_lang.php create mode 100644 system/language/indonesia/form_validation_lang.php create mode 100644 system/language/indonesia/ftp_lang.php create mode 100644 system/language/indonesia/imglib_lang.php create mode 100644 system/language/indonesia/index.html create mode 100644 system/language/indonesia/number_lang.php create mode 100644 system/language/indonesia/profiler_lang.php create mode 100644 system/language/indonesia/scaffolding_lang.php create mode 100644 system/language/indonesia/unit_test_lang.php create mode 100644 system/language/indonesia/upload_lang.php create mode 100644 system/language/indonesia/validation_lang.php create mode 100644 system/language/spanish/calendar_lang.php create mode 100644 system/language/spanish/date_lang.php create mode 100644 system/language/spanish/db_lang.php create mode 100644 system/language/spanish/email_lang.php create mode 100644 system/language/spanish/form_validation_lang.php create mode 100644 system/language/spanish/ftp_lang.php create mode 100644 system/language/spanish/imglib_lang.php create mode 100644 system/language/spanish/index.html create mode 100644 system/language/spanish/number_lang.php create mode 100644 system/language/spanish/profiler_lang.php create mode 100644 system/language/spanish/scaffolding_lang.php create mode 100644 system/language/spanish/unit_test_lang.php create mode 100644 system/language/spanish/upload_lang.php create mode 100644 system/language/spanish/validation_lang.php create mode 100644 system/libraries/Benchmark.php create mode 100644 system/libraries/Calendar.php create mode 100644 system/libraries/Cart.php create mode 100644 system/libraries/Config.php create mode 100644 system/libraries/Controller.php create mode 100644 system/libraries/Email.php create mode 100644 system/libraries/Encrypt.php create mode 100644 system/libraries/Exceptions.php create mode 100644 system/libraries/Form_validation.php create mode 100644 system/libraries/Ftp.php create mode 100644 system/libraries/Hooks.php create mode 100644 system/libraries/Image_lib.php create mode 100644 system/libraries/Input.php create mode 100644 system/libraries/Language.php create mode 100644 system/libraries/Loader.php create mode 100644 system/libraries/Log.php create mode 100644 system/libraries/Model.php create mode 100644 system/libraries/Output.php create mode 100644 system/libraries/Pagination.php create mode 100644 system/libraries/Parser.php create mode 100644 system/libraries/Profiler.php create mode 100644 system/libraries/Router.php create mode 100644 system/libraries/Session.php create mode 100644 system/libraries/Sha1.php create mode 100644 system/libraries/Table.php create mode 100644 system/libraries/Trackback.php create mode 100644 system/libraries/Typography.php create mode 100644 system/libraries/URI.php create mode 100644 system/libraries/Unit_test.php create mode 100644 system/libraries/Upload.php create mode 100644 system/libraries/User_agent.php create mode 100644 system/libraries/Validation.php create mode 100644 system/libraries/Xmlrpc.php create mode 100644 system/libraries/Xmlrpcs.php create mode 100644 system/libraries/Zip.php create mode 100644 system/libraries/index.html create mode 100644 system/logs/index.html create mode 100644 system/plugins/captcha_pi.php create mode 100644 system/plugins/index.html create mode 100644 system/plugins/js_calendar_pi.php create mode 100644 system/scaffolding/Scaffolding.php create mode 100644 system/scaffolding/images/background.jpg create mode 100644 system/scaffolding/images/index.html create mode 100644 system/scaffolding/images/logo.jpg create mode 100644 system/scaffolding/index.html create mode 100644 system/scaffolding/views/add.php create mode 100644 system/scaffolding/views/delete.php create mode 100644 system/scaffolding/views/edit.php create mode 100644 system/scaffolding/views/footer.php create mode 100644 system/scaffolding/views/header.php create mode 100644 system/scaffolding/views/index.html create mode 100644 system/scaffolding/views/no_data.php create mode 100644 system/scaffolding/views/stylesheet.css create mode 100644 system/scaffolding/views/view.php diff --git a/system/cache/index.html b/system/cache/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/cache/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/codeigniter/Base4.php b/system/codeigniter/Base4.php new file mode 100644 index 000000000..3561c2564 --- /dev/null +++ b/system/codeigniter/Base4.php @@ -0,0 +1,69 @@ +load->library('email') to instantiate + * classes that can then be used within controllers as $this->email->send() + * + * PHP 4 also has trouble referencing the CI super object within application + * constructors since objects do not exist until the class is fully + * instantiated. Basically PHP 4 sucks... + * + * Since PHP 5 doesn't suffer from this problem so we load one of + * two files based on the version of PHP being run. + * + * @package CodeIgniter + * @subpackage codeigniter + * @category front-controller + * @author ExpressionEngine Dev Team + * @link http://codeigniter.com/user_guide/ + */ + class CI_Base extends CI_Loader { + + function CI_Base() + { + // This allows syntax like $this->load->foo() to work + parent::CI_Loader(); + $this->load =& $this; + + // This allows resources used within controller constructors to work + global $OBJ; + $OBJ = $this->load; // Do NOT use a reference. + } +} + +function &get_instance() +{ + global $CI, $OBJ; + + if (is_object($CI)) + { + return $CI; + } + + return $OBJ->load; +} + + +/* End of file Base4.php */ +/* Location: ./system/codeigniter/Base4.php */ \ No newline at end of file diff --git a/system/codeigniter/Base5.php b/system/codeigniter/Base5.php new file mode 100644 index 000000000..5d944ae5a --- /dev/null +++ b/system/codeigniter/Base5.php @@ -0,0 +1,56 @@ +mark('total_execution_time_start'); +$BM->mark('loading_time_base_classes_start'); + +/* + * ------------------------------------------------------ + * Instantiate the hooks class + * ------------------------------------------------------ + */ + +$EXT =& load_class('Hooks'); + +/* + * ------------------------------------------------------ + * Is there a "pre_system" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('pre_system'); + +/* + * ------------------------------------------------------ + * Instantiate the base classes + * ------------------------------------------------------ + */ + +$CFG =& load_class('Config'); +$URI =& load_class('URI'); +$RTR =& load_class('Router'); +$OUT =& load_class('Output'); + +/* + * ------------------------------------------------------ + * Is there a valid cache file? If so, we're done... + * ------------------------------------------------------ + */ + +if ($EXT->_call_hook('cache_override') === FALSE) +{ + if ($OUT->_display_cache($CFG, $URI) == TRUE) + { + exit; + } +} + +/* + * ------------------------------------------------------ + * Load the remaining base classes + * ------------------------------------------------------ + */ + +$IN =& load_class('Input'); +$LANG =& load_class('Language'); + +/* + * ------------------------------------------------------ + * Load the app controller and local controller + * ------------------------------------------------------ + * + * Note: Due to the poor object handling in PHP 4 we'll + * conditionally load different versions of the base + * class. Retaining PHP 4 compatibility requires a bit of a hack. + * + * Note: The Loader class needs to be included first + * + */ +if ( ! is_php('5.0.0')) +{ + load_class('Loader', FALSE); + require(BASEPATH.'codeigniter/Base4'.EXT); +} +else +{ + require(BASEPATH.'codeigniter/Base5'.EXT); +} + +// Load the base controller class +load_class('Controller', FALSE); + +// Load the local application controller +// Note: The Router class automatically validates the controller path. If this include fails it +// means that the default controller in the Routes.php file is not resolving to something valid. +if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().EXT)) +{ + show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.'); +} + +include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().EXT); + +// Set a mark point for benchmarking +$BM->mark('loading_time_base_classes_end'); + + +/* + * ------------------------------------------------------ + * Security check + * ------------------------------------------------------ + * + * None of the functions in the app controller or the + * loader class can be called via the URI, nor can + * controller functions that begin with an underscore + */ +$class = $RTR->fetch_class(); +$method = $RTR->fetch_method(); + +if ( ! class_exists($class) + OR $method == 'controller' + OR strncmp($method, '_', 1) == 0 + OR in_array(strtolower($method), array_map('strtolower', get_class_methods('Controller'))) + ) +{ + show_404("{$class}/{$method}"); +} + +/* + * ------------------------------------------------------ + * Is there a "pre_controller" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('pre_controller'); + +/* + * ------------------------------------------------------ + * Instantiate the controller and call requested method + * ------------------------------------------------------ + */ + +// Mark a start point so we can benchmark the controller +$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start'); + +$CI = new $class(); + +// Is this a scaffolding request? +if ($RTR->scaffolding_request === TRUE) +{ + if ($EXT->_call_hook('scaffolding_override') === FALSE) + { + $CI->_ci_scaffolding(); + } +} +else +{ + /* + * ------------------------------------------------------ + * Is there a "post_controller_constructor" hook? + * ------------------------------------------------------ + */ + $EXT->_call_hook('post_controller_constructor'); + + // Is there a "remap" function? + if (method_exists($CI, '_remap')) + { + $CI->_remap($method); + } + else + { + // is_callable() returns TRUE on some versions of PHP 5 for private and protected + // methods, so we'll use this workaround for consistent behavior + if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI)))) + { + show_404("{$class}/{$method}"); + } + + // Call the requested method. + // Any URI segments present (besides the class/function) will be passed to the method for convenience + call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2)); + } +} + +// Mark a benchmark end point +$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); + +/* + * ------------------------------------------------------ + * Is there a "post_controller" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('post_controller'); + +/* + * ------------------------------------------------------ + * Send the final rendered output to the browser + * ------------------------------------------------------ + */ + +if ($EXT->_call_hook('display_override') === FALSE) +{ + $OUT->_display(); +} + +/* + * ------------------------------------------------------ + * Is there a "post_system" hook? + * ------------------------------------------------------ + */ +$EXT->_call_hook('post_system'); + +/* + * ------------------------------------------------------ + * Close the DB connection if one exists + * ------------------------------------------------------ + */ +if (class_exists('CI_DB') AND isset($CI->db)) +{ + $CI->db->close(); +} + + +/* End of file CodeIgniter.php */ +/* Location: ./system/codeigniter/CodeIgniter.php */ \ No newline at end of file diff --git a/system/codeigniter/Common.php b/system/codeigniter/Common.php new file mode 100644 index 000000000..3b84ffcc1 --- /dev/null +++ b/system/codeigniter/Common.php @@ -0,0 +1,421 @@ + 5 +* we'll set a static variable. +* +* @access public +* @param string +* @return bool +*/ +function is_php($version = '5.0.0') +{ + static $_is_php; + $version = (string)$version; + + if ( ! isset($_is_php[$version])) + { + $_is_php[$version] = (version_compare(PHP_VERSION, $version) < 0) ? FALSE : TRUE; + } + + return $_is_php[$version]; +} + +// ------------------------------------------------------------------------ + +/** + * Tests for file writability + * + * is_writable() returns TRUE on Windows servers when you really can't write to + * the file, based on the read-only attribute. is_writable() is also unreliable + * on Unix servers if safe_mode is on. + * + * @access private + * @return void + */ +function is_really_writable($file) +{ + // If we're on a Unix server with safe_mode off we call is_writable + if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == FALSE) + { + return is_writable($file); + } + + // For windows servers and safe_mode "on" installations we'll actually + // write a file then read it. Bah... + if (is_dir($file)) + { + $file = rtrim($file, '/').'/'.md5(rand(1,100)); + + if (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) + { + return FALSE; + } + + fclose($fp); + @chmod($file, DIR_WRITE_MODE); + @unlink($file); + return TRUE; + } + elseif (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) + { + return FALSE; + } + + fclose($fp); + return TRUE; +} + +// ------------------------------------------------------------------------ + +/** +* Class registry +* +* This function acts as a singleton. If the requested class does not +* exist it is instantiated and set to a static variable. If it has +* previously been instantiated the variable is returned. +* +* @access public +* @param string the class name being requested +* @param bool optional flag that lets classes get loaded but not instantiated +* @return object +*/ +function &load_class($class, $instantiate = TRUE) +{ + static $objects = array(); + + // Does the class exist? If so, we're done... + if (isset($objects[$class])) + { + return $objects[$class]; + } + + // If the requested class does not exist in the application/libraries + // folder we'll load the native class from the system/libraries folder. + if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT)) + { + require(BASEPATH.'libraries/'.$class.EXT); + require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT); + $is_subclass = TRUE; + } + else + { + if (file_exists(APPPATH.'libraries/'.$class.EXT)) + { + require(APPPATH.'libraries/'.$class.EXT); + $is_subclass = FALSE; + } + else + { + require(BASEPATH.'libraries/'.$class.EXT); + $is_subclass = FALSE; + } + } + + if ($instantiate == FALSE) + { + $objects[$class] = TRUE; + return $objects[$class]; + } + + if ($is_subclass == TRUE) + { + $name = config_item('subclass_prefix').$class; + + $objects[$class] =& instantiate_class(new $name()); + return $objects[$class]; + } + + $name = ($class != 'Controller') ? 'CI_'.$class : $class; + + $objects[$class] =& instantiate_class(new $name()); + return $objects[$class]; +} + +/** + * Instantiate Class + * + * Returns a new class object by reference, used by load_class() and the DB class. + * Required to retain PHP 4 compatibility and also not make PHP 5.3 cry. + * + * Use: $obj =& instantiate_class(new Foo()); + * + * @access public + * @param object + * @return object + */ +function &instantiate_class(&$class_object) +{ + return $class_object; +} + +/** +* Loads the main config.php file +* +* @access private +* @return array +*/ +function &get_config() +{ + static $main_conf; + + if ( ! isset($main_conf)) + { + if ( ! file_exists(APPPATH.'config/config'.EXT)) + { + exit('The configuration file config'.EXT.' does not exist.'); + } + + require(APPPATH.'config/config'.EXT); + + if ( ! isset($config) OR ! is_array($config)) + { + exit('Your config file does not appear to be formatted correctly.'); + } + + $main_conf[0] =& $config; + } + return $main_conf[0]; +} + +/** +* Gets a config item +* +* @access public +* @return mixed +*/ +function config_item($item) +{ + static $config_item = array(); + + if ( ! isset($config_item[$item])) + { + $config =& get_config(); + + if ( ! isset($config[$item])) + { + return FALSE; + } + $config_item[$item] = $config[$item]; + } + + return $config_item[$item]; +} + + +/** +* Error Handler +* +* This function lets us invoke the exception class and +* display errors using the standard error template located +* in application/errors/errors.php +* This function will send the error page directly to the +* browser and exit. +* +* @access public +* @return void +*/ +function show_error($message, $status_code = 500) +{ + $error =& load_class('Exceptions'); + echo $error->show_error('An Error Was Encountered', $message, 'error_general', $status_code); + exit; +} + + +/** +* 404 Page Handler +* +* This function is similar to the show_error() function above +* However, instead of the standard error template it displays +* 404 errors. +* +* @access public +* @return void +*/ +function show_404($page = '') +{ + $error =& load_class('Exceptions'); + $error->show_404($page); + exit; +} + + +/** +* Error Logging Interface +* +* We use this as a simple mechanism to access the logging +* class and send messages to be logged. +* +* @access public +* @return void +*/ +function log_message($level = 'error', $message, $php_error = FALSE) +{ + static $LOG; + + $config =& get_config(); + if ($config['log_threshold'] == 0) + { + return; + } + + $LOG =& load_class('Log'); + $LOG->write_log($level, $message, $php_error); +} + + +/** + * Set HTTP Status Header + * + * @access public + * @param int the status code + * @param string + * @return void + */ +function set_status_header($code = 200, $text = '') +{ + $stati = array( + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + + 400 => 'Bad Request', + 401 => 'Unauthorized', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + ); + + if ($code == '' OR ! is_numeric($code)) + { + show_error('Status codes must be numeric', 500); + } + + if (isset($stati[$code]) AND $text == '') + { + $text = $stati[$code]; + } + + if ($text == '') + { + show_error('No status text available. Please check your status code number or supply your own message text.', 500); + } + + $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : FALSE; + + if (substr(php_sapi_name(), 0, 3) == 'cgi') + { + header("Status: {$code} {$text}", TRUE); + } + elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0') + { + header($server_protocol." {$code} {$text}", TRUE, $code); + } + else + { + header("HTTP/1.1 {$code} {$text}", TRUE, $code); + } +} + + +/** +* Exception Handler +* +* This is the custom exception handler that is declaired at the top +* of Codeigniter.php. The main reason we use this is permit +* PHP errors to be logged in our own log files since we may +* not have access to server logs. Since this function +* effectively intercepts PHP errors, however, we also need +* to display errors based on the current error_reporting level. +* We do that with the use of a PHP error template. +* +* @access private +* @return void +*/ +function _exception_handler($severity, $message, $filepath, $line) +{ + // We don't bother with "strict" notices since they will fill up + // the log file with information that isn't normally very + // helpful. For example, if you are running PHP 5 and you + // use version 4 style class functions (without prefixes + // like "public", "private", etc.) you'll get notices telling + // you that these have been deprecated. + + if ($severity == E_STRICT) + { + return; + } + + $error =& load_class('Exceptions'); + + // Should we display the error? + // We'll get the current error_reporting level and add its bits + // with the severity bits to find out. + + if (($severity & error_reporting()) == $severity) + { + $error->show_php_error($severity, $message, $filepath, $line); + } + + // Should we log the error? No? We're done... + $config =& get_config(); + if ($config['log_threshold'] == 0) + { + return; + } + + $error->log_exception($severity, $message, $filepath, $line); +} + + + +/* End of file Common.php */ +/* Location: ./system/codeigniter/Common.php */ \ No newline at end of file diff --git a/system/codeigniter/Compat.php b/system/codeigniter/Compat.php new file mode 100644 index 000000000..40017a93b --- /dev/null +++ b/system/codeigniter/Compat.php @@ -0,0 +1,93 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/DB.php b/system/database/DB.php new file mode 100644 index 000000000..f174687ca --- /dev/null +++ b/system/database/DB.php @@ -0,0 +1,146 @@ + $dns['scheme'], + 'hostname' => (isset($dns['host'])) ? rawurldecode($dns['host']) : '', + 'username' => (isset($dns['user'])) ? rawurldecode($dns['user']) : '', + 'password' => (isset($dns['pass'])) ? rawurldecode($dns['pass']) : '', + 'database' => (isset($dns['path'])) ? rawurldecode(substr($dns['path'], 1)) : '' + ); + + // were additional config items set? + if (isset($dns['query'])) + { + parse_str($dns['query'], $extra); + + foreach($extra as $key => $val) + { + // booleans please + if (strtoupper($val) == "TRUE") + { + $val = TRUE; + } + elseif (strtoupper($val) == "FALSE") + { + $val = FALSE; + } + + $params[$key] = $val; + } + } + } + + // No DB specified yet? Beat them senseless... + if ( ! isset($params['dbdriver']) OR $params['dbdriver'] == '') + { + show_error('You have not selected a database type to connect to.'); + } + + // Load the DB classes. Note: Since the active record class is optional + // we need to dynamically create a class that extends proper parent class + // based on whether we're using the active record class or not. + // Kudos to Paul for discovering this clever use of eval() + + if ($active_record_override == TRUE) + { + $active_record = TRUE; + } + + require_once(BASEPATH.'database/DB_driver'.EXT); + + if ( ! isset($active_record) OR $active_record == TRUE) + { + require_once(BASEPATH.'database/DB_active_rec'.EXT); + + if ( ! class_exists('CI_DB')) + { + eval('class CI_DB extends CI_DB_active_record { }'); + } + } + else + { + if ( ! class_exists('CI_DB')) + { + eval('class CI_DB extends CI_DB_driver { }'); + } + } + + require_once(BASEPATH.'database/drivers/'.$params['dbdriver'].'/'.$params['dbdriver'].'_driver'.EXT); + + // Instantiate the DB adapter + $driver = 'CI_DB_'.$params['dbdriver'].'_driver'; + $DB =& instantiate_class(new $driver($params)); + + if ($DB->autoinit == TRUE) + { + $DB->initialize(); + } + + return $DB; +} + + + +/* End of file DB.php */ +/* Location: ./system/database/DB.php */ \ No newline at end of file diff --git a/system/database/DB_active_rec.php b/system/database/DB_active_rec.php new file mode 100644 index 000000000..bf4d5117e --- /dev/null +++ b/system/database/DB_active_rec.php @@ -0,0 +1,1820 @@ +_protect_identifiers = $escape; + } + + if (is_string($select)) + { + $select = explode(',', $select); + } + + foreach ($select as $val) + { + $val = trim($val); + + if ($val != '') + { + $this->ar_select[] = $val; + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_select[] = $val; + $this->ar_cache_exists[] = 'select'; + } + } + } + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Select Max + * + * Generates a SELECT MAX(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_max($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'MAX'); + } + + // -------------------------------------------------------------------- + + /** + * Select Min + * + * Generates a SELECT MIN(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_min($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'MIN'); + } + + // -------------------------------------------------------------------- + + /** + * Select Average + * + * Generates a SELECT AVG(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_avg($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'AVG'); + } + + // -------------------------------------------------------------------- + + /** + * Select Sum + * + * Generates a SELECT SUM(field) portion of a query + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function select_sum($select = '', $alias = '') + { + return $this->_max_min_avg_sum($select, $alias, 'SUM'); + } + + // -------------------------------------------------------------------- + + /** + * Processing Function for the four functions above: + * + * select_max() + * select_min() + * select_avg() + * select_sum() + * + * @access public + * @param string the field + * @param string an alias + * @return object + */ + function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX') + { + if ( ! is_string($select) OR $select == '') + { + $this->display_error('db_invalid_query'); + } + + $type = strtoupper($type); + + if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM'))) + { + show_error('Invalid function type: '.$type); + } + + if ($alias == '') + { + $alias = $this->_create_alias_from_table(trim($select)); + } + + $sql = $type.'('.$this->_protect_identifiers(trim($select)).') AS '.$alias; + + $this->ar_select[] = $sql; + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_select[] = $sql; + $this->ar_cache_exists[] = 'select'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Determines the alias name based on the table + * + * @access private + * @param string + * @return string + */ + function _create_alias_from_table($item) + { + if (strpos($item, '.') !== FALSE) + { + return end(explode('.', $item)); + } + + return $item; + } + + // -------------------------------------------------------------------- + + /** + * DISTINCT + * + * Sets a flag which tells the query string compiler to add DISTINCT + * + * @access public + * @param bool + * @return object + */ + function distinct($val = TRUE) + { + $this->ar_distinct = (is_bool($val)) ? $val : TRUE; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * From + * + * Generates the FROM portion of the query + * + * @access public + * @param mixed can be a string or array + * @return object + */ + function from($from) + { + foreach ((array)$from as $val) + { + if (strpos($val, ',') !== FALSE) + { + foreach (explode(',', $val) as $v) + { + $v = trim($v); + $this->_track_aliases($v); + + $this->ar_from[] = $this->_protect_identifiers($v, TRUE, NULL, FALSE); + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_from[] = $this->_protect_identifiers($v, TRUE, NULL, FALSE); + $this->ar_cache_exists[] = 'from'; + } + } + + } + else + { + $val = trim($val); + + // Extract any aliases that might exist. We use this information + // in the _protect_identifiers to know whether to add a table prefix + $this->_track_aliases($val); + + $this->ar_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE); + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_from[] = $this->_protect_identifiers($val, TRUE, NULL, FALSE); + $this->ar_cache_exists[] = 'from'; + } + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Join + * + * Generates the JOIN portion of the query + * + * @access public + * @param string + * @param string the join condition + * @param string the type of join + * @return object + */ + function join($table, $cond, $type = '') + { + if ($type != '') + { + $type = strtoupper(trim($type)); + + if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'))) + { + $type = ''; + } + else + { + $type .= ' '; + } + } + + // Extract any aliases that might exist. We use this information + // in the _protect_identifiers to know whether to add a table prefix + $this->_track_aliases($table); + + // Strip apart the condition and protect the identifiers + if (preg_match('/([\w\.]+)([\W\s]+)(.+)/', $cond, $match)) + { + $match[1] = $this->_protect_identifiers($match[1]); + $match[3] = $this->_protect_identifiers($match[3]); + + $cond = $match[1].$match[2].$match[3]; + } + + // Assemble the JOIN statement + $join = $type.'JOIN '.$this->_protect_identifiers($table, TRUE, NULL, FALSE).' ON '.$cond; + + $this->ar_join[] = $join; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_join[] = $join; + $this->ar_cache_exists[] = 'join'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Where + * + * Generates the WHERE portion of the query. Separates + * multiple calls with AND + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function where($key, $value = NULL, $escape = TRUE) + { + return $this->_where($key, $value, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * OR Where + * + * Generates the WHERE portion of the query. Separates + * multiple calls with OR + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function or_where($key, $value = NULL, $escape = TRUE) + { + return $this->_where($key, $value, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * orwhere() is an alias of or_where() + * this function is here for backwards compatibility, as + * orwhere() has been deprecated + */ + function orwhere($key, $value = NULL, $escape = TRUE) + { + return $this->or_where($key, $value, $escape); + } + + // -------------------------------------------------------------------- + + /** + * Where + * + * Called by where() or orwhere() + * + * @access private + * @param mixed + * @param mixed + * @param string + * @return object + */ + function _where($key, $value = NULL, $type = 'AND ', $escape = NULL) + { + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + // If the escape value was not set will will base it on the global setting + if ( ! is_bool($escape)) + { + $escape = $this->_protect_identifiers; + } + + foreach ($key as $k => $v) + { + $prefix = (count($this->ar_where) == 0 AND count($this->ar_cache_where) == 0) ? '' : $type; + + if (is_null($v) && ! $this->_has_operator($k)) + { + // value appears not to have been set, assign the test to IS NULL + $k .= ' IS NULL'; + } + + if ( ! is_null($v)) + { + if ($escape === TRUE) + { + $k = $this->_protect_identifiers($k, FALSE, $escape); + + $v = ' '.$this->escape($v); + } + + if ( ! $this->_has_operator($k)) + { + $k .= ' ='; + } + } + else + { + $k = $this->_protect_identifiers($k, FALSE, $escape); + } + + $this->ar_where[] = $prefix.$k.$v; + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_where[] = $prefix.$k.$v; + $this->ar_cache_exists[] = 'where'; + } + + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Where_in + * + * Generates a WHERE field IN ('item', 'item') SQL query joined with + * AND if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function where_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values); + } + + // -------------------------------------------------------------------- + + /** + * Where_in_or + * + * Generates a WHERE field IN ('item', 'item') SQL query joined with + * OR if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function or_where_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values, FALSE, 'OR '); + } + + // -------------------------------------------------------------------- + + /** + * Where_not_in + * + * Generates a WHERE field NOT IN ('item', 'item') SQL query joined + * with AND if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function where_not_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Where_not_in_or + * + * Generates a WHERE field NOT IN ('item', 'item') SQL query joined + * with OR if appropriate + * + * @access public + * @param string The field to search + * @param array The values searched on + * @return object + */ + function or_where_not_in($key = NULL, $values = NULL) + { + return $this->_where_in($key, $values, TRUE, 'OR '); + } + + // -------------------------------------------------------------------- + + /** + * Where_in + * + * Called by where_in, where_in_or, where_not_in, where_not_in_or + * + * @access public + * @param string The field to search + * @param array The values searched on + * @param boolean If the statement would be IN or NOT IN + * @param string + * @return object + */ + function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ') + { + if ($key === NULL OR $values === NULL) + { + return; + } + + if ( ! is_array($values)) + { + $values = array($values); + } + + $not = ($not) ? ' NOT' : ''; + + foreach ($values as $value) + { + $this->ar_wherein[] = $this->escape($value); + } + + $prefix = (count($this->ar_where) == 0) ? '' : $type; + + $where_in = $prefix . $this->_protect_identifiers($key) . $not . " IN (" . implode(", ", $this->ar_wherein) . ") "; + + $this->ar_where[] = $where_in; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_where[] = $where_in; + $this->ar_cache_exists[] = 'where'; + } + + // reset the array for multiple calls + $this->ar_wherein = array(); + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Like + * + * Generates a %LIKE% portion of the query. Separates + * multiple calls with AND + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'AND ', $side); + } + + // -------------------------------------------------------------------- + + /** + * Not Like + * + * Generates a NOT LIKE portion of the query. Separates + * multiple calls with AND + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function not_like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'AND ', $side, 'NOT'); + } + + // -------------------------------------------------------------------- + + /** + * OR Like + * + * Generates a %LIKE% portion of the query. Separates + * multiple calls with OR + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function or_like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'OR ', $side); + } + + // -------------------------------------------------------------------- + + /** + * OR Not Like + * + * Generates a NOT LIKE portion of the query. Separates + * multiple calls with OR + * + * @access public + * @param mixed + * @param mixed + * @return object + */ + function or_not_like($field, $match = '', $side = 'both') + { + return $this->_like($field, $match, 'OR ', $side, 'NOT'); + } + + // -------------------------------------------------------------------- + + /** + * orlike() is an alias of or_like() + * this function is here for backwards compatibility, as + * orlike() has been deprecated + */ + function orlike($field, $match = '', $side = 'both') + { + return $this->or_like($field, $match, $side); + } + + // -------------------------------------------------------------------- + + /** + * Like + * + * Called by like() or orlike() + * + * @access private + * @param mixed + * @param mixed + * @param string + * @return object + */ + function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '') + { + if ( ! is_array($field)) + { + $field = array($field => $match); + } + + foreach ($field as $k => $v) + { + $k = $this->_protect_identifiers($k); + + $prefix = (count($this->ar_like) == 0) ? '' : $type; + + $v = $this->escape_like_str($v); + + if ($side == 'before') + { + $like_statement = $prefix." $k $not LIKE '%{$v}'"; + } + elseif ($side == 'after') + { + $like_statement = $prefix." $k $not LIKE '{$v}%'"; + } + else + { + $like_statement = $prefix." $k $not LIKE '%{$v}%'"; + } + + // some platforms require an escape sequence definition for LIKE wildcards + if ($this->_like_escape_str != '') + { + $like_statement = $like_statement.sprintf($this->_like_escape_str, $this->_like_escape_char); + } + + $this->ar_like[] = $like_statement; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_like[] = $like_statement; + $this->ar_cache_exists[] = 'like'; + } + + } + return $this; + } + + // -------------------------------------------------------------------- + + /** + * GROUP BY + * + * @access public + * @param string + * @return object + */ + function group_by($by) + { + if (is_string($by)) + { + $by = explode(',', $by); + } + + foreach ($by as $val) + { + $val = trim($val); + + if ($val != '') + { + $this->ar_groupby[] = $this->_protect_identifiers($val); + + if ($this->ar_caching === TRUE) + { + $this->ar_cache_groupby[] = $this->_protect_identifiers($val); + $this->ar_cache_exists[] = 'groupby'; + } + } + } + return $this; + } + + // -------------------------------------------------------------------- + + /** + * groupby() is an alias of group_by() + * this function is here for backwards compatibility, as + * groupby() has been deprecated + */ + function groupby($by) + { + return $this->group_by($by); + } + + // -------------------------------------------------------------------- + + /** + * Sets the HAVING value + * + * Separates multiple calls with AND + * + * @access public + * @param string + * @param string + * @return object + */ + function having($key, $value = '', $escape = TRUE) + { + return $this->_having($key, $value, 'AND ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * orhaving() is an alias of or_having() + * this function is here for backwards compatibility, as + * orhaving() has been deprecated + */ + + function orhaving($key, $value = '', $escape = TRUE) + { + return $this->or_having($key, $value, $escape); + } + // -------------------------------------------------------------------- + + /** + * Sets the OR HAVING value + * + * Separates multiple calls with OR + * + * @access public + * @param string + * @param string + * @return object + */ + function or_having($key, $value = '', $escape = TRUE) + { + return $this->_having($key, $value, 'OR ', $escape); + } + + // -------------------------------------------------------------------- + + /** + * Sets the HAVING values + * + * Called by having() or or_having() + * + * @access private + * @param string + * @param string + * @return object + */ + function _having($key, $value = '', $type = 'AND ', $escape = TRUE) + { + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + foreach ($key as $k => $v) + { + $prefix = (count($this->ar_having) == 0) ? '' : $type; + + if ($escape === TRUE) + { + $k = $this->_protect_identifiers($k); + } + + if ( ! $this->_has_operator($k)) + { + $k .= ' = '; + } + + if ($v != '') + { + $v = ' '.$this->escape_str($v); + } + + $this->ar_having[] = $prefix.$k.$v; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_having[] = $prefix.$k.$v; + $this->ar_cache_exists[] = 'having'; + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Sets the ORDER BY value + * + * @access public + * @param string + * @param string direction: asc or desc + * @return object + */ + function order_by($orderby, $direction = '') + { + if (strtolower($direction) == 'random') + { + $orderby = ''; // Random results want or don't need a field name + $direction = $this->_random_keyword; + } + elseif (trim($direction) != '') + { + $direction = (in_array(strtoupper(trim($direction)), array('ASC', 'DESC'), TRUE)) ? ' '.$direction : ' ASC'; + } + + + if (strpos($orderby, ',') !== FALSE) + { + $temp = array(); + foreach (explode(',', $orderby) as $part) + { + $part = trim($part); + if ( ! in_array($part, $this->ar_aliased_tables)) + { + $part = $this->_protect_identifiers(trim($part)); + } + + $temp[] = $part; + } + + $orderby = implode(', ', $temp); + } + else if ($direction != $this->_random_keyword) + { + $orderby = $this->_protect_identifiers($orderby); + } + + $orderby_statement = $orderby.$direction; + + $this->ar_orderby[] = $orderby_statement; + if ($this->ar_caching === TRUE) + { + $this->ar_cache_orderby[] = $orderby_statement; + $this->ar_cache_exists[] = 'orderby'; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * orderby() is an alias of order_by() + * this function is here for backwards compatibility, as + * orderby() has been deprecated + */ + function orderby($orderby, $direction = '') + { + return $this->order_by($orderby, $direction); + } + + // -------------------------------------------------------------------- + + /** + * Sets the LIMIT value + * + * @access public + * @param integer the limit value + * @param integer the offset value + * @return object + */ + function limit($value, $offset = '') + { + $this->ar_limit = $value; + + if ($offset != '') + { + $this->ar_offset = $offset; + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Sets the OFFSET value + * + * @access public + * @param integer the offset value + * @return object + */ + function offset($offset) + { + $this->ar_offset = $offset; + return $this; + } + + // -------------------------------------------------------------------- + + /** + * The "set" function. Allows key/value pairs to be set for inserting or updating + * + * @access public + * @param mixed + * @param string + * @param boolean + * @return object + */ + function set($key, $value = '', $escape = TRUE) + { + $key = $this->_object_to_array($key); + + if ( ! is_array($key)) + { + $key = array($key => $value); + } + + foreach ($key as $k => $v) + { + if ($escape === FALSE) + { + $this->ar_set[$this->_protect_identifiers($k)] = $v; + } + else + { + $this->ar_set[$this->_protect_identifiers($k)] = $this->escape($v); + } + } + + return $this; + } + + // -------------------------------------------------------------------- + + /** + * Get + * + * Compiles the select statement based on the other functions called + * and runs the query + * + * @access public + * @param string the table + * @param string the limit clause + * @param string the offset clause + * @return object + */ + function get($table = '', $limit = null, $offset = null) + { + if ($table != '') + { + $this->_track_aliases($table); + $this->from($table); + } + + if ( ! is_null($limit)) + { + $this->limit($limit, $offset); + } + + $sql = $this->_compile_select(); + + $result = $this->query($sql); + $this->_reset_select(); + return $result; + } + + /** + * "Count All Results" query + * + * Generates a platform-specific query string that counts all records + * returned by an Active Record query. + * + * @access public + * @param string + * @return string + */ + function count_all_results($table = '') + { + if ($table != '') + { + $this->_track_aliases($table); + $this->from($table); + } + + $sql = $this->_compile_select($this->_count_string . $this->_protect_identifiers('numrows')); + + $query = $this->query($sql); + $this->_reset_select(); + + if ($query->num_rows() == 0) + { + return '0'; + } + + $row = $query->row(); + return $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Get_Where + * + * Allows the where clause, limit and offset to be added directly + * + * @access public + * @param string the where clause + * @param string the limit clause + * @param string the offset clause + * @return object + */ + function get_where($table = '', $where = null, $limit = null, $offset = null) + { + if ($table != '') + { + $this->from($table); + } + + if ( ! is_null($where)) + { + $this->where($where); + } + + if ( ! is_null($limit)) + { + $this->limit($limit, $offset); + } + + $sql = $this->_compile_select(); + + $result = $this->query($sql); + $this->_reset_select(); + return $result; + } + + // -------------------------------------------------------------------- + + /** + * getwhere() is an alias of get_where() + * this function is here for backwards compatibility, as + * getwhere() has been deprecated + */ + function getwhere($table = '', $where = null, $limit = null, $offset = null) + { + return $this->get_where($table, $where, $limit, $offset); + } + + // -------------------------------------------------------------------- + + /** + * Insert + * + * Compiles an insert string and runs the query + * + * @access public + * @param string the table to retrieve the results from + * @param array an associative array of insert values + * @return object + */ + function insert($table = '', $set = NULL) + { + if ( ! is_null($set)) + { + $this->set($set); + } + + if (count($this->ar_set) == 0) + { + if ($this->db_debug) + { + return $this->display_error('db_must_use_set'); + } + return FALSE; + } + + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + + $sql = $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->ar_set), array_values($this->ar_set)); + + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Update + * + * Compiles an update string and runs the query + * + * @access public + * @param string the table to retrieve the results from + * @param array an associative array of update values + * @param mixed the where clause + * @return object + */ + function update($table = '', $set = NULL, $where = NULL, $limit = NULL) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ( ! is_null($set)) + { + $this->set($set); + } + + if (count($this->ar_set) == 0) + { + if ($this->db_debug) + { + return $this->display_error('db_must_use_set'); + } + return FALSE; + } + + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + + if ($where != NULL) + { + $this->where($where); + } + + if ($limit != NULL) + { + $this->limit($limit); + } + + $sql = $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_set, $this->ar_where, $this->ar_orderby, $this->ar_limit); + + $this->_reset_write(); + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Empty Table + * + * Compiles a delete string and runs "DELETE FROM table" + * + * @access public + * @param string the table to empty + * @return object + */ + function empty_table($table = '') + { + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + else + { + $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); + } + + $sql = $this->_delete($table); + + $this->_reset_write(); + + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Truncate + * + * Compiles a truncate string and runs the query + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table to truncate + * @return object + */ + function truncate($table = '') + { + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + else + { + $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); + } + + $sql = $this->_truncate($table); + + $this->_reset_write(); + + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Delete + * + * Compiles a delete string and runs the query + * + * @access public + * @param mixed the table(s) to delete from. String or array + * @param mixed the where clause + * @param mixed the limit clause + * @param boolean + * @return object + */ + function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + if ($table == '') + { + if ( ! isset($this->ar_from[0])) + { + if ($this->db_debug) + { + return $this->display_error('db_must_set_table'); + } + return FALSE; + } + + $table = $this->ar_from[0]; + } + elseif (is_array($table)) + { + foreach($table as $single_table) + { + $this->delete($single_table, $where, $limit, FALSE); + } + + $this->_reset_write(); + return; + } + else + { + $table = $this->_protect_identifiers($table, TRUE, NULL, FALSE); + } + + if ($where != '') + { + $this->where($where); + } + + if ($limit != NULL) + { + $this->limit($limit); + } + + if (count($this->ar_where) == 0 && count($this->ar_wherein) == 0 && count($this->ar_like) == 0) + { + if ($this->db_debug) + { + return $this->display_error('db_del_must_use_where'); + } + + return FALSE; + } + + $sql = $this->_delete($table, $this->ar_where, $this->ar_like, $this->ar_limit); + + if ($reset_data) + { + $this->_reset_write(); + } + + return $this->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * DB Prefix + * + * Prepends a database prefix if one exists in configuration + * + * @access public + * @param string the table + * @return string + */ + function dbprefix($table = '') + { + if ($table == '') + { + $this->display_error('db_table_name_required'); + } + + return $this->dbprefix.$table; + } + + // -------------------------------------------------------------------- + + /** + * Track Aliases + * + * Used to track SQL statements written with aliased tables. + * + * @access private + * @param string The table to inspect + * @return string + */ + function _track_aliases($table) + { + if (is_array($table)) + { + foreach ($table as $t) + { + $this->_track_aliases($t); + } + return; + } + + // Does the string contain a comma? If so, we need to separate + // the string into discreet statements + if (strpos($table, ',') !== FALSE) + { + return $this->_track_aliases(explode(',', $table)); + } + + // if a table alias is used we can recognize it by a space + if (strpos($table, " ") !== FALSE) + { + // if the alias is written with the AS keyword, remove it + $table = preg_replace('/ AS /i', ' ', $table); + + // Grab the alias + $table = trim(strrchr($table, " ")); + + // Store the alias, if it doesn't already exist + if ( ! in_array($table, $this->ar_aliased_tables)) + { + $this->ar_aliased_tables[] = $table; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Compile the SELECT statement + * + * Generates a query string based on which functions were used. + * Should not be called directly. The get() function calls it. + * + * @access private + * @return string + */ + function _compile_select($select_override = FALSE) + { + // Combine any cached components with the current statements + $this->_merge_cache(); + + // ---------------------------------------------------------------- + + // Write the "select" portion of the query + + if ($select_override !== FALSE) + { + $sql = $select_override; + } + else + { + $sql = ( ! $this->ar_distinct) ? 'SELECT ' : 'SELECT DISTINCT '; + + if (count($this->ar_select) == 0) + { + $sql .= '*'; + } + else + { + // Cycle through the "select" portion of the query and prep each column name. + // The reason we protect identifiers here rather then in the select() function + // is because until the user calls the from() function we don't know if there are aliases + foreach ($this->ar_select as $key => $val) + { + $this->ar_select[$key] = $this->_protect_identifiers($val); + } + + $sql .= implode(', ', $this->ar_select); + } + } + + // ---------------------------------------------------------------- + + // Write the "FROM" portion of the query + + if (count($this->ar_from) > 0) + { + $sql .= "\nFROM "; + + $sql .= $this->_from_tables($this->ar_from); + } + + // ---------------------------------------------------------------- + + // Write the "JOIN" portion of the query + + if (count($this->ar_join) > 0) + { + $sql .= "\n"; + + $sql .= implode("\n", $this->ar_join); + } + + // ---------------------------------------------------------------- + + // Write the "WHERE" portion of the query + + if (count($this->ar_where) > 0 OR count($this->ar_like) > 0) + { + $sql .= "\n"; + + $sql .= "WHERE "; + } + + $sql .= implode("\n", $this->ar_where); + + // ---------------------------------------------------------------- + + // Write the "LIKE" portion of the query + + if (count($this->ar_like) > 0) + { + if (count($this->ar_where) > 0) + { + $sql .= "\nAND "; + } + + $sql .= implode("\n", $this->ar_like); + } + + // ---------------------------------------------------------------- + + // Write the "GROUP BY" portion of the query + + if (count($this->ar_groupby) > 0) + { + $sql .= "\nGROUP BY "; + + $sql .= implode(', ', $this->ar_groupby); + } + + // ---------------------------------------------------------------- + + // Write the "HAVING" portion of the query + + if (count($this->ar_having) > 0) + { + $sql .= "\nHAVING "; + $sql .= implode("\n", $this->ar_having); + } + + // ---------------------------------------------------------------- + + // Write the "ORDER BY" portion of the query + + if (count($this->ar_orderby) > 0) + { + $sql .= "\nORDER BY "; + $sql .= implode(', ', $this->ar_orderby); + + if ($this->ar_order !== FALSE) + { + $sql .= ($this->ar_order == 'desc') ? ' DESC' : ' ASC'; + } + } + + // ---------------------------------------------------------------- + + // Write the "LIMIT" portion of the query + + if (is_numeric($this->ar_limit)) + { + $sql .= "\n"; + $sql = $this->_limit($sql, $this->ar_limit, $this->ar_offset); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Object to Array + * + * Takes an object as input and converts the class variables to array key/vals + * + * @access public + * @param object + * @return array + */ + function _object_to_array($object) + { + if ( ! is_object($object)) + { + return $object; + } + + $array = array(); + foreach (get_object_vars($object) as $key => $val) + { + // There are some built in keys we need to ignore for this conversion + if ( ! is_object($val) && ! is_array($val) && $key != '_parent_name' && $key != '_ci_scaffolding' && $key != '_ci_scaff_table') + { + $array[$key] = $val; + } + } + + return $array; + } + + // -------------------------------------------------------------------- + + /** + * Start Cache + * + * Starts AR caching + * + * @access public + * @return void + */ + function start_cache() + { + $this->ar_caching = TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Stop Cache + * + * Stops AR caching + * + * @access public + * @return void + */ + function stop_cache() + { + $this->ar_caching = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Flush Cache + * + * Empties the AR cache + * + * @access public + * @return void + */ + function flush_cache() + { + $this->_reset_run( + array( + 'ar_cache_select' => array(), + 'ar_cache_from' => array(), + 'ar_cache_join' => array(), + 'ar_cache_where' => array(), + 'ar_cache_like' => array(), + 'ar_cache_groupby' => array(), + 'ar_cache_having' => array(), + 'ar_cache_orderby' => array(), + 'ar_cache_set' => array(), + 'ar_cache_exists' => array() + ) + ); + } + + // -------------------------------------------------------------------- + + /** + * Merge Cache + * + * When called, this function merges any cached AR arrays with + * locally called ones. + * + * @access private + * @return void + */ + function _merge_cache() + { + if (count($this->ar_cache_exists) == 0) + { + return; + } + + foreach ($this->ar_cache_exists as $val) + { + $ar_variable = 'ar_'.$val; + $ar_cache_var = 'ar_cache_'.$val; + + if (count($this->$ar_cache_var) == 0) + { + continue; + } + + $this->$ar_variable = array_unique(array_merge($this->$ar_cache_var, $this->$ar_variable)); + } + + // If we are "protecting identifiers" we need to examine the "from" + // portion of the query to determine if there are any aliases + if ($this->_protect_identifiers === TRUE AND count($this->ar_cache_from) > 0) + { + $this->_track_aliases($this->ar_from); + } + } + + // -------------------------------------------------------------------- + + /** + * Resets the active record values. Called by the get() function + * + * @access private + * @param array An array of fields to reset + * @return void + */ + function _reset_run($ar_reset_items) + { + foreach ($ar_reset_items as $item => $default_value) + { + if ( ! in_array($item, $this->ar_store_array)) + { + $this->$item = $default_value; + } + } + } + + // -------------------------------------------------------------------- + + /** + * Resets the active record values. Called by the get() function + * + * @access private + * @return void + */ + function _reset_select() + { + $ar_reset_items = array( + 'ar_select' => array(), + 'ar_from' => array(), + 'ar_join' => array(), + 'ar_where' => array(), + 'ar_like' => array(), + 'ar_groupby' => array(), + 'ar_having' => array(), + 'ar_orderby' => array(), + 'ar_wherein' => array(), + 'ar_aliased_tables' => array(), + 'ar_distinct' => FALSE, + 'ar_limit' => FALSE, + 'ar_offset' => FALSE, + 'ar_order' => FALSE, + ); + + $this->_reset_run($ar_reset_items); + } + + // -------------------------------------------------------------------- + + /** + * Resets the active record "write" values. + * + * Called by the insert() update() and delete() functions + * + * @access private + * @return void + */ + function _reset_write() + { + $ar_reset_items = array( + 'ar_set' => array(), + 'ar_from' => array(), + 'ar_where' => array(), + 'ar_like' => array(), + 'ar_orderby' => array(), + 'ar_limit' => FALSE, + 'ar_order' => FALSE + ); + + $this->_reset_run($ar_reset_items); + } + +} + +/* End of file DB_active_rec.php */ +/* Location: ./system/database/DB_active_rec.php */ \ No newline at end of file diff --git a/system/database/DB_cache.php b/system/database/DB_cache.php new file mode 100644 index 000000000..d3968b315 --- /dev/null +++ b/system/database/DB_cache.php @@ -0,0 +1,195 @@ +CI + // and load the file helper since we use it a lot + $this->CI =& get_instance(); + $this->db =& $db; + $this->CI->load->helper('file'); + } + + // -------------------------------------------------------------------- + + /** + * Set Cache Directory Path + * + * @access public + * @param string the path to the cache directory + * @return bool + */ + function check_path($path = '') + { + if ($path == '') + { + if ($this->db->cachedir == '') + { + return $this->db->cache_off(); + } + + $path = $this->db->cachedir; + } + + // Add a trailing slash to the path if needed + $path = preg_replace("/(.+?)\/*$/", "\\1/", $path); + + if ( ! is_dir($path) OR ! is_really_writable($path)) + { + // If the path is wrong we'll turn off caching + return $this->db->cache_off(); + } + + $this->db->cachedir = $path; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Retrieve a cached query + * + * The URI being requested will become the name of the cache sub-folder. + * An MD5 hash of the SQL statement will become the cache file name + * + * @access public + * @return string + */ + function read($sql) + { + if ( ! $this->check_path()) + { + return $this->db->cache_off(); + } + + $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); + + $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); + + $filepath = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'.md5($sql); + + if (FALSE === ($cachedata = read_file($filepath))) + { + return FALSE; + } + + return unserialize($cachedata); + } + + // -------------------------------------------------------------------- + + /** + * Write a query to a cache file + * + * @access public + * @return bool + */ + function write($sql, $object) + { + if ( ! $this->check_path()) + { + return $this->db->cache_off(); + } + + $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); + + $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); + + $dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'; + + $filename = md5($sql); + + if ( ! @is_dir($dir_path)) + { + if ( ! @mkdir($dir_path, DIR_WRITE_MODE)) + { + return FALSE; + } + + @chmod($dir_path, DIR_WRITE_MODE); + } + + if (write_file($dir_path.$filename, serialize($object)) === FALSE) + { + return FALSE; + } + + @chmod($dir_path.$filename, DIR_WRITE_MODE); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Delete cache files within a particular directory + * + * @access public + * @return bool + */ + function delete($segment_one = '', $segment_two = '') + { + if ($segment_one == '') + { + $segment_one = ($this->CI->uri->segment(1) == FALSE) ? 'default' : $this->CI->uri->segment(1); + } + + if ($segment_two == '') + { + $segment_two = ($this->CI->uri->segment(2) == FALSE) ? 'index' : $this->CI->uri->segment(2); + } + + $dir_path = $this->db->cachedir.$segment_one.'+'.$segment_two.'/'; + + delete_files($dir_path, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Delete all existing cache files + * + * @access public + * @return bool + */ + function delete_all() + { + delete_files($this->db->cachedir, TRUE); + } + +} + + +/* End of file DB_cache.php */ +/* Location: ./system/database/DB_cache.php */ \ No newline at end of file diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php new file mode 100644 index 000000000..b2a3c2ebd --- /dev/null +++ b/system/database/DB_driver.php @@ -0,0 +1,1366 @@ + $val) + { + $this->$key = $val; + } + } + + log_message('debug', 'Database Driver Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * Initialize Database Settings + * + * @access private Called by the constructor + * @param mixed + * @return void + */ + function initialize() + { + // If an existing connection resource is available + // there is no need to connect and select the database + if (is_resource($this->conn_id) OR is_object($this->conn_id)) + { + return TRUE; + } + + // ---------------------------------------------------------------- + + // Connect to the database and set the connection ID + $this->conn_id = ($this->pconnect == FALSE) ? $this->db_connect() : $this->db_pconnect(); + + // No connection resource? Throw an error + if ( ! $this->conn_id) + { + log_message('error', 'Unable to connect to the database'); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_connect'); + } + return FALSE; + } + + // ---------------------------------------------------------------- + + // Select the DB... assuming a database name is specified in the config file + if ($this->database != '') + { + if ( ! $this->db_select()) + { + log_message('error', 'Unable to select database: '.$this->database); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_select', $this->database); + } + return FALSE; + } + else + { + // We've selected the DB. Now we set the character set + if ( ! $this->db_set_charset($this->char_set, $this->dbcollat)) + { + return FALSE; + } + + return TRUE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + if ( ! $this->_db_set_charset($this->char_set, $this->dbcollat)) + { + log_message('error', 'Unable to set database connection charset: '.$this->char_set); + + if ($this->db_debug) + { + $this->display_error('db_unable_to_set_charset', $this->char_set); + } + + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * The name of the platform in use (mysql, mssql, etc...) + * + * @access public + * @return string + */ + function platform() + { + return $this->dbdriver; + } + + // -------------------------------------------------------------------- + + /** + * Database Version Number. Returns a string containing the + * version of the database being used + * + * @access public + * @return string + */ + function version() + { + if (FALSE === ($sql = $this->_version())) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + + if ($this->dbdriver == 'oci8') + { + return $sql; + } + + $query = $this->query($sql); + return $query->row('ver'); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * Accepts an SQL string as input and returns a result object upon + * successful execution of a "read" type query. Returns boolean TRUE + * upon successful execution of a "write" type query. Returns boolean + * FALSE upon failure, and if the $db_debug variable is set to TRUE + * will raise an error. + * + * @access public + * @param string An SQL query string + * @param array An array of binding data + * @return mixed + */ + function query($sql, $binds = FALSE, $return_object = TRUE) + { + if ($sql == '') + { + if ($this->db_debug) + { + log_message('error', 'Invalid query: '.$sql); + return $this->display_error('db_invalid_query'); + } + return FALSE; + } + + // Verify table prefix and replace if necessary + if ( ($this->dbprefix != '' AND $this->swap_pre != '') AND ($this->dbprefix != $this->swap_pre) ) + { + $sql = preg_replace("/(\W)".$this->swap_pre."(\S+?)/", "\\1".$this->dbprefix."\\2", $sql); + } + + // Is query caching enabled? If the query is a "read type" + // we will load the caching class and return the previously + // cached query if it exists + if ($this->cache_on == TRUE AND stristr($sql, 'SELECT')) + { + if ($this->_cache_init()) + { + $this->load_rdriver(); + if (FALSE !== ($cache = $this->CACHE->read($sql))) + { + return $cache; + } + } + } + + // Compile binds if needed + if ($binds !== FALSE) + { + $sql = $this->compile_binds($sql, $binds); + } + + // Save the query for debugging + if ($this->save_queries == TRUE) + { + $this->queries[] = $sql; + } + + // Start the Query Timer + $time_start = list($sm, $ss) = explode(' ', microtime()); + + // Run the Query + if (FALSE === ($this->result_id = $this->simple_query($sql))) + { + if ($this->save_queries == TRUE) + { + $this->query_times[] = 0; + } + + // This will trigger a rollback if transactions are being used + $this->_trans_status = FALSE; + + if ($this->db_debug) + { + // grab the error number and message now, as we might run some + // additional queries before displaying the error + $error_no = $this->_error_number(); + $error_msg = $this->_error_message(); + + // We call this function in order to roll-back queries + // if transactions are enabled. If we don't call this here + // the error message will trigger an exit, causing the + // transactions to remain in limbo. + $this->trans_complete(); + + // Log and display errors + log_message('error', 'Query error: '.$error_msg); + return $this->display_error( + array( + 'Error Number: '.$error_no, + $error_msg, + $sql + ) + ); + } + + return FALSE; + } + + // Stop and aggregate the query time results + $time_end = list($em, $es) = explode(' ', microtime()); + $this->benchmark += ($em + $es) - ($sm + $ss); + + if ($this->save_queries == TRUE) + { + $this->query_times[] = ($em + $es) - ($sm + $ss); + } + + // Increment the query counter + $this->query_count++; + + // Was the query a "write" type? + // If so we'll simply return true + if ($this->is_write_type($sql) === TRUE) + { + // If caching is enabled we'll auto-cleanup any + // existing files related to this particular URI + if ($this->cache_on == TRUE AND $this->cache_autodel == TRUE AND $this->_cache_init()) + { + $this->CACHE->delete(); + } + + return TRUE; + } + + // Return TRUE if we don't need to create a result object + // Currently only the Oracle driver uses this when stored + // procedures are used + if ($return_object !== TRUE) + { + return TRUE; + } + + // Load and instantiate the result driver + + $driver = $this->load_rdriver(); + $RES = new $driver(); + $RES->conn_id = $this->conn_id; + $RES->result_id = $this->result_id; + + if ($this->dbdriver == 'oci8') + { + $RES->stmt_id = $this->stmt_id; + $RES->curs_id = NULL; + $RES->limit_used = $this->limit_used; + $this->stmt_id = FALSE; + } + + // oci8 vars must be set before calling this + $RES->num_rows = $RES->num_rows(); + + // Is query caching enabled? If so, we'll serialize the + // result object and save it to a cache file. + if ($this->cache_on == TRUE AND $this->_cache_init()) + { + // We'll create a new instance of the result object + // only without the platform specific driver since + // we can't use it with cached data (the query result + // resource ID won't be any good once we've cached the + // result object, so we'll have to compile the data + // and save it) + $CR = new CI_DB_result(); + $CR->num_rows = $RES->num_rows(); + $CR->result_object = $RES->result_object(); + $CR->result_array = $RES->result_array(); + + // Reset these since cached objects can not utilize resource IDs. + $CR->conn_id = NULL; + $CR->result_id = NULL; + + $this->CACHE->write($sql, $CR); + } + + return $RES; + } + + // -------------------------------------------------------------------- + + /** + * Load the result drivers + * + * @access public + * @return string the name of the result class + */ + function load_rdriver() + { + $driver = 'CI_DB_'.$this->dbdriver.'_result'; + + if ( ! class_exists($driver)) + { + include_once(BASEPATH.'database/DB_result'.EXT); + include_once(BASEPATH.'database/drivers/'.$this->dbdriver.'/'.$this->dbdriver.'_result'.EXT); + } + + return $driver; + } + + // -------------------------------------------------------------------- + + /** + * Simple Query + * This is a simplified version of the query() function. Internally + * we only use it when running transaction commands since they do + * not require all the features of the main query() function. + * + * @access public + * @param string the sql query + * @return mixed + */ + function simple_query($sql) + { + if ( ! $this->conn_id) + { + $this->initialize(); + } + + return $this->_execute($sql); + } + + // -------------------------------------------------------------------- + + /** + * Disable Transactions + * This permits transactions to be disabled at run-time. + * + * @access public + * @return void + */ + function trans_off() + { + $this->trans_enabled = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Enable/disable Transaction Strict Mode + * When strict mode is enabled, if you are running multiple groups of + * transactions, if one group fails all groups will be rolled back. + * If strict mode is disabled, each group is treated autonomously, meaning + * a failure of one group will not affect any others + * + * @access public + * @return void + */ + function trans_strict($mode = TRUE) + { + $this->trans_strict = is_bool($mode) ? $mode : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Start Transaction + * + * @access public + * @return void + */ + function trans_start($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + $this->_trans_depth += 1; + return; + } + + $this->trans_begin($test_mode); + } + + // -------------------------------------------------------------------- + + /** + * Complete Transaction + * + * @access public + * @return bool + */ + function trans_complete() + { + if ( ! $this->trans_enabled) + { + return FALSE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 1) + { + $this->_trans_depth -= 1; + return TRUE; + } + + // The query() function will set this flag to FALSE in the event that a query failed + if ($this->_trans_status === FALSE) + { + $this->trans_rollback(); + + // If we are NOT running in strict mode, we will reset + // the _trans_status flag so that subsequent groups of transactions + // will be permitted. + if ($this->trans_strict === FALSE) + { + $this->_trans_status = TRUE; + } + + log_message('debug', 'DB Transaction Failure'); + return FALSE; + } + + $this->trans_commit(); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Lets you retrieve the transaction flag to determine if it has failed + * + * @access public + * @return bool + */ + function trans_status() + { + return $this->_trans_status; + } + + // -------------------------------------------------------------------- + + /** + * Compile Bindings + * + * @access public + * @param string the sql statement + * @param array an array of bind data + * @return string + */ + function compile_binds($sql, $binds) + { + if (strpos($sql, $this->bind_marker) === FALSE) + { + return $sql; + } + + if ( ! is_array($binds)) + { + $binds = array($binds); + } + + // Get the sql segments around the bind markers + $segments = explode($this->bind_marker, $sql); + + // The count of bind should be 1 less then the count of segments + // If there are more bind arguments trim it down + if (count($binds) >= count($segments)) { + $binds = array_slice($binds, 0, count($segments)-1); + } + + // Construct the binded query + $result = $segments[0]; + $i = 0; + foreach ($binds as $bind) + { + $result .= $this->escape($bind); + $result .= $segments[++$i]; + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Determines if a query is a "write" type. + * + * @access public + * @param string An SQL query string + * @return boolean + */ + function is_write_type($sql) + { + if ( ! preg_match('/^\s*"?(SET|INSERT|UPDATE|DELETE|REPLACE|CREATE|DROP|TRUNCATE|LOAD DATA|COPY|ALTER|GRANT|REVOKE|LOCK|UNLOCK)\s+/i', $sql)) + { + return FALSE; + } + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Calculate the aggregate query elapsed time + * + * @access public + * @param integer The number of decimal places + * @return integer + */ + function elapsed_time($decimals = 6) + { + return number_format($this->benchmark, $decimals); + } + + // -------------------------------------------------------------------- + + /** + * Returns the total number of queries + * + * @access public + * @return integer + */ + function total_queries() + { + return $this->query_count; + } + + // -------------------------------------------------------------------- + + /** + * Returns the last query that was executed + * + * @access public + * @return void + */ + function last_query() + { + return end($this->queries); + } + + // -------------------------------------------------------------------- + + /** + * "Smart" Escape String + * + * Escapes data based on type + * Sets boolean and null types + * + * @access public + * @param string + * @return mixed + */ + function escape($str) + { + if (is_string($str)) + { + $str = "'".$this->escape_str($str)."'"; + } + elseif (is_bool($str)) + { + $str = ($str === FALSE) ? 0 : 1; + } + elseif (is_null($str)) + { + $str = 'NULL'; + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Escape LIKE String + * + * Calls the individual driver for platform + * specific escaping for LIKE conditions + * + * @access public + * @param string + * @return mixed + */ + function escape_like_str($str) + { + return $this->escape_str($str, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Primary + * + * Retrieves the primary key. It assumes that the row in the first + * position is the primary key + * + * @access public + * @param string the table name + * @return string + */ + function primary($table = '') + { + $fields = $this->list_fields($table); + + if ( ! is_array($fields)) + { + return FALSE; + } + + return current($fields); + } + + // -------------------------------------------------------------------- + + /** + * Returns an array of table names + * + * @access public + * @return array + */ + function list_tables($constrain_by_prefix = FALSE) + { + // Is there a cached result? + if (isset($this->data_cache['table_names'])) + { + return $this->data_cache['table_names']; + } + + if (FALSE === ($sql = $this->_list_tables($constrain_by_prefix))) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + + $retval = array(); + $query = $this->query($sql); + + if ($query->num_rows() > 0) + { + foreach($query->result_array() as $row) + { + if (isset($row['TABLE_NAME'])) + { + $retval[] = $row['TABLE_NAME']; + } + else + { + $retval[] = array_shift($row); + } + } + } + + $this->data_cache['table_names'] = $retval; + return $this->data_cache['table_names']; + } + + // -------------------------------------------------------------------- + + /** + * Determine if a particular table exists + * @access public + * @return boolean + */ + function table_exists($table_name) + { + return ( ! in_array($this->_protect_identifiers($table_name, TRUE, FALSE, FALSE), $this->list_tables())) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Fetch MySQL Field Names + * + * @access public + * @param string the table name + * @return array + */ + function list_fields($table = '') + { + // Is there a cached result? + if (isset($this->data_cache['field_names'][$table])) + { + return $this->data_cache['field_names'][$table]; + } + + if ($table == '') + { + if ($this->db_debug) + { + return $this->display_error('db_field_param_missing'); + } + return FALSE; + } + + if (FALSE === ($sql = $this->_list_columns($this->_protect_identifiers($table, TRUE, NULL, FALSE)))) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + + $query = $this->query($sql); + + $retval = array(); + foreach($query->result_array() as $row) + { + if (isset($row['COLUMN_NAME'])) + { + $retval[] = $row['COLUMN_NAME']; + } + else + { + $retval[] = current($row); + } + } + + $this->data_cache['field_names'][$table] = $retval; + return $this->data_cache['field_names'][$table]; + } + + // -------------------------------------------------------------------- + + /** + * Determine if a particular field exists + * @access public + * @param string + * @param string + * @return boolean + */ + function field_exists($field_name, $table_name) + { + return ( ! in_array($field_name, $this->list_fields($table_name))) ? FALSE : TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Returns an object with field data + * + * @access public + * @param string the table name + * @return object + */ + function field_data($table = '') + { + if ($table == '') + { + if ($this->db_debug) + { + return $this->display_error('db_field_param_missing'); + } + return FALSE; + } + + $query = $this->query($this->_field_data($this->_protect_identifiers($table, TRUE, NULL, FALSE))); + + return $query->field_data(); + } + + // -------------------------------------------------------------------- + + /** + * Generate an insert string + * + * @access public + * @param string the table upon which the query will be performed + * @param array an associative array data of key/values + * @return string + */ + function insert_string($table, $data) + { + $fields = array(); + $values = array(); + + foreach($data as $key => $val) + { + $fields[] = $this->_escape_identifiers($key); + $values[] = $this->escape($val); + } + + return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values); + } + + // -------------------------------------------------------------------- + + /** + * Generate an update string + * + * @access public + * @param string the table upon which the query will be performed + * @param array an associative array data of key/values + * @param mixed the "where" statement + * @return string + */ + function update_string($table, $data, $where) + { + if ($where == '') + { + return false; + } + + $fields = array(); + foreach($data as $key => $val) + { + $fields[$this->_protect_identifiers($key)] = $this->escape($val); + } + + if ( ! is_array($where)) + { + $dest = array($where); + } + else + { + $dest = array(); + foreach ($where as $key => $val) + { + $prefix = (count($dest) == 0) ? '' : ' AND '; + + if ($val !== '') + { + if ( ! $this->_has_operator($key)) + { + $key .= ' ='; + } + + $val = ' '.$this->escape($val); + } + + $dest[] = $prefix.$key.$val; + } + } + + return $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $dest); + } + + // -------------------------------------------------------------------- + + /** + * Tests whether the string has an SQL operator + * + * @access private + * @param string + * @return bool + */ + function _has_operator($str) + { + $str = trim($str); + if ( ! preg_match("/(\s|<|>|!|=|is null|is not null)/i", $str)) + { + return FALSE; + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Enables a native PHP function to be run, using a platform agnostic wrapper. + * + * @access public + * @param string the function name + * @param mixed any parameters needed by the function + * @return mixed + */ + function call_function($function) + { + $driver = ($this->dbdriver == 'postgre') ? 'pg_' : $this->dbdriver.'_'; + + if (FALSE === strpos($driver, $function)) + { + $function = $driver.$function; + } + + if ( ! function_exists($function)) + { + if ($this->db_debug) + { + return $this->display_error('db_unsupported_function'); + } + return FALSE; + } + else + { + $args = (func_num_args() > 1) ? array_splice(func_get_args(), 1) : null; + + return call_user_func_array($function, $args); + } + } + + // -------------------------------------------------------------------- + + /** + * Set Cache Directory Path + * + * @access public + * @param string the path to the cache directory + * @return void + */ + function cache_set_path($path = '') + { + $this->cachedir = $path; + } + + // -------------------------------------------------------------------- + + /** + * Enable Query Caching + * + * @access public + * @return void + */ + function cache_on() + { + $this->cache_on = TRUE; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Disable Query Caching + * + * @access public + * @return void + */ + function cache_off() + { + $this->cache_on = FALSE; + return FALSE; + } + + + // -------------------------------------------------------------------- + + /** + * Delete the cache files associated with a particular URI + * + * @access public + * @return void + */ + function cache_delete($segment_one = '', $segment_two = '') + { + if ( ! $this->_cache_init()) + { + return FALSE; + } + return $this->CACHE->delete($segment_one, $segment_two); + } + + // -------------------------------------------------------------------- + + /** + * Delete All cache files + * + * @access public + * @return void + */ + function cache_delete_all() + { + if ( ! $this->_cache_init()) + { + return FALSE; + } + + return $this->CACHE->delete_all(); + } + + // -------------------------------------------------------------------- + + /** + * Initialize the Cache Class + * + * @access private + * @return void + */ + function _cache_init() + { + if (is_object($this->CACHE) AND class_exists('CI_DB_Cache')) + { + return TRUE; + } + + if ( ! class_exists('CI_DB_Cache')) + { + if ( ! @include(BASEPATH.'database/DB_cache'.EXT)) + { + return $this->cache_off(); + } + } + + $this->CACHE = new CI_DB_Cache($this); // pass db object to support multiple db connections and returned db objects + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @return void + */ + function close() + { + if (is_resource($this->conn_id) OR is_object($this->conn_id)) + { + $this->_close($this->conn_id); + } + $this->conn_id = FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Display an error message + * + * @access public + * @param string the error message + * @param string any "swap" values + * @param boolean whether to localize the message + * @return string sends the application/error_db.php template + */ + function display_error($error = '', $swap = '', $native = FALSE) + { + $LANG =& load_class('Language'); + $LANG->load('db'); + + $heading = $LANG->line('db_error_heading'); + + if ($native == TRUE) + { + $message = $error; + } + else + { + $message = ( ! is_array($error)) ? array(str_replace('%s', $swap, $LANG->line($error))) : $error; + } + + $error =& load_class('Exceptions'); + echo $error->show_error($heading, $message, 'error_db'); + exit; + } + + // -------------------------------------------------------------------- + + /** + * Protect Identifiers + * + * This function adds backticks if appropriate based on db type + * + * @access private + * @param mixed the item to escape + * @return mixed the item with backticks + */ + function protect_identifiers($item, $prefix_single = FALSE) + { + return $this->_protect_identifiers($item, $prefix_single); + } + + // -------------------------------------------------------------------- + + /** + * Protect Identifiers + * + * This function is used extensively by the Active Record class, and by + * a couple functions in this class. + * It takes a column or table name (optionally with an alias) and inserts + * the table prefix onto it. Some logic is necessary in order to deal with + * column names that include the path. Consider a query like this: + * + * SELECT * FROM hostname.database.table.column AS c FROM hostname.database.table + * + * Or a query with aliasing: + * + * SELECT m.member_id, m.member_name FROM members AS m + * + * Since the column name can include up to four segments (host, DB, table, column) + * or also have an alias prefix, we need to do a bit of work to figure this out and + * insert the table prefix (if it exists) in the proper position, and escape only + * the correct identifiers. + * + * @access private + * @param string + * @param bool + * @param mixed + * @param bool + * @return string + */ + function _protect_identifiers($item, $prefix_single = FALSE, $protect_identifiers = NULL, $field_exists = TRUE) + { + if ( ! is_bool($protect_identifiers)) + { + $protect_identifiers = $this->_protect_identifiers; + } + + if (is_array($item)) + { + $escaped_array = array(); + + foreach($item as $k => $v) + { + $escaped_array[$this->_protect_identifiers($k)] = $this->_protect_identifiers($v); + } + + return $escaped_array; + } + + // Convert tabs or multiple spaces into single spaces + $item = preg_replace('/[\t ]+/', ' ', $item); + + // If the item has an alias declaration we remove it and set it aside. + // Basically we remove everything to the right of the first space + $alias = ''; + if (strpos($item, ' ') !== FALSE) + { + $alias = strstr($item, " "); + $item = substr($item, 0, - strlen($alias)); + } + + // This is basically a bug fix for queries that use MAX, MIN, etc. + // If a parenthesis is found we know that we do not need to + // escape the data or add a prefix. There's probably a more graceful + // way to deal with this, but I'm not thinking of it -- Rick + if (strpos($item, '(') !== FALSE) + { + return $item.$alias; + } + + // Break the string apart if it contains periods, then insert the table prefix + // in the correct location, assuming the period doesn't indicate that we're dealing + // with an alias. While we're at it, we will escape the components + if (strpos($item, '.') !== FALSE) + { + $parts = explode('.', $item); + + // Does the first segment of the exploded item match + // one of the aliases previously identified? If so, + // we have nothing more to do other than escape the item + if (in_array($parts[0], $this->ar_aliased_tables)) + { + if ($protect_identifiers === TRUE) + { + foreach ($parts as $key => $val) + { + if ( ! in_array($val, $this->_reserved_identifiers)) + { + $parts[$key] = $this->_escape_identifiers($val); + } + } + + $item = implode('.', $parts); + } + return $item.$alias; + } + + // Is there a table prefix defined in the config file? If not, no need to do anything + if ($this->dbprefix != '') + { + // We now add the table prefix based on some logic. + // Do we have 4 segments (hostname.database.table.column)? + // If so, we add the table prefix to the column name in the 3rd segment. + if (isset($parts[3])) + { + $i = 2; + } + // Do we have 3 segments (database.table.column)? + // If so, we add the table prefix to the column name in 2nd position + elseif (isset($parts[2])) + { + $i = 1; + } + // Do we have 2 segments (table.column)? + // If so, we add the table prefix to the column name in 1st segment + else + { + $i = 0; + } + + // This flag is set when the supplied $item does not contain a field name. + // This can happen when this function is being called from a JOIN. + if ($field_exists == FALSE) + { + $i++; + } + + // Verify table prefix and replace if necessary + if ($this->swap_pre != '' && strncmp($parts[$i], $this->swap_pre, strlen($this->swap_pre)) === 0) + { + $parts[$i] = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $parts[$i]); + } + + // We only add the table prefix if it does not already exist + if (substr($parts[$i], 0, strlen($this->dbprefix)) != $this->dbprefix) + { + $parts[$i] = $this->dbprefix.$parts[$i]; + } + + // Put the parts back together + $item = implode('.', $parts); + } + + if ($protect_identifiers === TRUE) + { + $item = $this->_escape_identifiers($item); + } + + return $item.$alias; + } + + // Is there a table prefix? If not, no need to insert it + if ($this->dbprefix != '') + { + // Verify table prefix and replace if necessary + if ($this->swap_pre != '' && strncmp($item, $this->swap_pre, strlen($this->swap_pre)) === 0) + { + $item = preg_replace("/^".$this->swap_pre."(\S+?)/", $this->dbprefix."\\1", $item); + } + + // Do we prefix an item with no segments? + if ($prefix_single == TRUE AND substr($item, 0, strlen($this->dbprefix)) != $this->dbprefix) + { + $item = $this->dbprefix.$item; + } + } + + if ($protect_identifiers === TRUE AND ! in_array($item, $this->_reserved_identifiers)) + { + $item = $this->_escape_identifiers($item); + } + + return $item.$alias; + } + + +} + + +/* End of file DB_driver.php */ +/* Location: ./system/database/DB_driver.php */ \ No newline at end of file diff --git a/system/database/DB_forge.php b/system/database/DB_forge.php new file mode 100644 index 000000000..c5931db27 --- /dev/null +++ b/system/database/DB_forge.php @@ -0,0 +1,375 @@ +db + $CI =& get_instance(); + $this->db =& $CI->db; + log_message('debug', "Database Forge Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * Create database + * + * @access public + * @param string the database name + * @return bool + */ + function create_database($db_name) + { + $sql = $this->_create_database($db_name); + + if (is_bool($sql)) + { + return $sql; + } + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access public + * @param string the database name + * @return bool + */ + function drop_database($db_name) + { + $sql = $this->_drop_database($db_name); + + if (is_bool($sql)) + { + return $sql; + } + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Add Key + * + * @access public + * @param string key + * @param string type + * @return void + */ + function add_key($key = '', $primary = FALSE) + { + if (is_array($key)) + { + foreach($key as $one) + { + $this->add_key($one, $primary); + } + + return; + } + + if ($key == '') + { + show_error('Key information is required for that operation.'); + } + + if ($primary === TRUE) + { + $this->primary_keys[] = $key; + } + else + { + $this->keys[] = $key; + } + } + + // -------------------------------------------------------------------- + + /** + * Add Field + * + * @access public + * @param string collation + * @return void + */ + function add_field($field = '') + { + if ($field == '') + { + show_error('Field information is required.'); + } + + if (is_string($field)) + { + if ($field == 'id') + { + $this->add_field(array( + 'id' => array( + 'type' => 'INT', + 'constraint' => 9, + 'auto_increment' => TRUE + ) + )); + $this->add_key('id', TRUE); + } + else + { + if (strpos($field, ' ') === FALSE) + { + show_error('Field information is required for that operation.'); + } + + $this->fields[] = $field; + } + } + + if (is_array($field)) + { + $this->fields = array_merge($this->fields, $field); + } + + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access public + * @param string the table name + * @return bool + */ + function create_table($table = '', $if_not_exists = FALSE) + { + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + if (count($this->fields) == 0) + { + show_error('Field information is required.'); + } + + $sql = $this->_create_table($this->db->dbprefix.$table, $this->fields, $this->primary_keys, $this->keys, $if_not_exists); + + $this->_reset(); + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access public + * @param string the table name + * @return bool + */ + function drop_table($table_name) + { + $sql = $this->_drop_table($this->db->dbprefix.$table_name); + + if (is_bool($sql)) + { + return $sql; + } + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Rename Table + * + * @access public + * @param string the old table name + * @param string the new table name + * @return bool + */ + function rename_table($table_name, $new_table_name) + { + if ($table_name == '' OR $new_table_name == '') + { + show_error('A table name is required for that operation.'); + } + + $sql = $this->_rename_table($table_name, $new_table_name); + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Column Add + * + * @access public + * @param string the table name + * @param string the column name + * @param string the column definition + * @return bool + */ + function add_column($table = '', $field = array(), $after_field = '') + { + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + // add field info into field array, but we can only do one at a time + // so we cycle through + + foreach ($field as $k => $v) + { + $this->add_field(array($k => $field[$k])); + + if (count($this->fields) == 0) + { + show_error('Field information is required.'); + } + + $sql = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->fields, $after_field); + + $this->_reset(); + + if ($this->db->query($sql) === FALSE) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Column Drop + * + * @access public + * @param string the table name + * @param string the column name + * @return bool + */ + function drop_column($table = '', $column_name = '') + { + + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + if ($column_name == '') + { + show_error('A column name is required for that operation.'); + } + + $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name); + + return $this->db->query($sql); + } + + // -------------------------------------------------------------------- + + /** + * Column Modify + * + * @access public + * @param string the table name + * @param string the column name + * @param string the column definition + * @return bool + */ + function modify_column($table = '', $field = array()) + { + if ($table == '') + { + show_error('A table name is required for that operation.'); + } + + // add field info into field array, but we can only do one at a time + // so we cycle through + + foreach ($field as $k => $v) + { + $this->add_field(array($k => $field[$k])); + + if (count($this->fields) == 0) + { + show_error('Field information is required.'); + } + + $sql = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->fields); + + $this->_reset(); + + if ($this->db->query($sql) === FALSE) + { + return FALSE; + } + } + + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Reset + * + * Resets table creation vars + * + * @access private + * @return void + */ + function _reset() + { + $this->fields = array(); + $this->keys = array(); + $this->primary_keys = array(); + } + +} + +/* End of file DB_forge.php */ +/* Location: ./system/database/DB_forge.php */ \ No newline at end of file diff --git a/system/database/DB_result.php b/system/database/DB_result.php new file mode 100644 index 000000000..b9e64feeb --- /dev/null +++ b/system/database/DB_result.php @@ -0,0 +1,342 @@ +result_object() : $this->result_array(); + } + + // -------------------------------------------------------------------- + + /** + * Query result. "object" version. + * + * @access public + * @return object + */ + function result_object() + { + if (count($this->result_object) > 0) + { + return $this->result_object; + } + + // In the event that query caching is on the result_id variable + // will return FALSE since there isn't a valid SQL resource so + // we'll simply return an empty array. + if ($this->result_id === FALSE OR $this->num_rows() == 0) + { + return array(); + } + + $this->_data_seek(0); + while ($row = $this->_fetch_object()) + { + $this->result_object[] = $row; + } + + return $this->result_object; + } + + // -------------------------------------------------------------------- + + /** + * Query result. "array" version. + * + * @access public + * @return array + */ + function result_array() + { + if (count($this->result_array) > 0) + { + return $this->result_array; + } + + // In the event that query caching is on the result_id variable + // will return FALSE since there isn't a valid SQL resource so + // we'll simply return an empty array. + if ($this->result_id === FALSE OR $this->num_rows() == 0) + { + return array(); + } + + $this->_data_seek(0); + while ($row = $this->_fetch_assoc()) + { + $this->result_array[] = $row; + } + + return $this->result_array; + } + + // -------------------------------------------------------------------- + + /** + * Query result. Acts as a wrapper function for the following functions. + * + * @access public + * @param string + * @param string can be "object" or "array" + * @return mixed either a result object or array + */ + function row($n = 0, $type = 'object') + { + if ( ! is_numeric($n)) + { + // We cache the row data for subsequent uses + if ( ! is_array($this->row_data)) + { + $this->row_data = $this->row_array(0); + } + + // array_key_exists() instead of isset() to allow for MySQL NULL values + if (array_key_exists($n, $this->row_data)) + { + return $this->row_data[$n]; + } + // reset the $n variable if the result was not achieved + $n = 0; + } + + return ($type == 'object') ? $this->row_object($n) : $this->row_array($n); + } + + // -------------------------------------------------------------------- + + /** + * Assigns an item into a particular column slot + * + * @access public + * @return object + */ + function set_row($key, $value = NULL) + { + // We cache the row data for subsequent uses + if ( ! is_array($this->row_data)) + { + $this->row_data = $this->row_array(0); + } + + if (is_array($key)) + { + foreach ($key as $k => $v) + { + $this->row_data[$k] = $v; + } + + return; + } + + if ($key != '' AND ! is_null($value)) + { + $this->row_data[$key] = $value; + } + } + + // -------------------------------------------------------------------- + + /** + * Returns a single result row - object version + * + * @access public + * @return object + */ + function row_object($n = 0) + { + $result = $this->result_object(); + + if (count($result) == 0) + { + return $result; + } + + if ($n != $this->current_row AND isset($result[$n])) + { + $this->current_row = $n; + } + + return $result[$this->current_row]; + } + + // -------------------------------------------------------------------- + + /** + * Returns a single result row - array version + * + * @access public + * @return array + */ + function row_array($n = 0) + { + $result = $this->result_array(); + + if (count($result) == 0) + { + return $result; + } + + if ($n != $this->current_row AND isset($result[$n])) + { + $this->current_row = $n; + } + + return $result[$this->current_row]; + } + + + // -------------------------------------------------------------------- + + /** + * Returns the "first" row + * + * @access public + * @return object + */ + function first_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + return $result[0]; + } + + // -------------------------------------------------------------------- + + /** + * Returns the "last" row + * + * @access public + * @return object + */ + function last_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + return $result[count($result) -1]; + } + + // -------------------------------------------------------------------- + + /** + * Returns the "next" row + * + * @access public + * @return object + */ + function next_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + + if (isset($result[$this->current_row + 1])) + { + ++$this->current_row; + } + + return $result[$this->current_row]; + } + + // -------------------------------------------------------------------- + + /** + * Returns the "previous" row + * + * @access public + * @return object + */ + function previous_row($type = 'object') + { + $result = $this->result($type); + + if (count($result) == 0) + { + return $result; + } + + if (isset($result[$this->current_row - 1])) + { + --$this->current_row; + } + return $result[$this->current_row]; + } + + // -------------------------------------------------------------------- + + /** + * The following functions are normally overloaded by the identically named + * methods in the platform-specific driver -- except when query caching + * is used. When caching is enabled we do not load the other driver. + * These functions are primarily here to prevent undefined function errors + * when a cached result object is in use. They are not otherwise fully + * operational due to the unavailability of the database resource IDs with + * cached results. + */ + function num_rows() { return $this->num_rows; } + function num_fields() { return 0; } + function list_fields() { return array(); } + function field_data() { return array(); } + function free_result() { return TRUE; } + function _data_seek() { return TRUE; } + function _fetch_assoc() { return array(); } + function _fetch_object() { return array(); } + +} +// END DB_result class + +/* End of file DB_result.php */ +/* Location: ./system/database/DB_result.php */ \ No newline at end of file diff --git a/system/database/DB_utility.php b/system/database/DB_utility.php new file mode 100644 index 000000000..94e7a0bda --- /dev/null +++ b/system/database/DB_utility.php @@ -0,0 +1,389 @@ +db + $CI =& get_instance(); + $this->db =& $CI->db; + + log_message('debug', "Database Utility Class Initialized"); + } + + // -------------------------------------------------------------------- + + /** + * List databases + * + * @access public + * @return bool + */ + function list_databases() + { + // Is there a cached result? + if (isset($this->data_cache['db_names'])) + { + return $this->data_cache['db_names']; + } + + $query = $this->db->query($this->_list_databases()); + $dbs = array(); + if ($query->num_rows() > 0) + { + foreach ($query->result_array() as $row) + { + $dbs[] = current($row); + } + } + + $this->data_cache['db_names'] = $dbs; + return $this->data_cache['db_names']; + } + + // -------------------------------------------------------------------- + + /** + * Optimize Table + * + * @access public + * @param string the table name + * @return bool + */ + function optimize_table($table_name) + { + $sql = $this->_optimize_table($table_name); + + if (is_bool($sql)) + { + show_error('db_must_use_set'); + } + + $query = $this->db->query($sql); + $res = $query->result_array(); + + // Note: Due to a bug in current() that affects some versions + // of PHP we can not pass function call directly into it + return current($res); + } + + // -------------------------------------------------------------------- + + /** + * Optimize Database + * + * @access public + * @return array + */ + function optimize_database() + { + $result = array(); + foreach ($this->db->list_tables() as $table_name) + { + $sql = $this->_optimize_table($table_name); + + if (is_bool($sql)) + { + return $sql; + } + + $query = $this->db->query($sql); + + // Build the result array... + // Note: Due to a bug in current() that affects some versions + // of PHP we can not pass function call directly into it + $res = $query->result_array(); + $res = current($res); + $key = str_replace($this->db->database.'.', '', current($res)); + $keys = array_keys($res); + unset($res[$keys[0]]); + + $result[$key] = $res; + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Repair Table + * + * @access public + * @param string the table name + * @return bool + */ + function repair_table($table_name) + { + $sql = $this->_repair_table($table_name); + + if (is_bool($sql)) + { + return $sql; + } + + $query = $this->db->query($sql); + + // Note: Due to a bug in current() that affects some versions + // of PHP we can not pass function call directly into it + $res = $query->result_array(); + return current($res); + } + + // -------------------------------------------------------------------- + + /** + * Generate CSV from a query result object + * + * @access public + * @param object The query result object + * @param string The delimiter - comma by default + * @param string The newline character - \n by default + * @param string The enclosure - double quote by default + * @return string + */ + function csv_from_result($query, $delim = ",", $newline = "\n", $enclosure = '"') + { + if ( ! is_object($query) OR ! method_exists($query, 'list_fields')) + { + show_error('You must submit a valid result object'); + } + + $out = ''; + + // First generate the headings from the table column names + foreach ($query->list_fields() as $name) + { + $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $name).$enclosure.$delim; + } + + $out = rtrim($out); + $out .= $newline; + + // Next blast through the result array and build out the rows + foreach ($query->result_array() as $row) + { + foreach ($row as $item) + { + $out .= $enclosure.str_replace($enclosure, $enclosure.$enclosure, $item).$enclosure.$delim; + } + $out = rtrim($out); + $out .= $newline; + } + + return $out; + } + + // -------------------------------------------------------------------- + + /** + * Generate XML data from a query result object + * + * @access public + * @param object The query result object + * @param array Any preferences + * @return string + */ + function xml_from_result($query, $params = array()) + { + if ( ! is_object($query) OR ! method_exists($query, 'list_fields')) + { + show_error('You must submit a valid result object'); + } + + // Set our default values + foreach (array('root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t") as $key => $val) + { + if ( ! isset($params[$key])) + { + $params[$key] = $val; + } + } + + // Create variables for convenience + extract($params); + + // Load the xml helper + $CI =& get_instance(); + $CI->load->helper('xml'); + + // Generate the result + $xml = "<{$root}>".$newline; + foreach ($query->result_array() as $row) + { + $xml .= $tab."<{$element}>".$newline; + + foreach ($row as $key => $val) + { + $xml .= $tab.$tab."<{$key}>".xml_convert($val)."".$newline; + } + $xml .= $tab."".$newline; + } + $xml .= "".$newline; + + return $xml; + } + + // -------------------------------------------------------------------- + + /** + * Database Backup + * + * @access public + * @return void + */ + function backup($params = array()) + { + // If the parameters have not been submitted as an + // array then we know that it is simply the table + // name, which is a valid short cut. + if (is_string($params)) + { + $params = array('tables' => $params); + } + + // ------------------------------------------------------ + + // Set up our default preferences + $prefs = array( + 'tables' => array(), + 'ignore' => array(), + 'filename' => '', + 'format' => 'gzip', // gzip, zip, txt + 'add_drop' => TRUE, + 'add_insert' => TRUE, + 'newline' => "\n" + ); + + // Did the user submit any preferences? If so set them.... + if (count($params) > 0) + { + foreach ($prefs as $key => $val) + { + if (isset($params[$key])) + { + $prefs[$key] = $params[$key]; + } + } + } + + // ------------------------------------------------------ + + // Are we backing up a complete database or individual tables? + // If no table names were submitted we'll fetch the entire table list + if (count($prefs['tables']) == 0) + { + $prefs['tables'] = $this->db->list_tables(); + } + + // ------------------------------------------------------ + + // Validate the format + if ( ! in_array($prefs['format'], array('gzip', 'zip', 'txt'), TRUE)) + { + $prefs['format'] = 'txt'; + } + + // ------------------------------------------------------ + + // Is the encoder supported? If not, we'll either issue an + // error or use plain text depending on the debug settings + if (($prefs['format'] == 'gzip' AND ! @function_exists('gzencode')) + OR ($prefs['format'] == 'zip' AND ! @function_exists('gzcompress'))) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_compression'); + } + + $prefs['format'] = 'txt'; + } + + // ------------------------------------------------------ + + // Set the filename if not provided - Only needed with Zip files + if ($prefs['filename'] == '' AND $prefs['format'] == 'zip') + { + $prefs['filename'] = (count($prefs['tables']) == 1) ? $prefs['tables'] : $this->db->database; + $prefs['filename'] .= '_'.date('Y-m-d_H-i', time()); + } + + // ------------------------------------------------------ + + // Was a Gzip file requested? + if ($prefs['format'] == 'gzip') + { + return gzencode($this->_backup($prefs)); + } + + // ------------------------------------------------------ + + // Was a text file requested? + if ($prefs['format'] == 'txt') + { + return $this->_backup($prefs); + } + + // ------------------------------------------------------ + + // Was a Zip file requested? + if ($prefs['format'] == 'zip') + { + // If they included the .zip file extension we'll remove it + if (preg_match("|.+?\.zip$|", $prefs['filename'])) + { + $prefs['filename'] = str_replace('.zip', '', $prefs['filename']); + } + + // Tack on the ".sql" file extension if needed + if ( ! preg_match("|.+?\.sql$|", $prefs['filename'])) + { + $prefs['filename'] .= '.sql'; + } + + // Load the Zip class and output it + + $CI =& get_instance(); + $CI->load->library('zip'); + $CI->zip->add_data($prefs['filename'], $this->_backup($prefs)); + return $CI->zip->get_zip(); + } + + } + +} + + +/* End of file DB_utility.php */ +/* Location: ./system/database/DB_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/index.html b/system/database/drivers/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/mssql/index.html b/system/database/drivers/mssql/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/mssql/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_driver.php b/system/database/drivers/mssql/mssql_driver.php new file mode 100644 index 000000000..f301ba533 --- /dev/null +++ b/system/database/drivers/mssql/mssql_driver.php @@ -0,0 +1,667 @@ +port != '') + { + $this->hostname .= ','.$this->port; + } + + return @mssql_connect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + if ($this->port != '') + { + $this->hostname .= ','.$this->port; + } + + return @mssql_pconnect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + // not implemented in MSSQL + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // Note: The brackets are required in the event that the DB name + // contains reserved characters + return @mssql_select_db('['.$this->database.']', $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @mssql_query($sql, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('BEGIN TRAN'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT TRAN'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK TRAN'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + // Access the CI object + $CI =& get_instance(); + + // Escape single quotes + $str = str_replace("'", "''", $CI->input->_remove_invisible_characters($str)); + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace( array('%', '_', $this->_like_escape_chr), + array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), + $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @mssql_rows_affected($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * Returns the last id created in the Identity column. + * + * @access public + * @return integer + */ + function insert_id() + { + $ver = self::_parse_major_version($this->version()); + $sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id"); + $query = $this->query($sql); + $row = $query->row(); + return $row->last_id; + } + + // -------------------------------------------------------------------- + + /** + * Parse major version + * + * Grabs the major version number from the + * database server version string passed in. + * + * @access private + * @param string $version + * @return int16 major version number + */ + function _parse_major_version($version) + { + preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info); + return $ver_info[1]; // return the major version b/c that's all we're interested in. + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT @@VERSION AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name"; + + // for future compatibility + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_char); + return FALSE; // not currently supported + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * List column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access private + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$table."'"; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT TOP 1 * FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return mssql_get_last_message(); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + // Are error numbers supported? + return ''; + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return implode(', ', $tables); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $i = $limit + $offset; + + return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql); + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @mssql_close($conn_id); + } + +} + + + +/* End of file mssql_driver.php */ +/* Location: ./system/database/drivers/mssql/mssql_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_forge.php b/system/database/drivers/mssql/mssql_forge.php new file mode 100644 index 000000000..f6a17811f --- /dev/null +++ b/system/database/drivers/mssql/mssql_forge.php @@ -0,0 +1,248 @@ +db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + // I think this syntax will work, but can find little documentation on renaming tables in MSSQL + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file mssql_forge.php */ +/* Location: ./system/database/drivers/mssql/mssql_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_result.php b/system/database/drivers/mssql/mssql_result.php new file mode 100644 index 000000000..e7b338045 --- /dev/null +++ b/system/database/drivers/mssql/mssql_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @mssql_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + while ($field = mssql_fetch_field($this->result_id)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + while ($field = mssql_fetch_field($this->result_id)) + { + $F = new stdClass(); + $F->name = $field->name; + $F->type = $field->type; + $F->max_length = $field->max_length; + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + mssql_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return mssql_data_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return mssql_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return mssql_fetch_object($this->result_id); + } + +} + + +/* End of file mssql_result.php */ +/* Location: ./system/database/drivers/mssql/mssql_result.php */ \ No newline at end of file diff --git a/system/database/drivers/mssql/mssql_utility.php b/system/database/drivers/mssql/mssql_utility.php new file mode 100644 index 000000000..da887b815 --- /dev/null +++ b/system/database/drivers/mssql/mssql_utility.php @@ -0,0 +1,123 @@ +db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + +} + + +/* End of file mssql_utility.php */ +/* Location: ./system/database/drivers/mssql/mssql_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/index.html b/system/database/drivers/mysql/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/mysql/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_driver.php b/system/database/drivers/mysql/mysql_driver.php new file mode 100644 index 000000000..85a6ef4a0 --- /dev/null +++ b/system/database/drivers/mysql/mysql_driver.php @@ -0,0 +1,670 @@ +port != '') + { + $this->hostname .= ':'.$this->port; + } + + return @mysql_connect($this->hostname, $this->username, $this->password, TRUE); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + if ($this->port != '') + { + $this->hostname .= ':'.$this->port; + } + + return @mysql_pconnect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + if (mysql_ping($this->conn_id) === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return @mysql_select_db($this->database, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + return @mysql_query("SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'", $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @mysql_query($sql, $this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + // "DELETE FROM TABLE" returns 0 affected rows This hack modifies + // the query so that it returns the number of affected rows + if ($this->delete_hack === TRUE) + { + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) + { + $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql); + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('SET AUTOCOMMIT=0'); + $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + if (function_exists('mysql_real_escape_string') AND is_resource($this->conn_id)) + { + $str = mysql_real_escape_string($str, $this->conn_id); + } + elseif (function_exists('mysql_escape_string')) + { + $str = mysql_escape_string($str); + } + else + { + $str = addslashes($str); + } + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @mysql_affected_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @mysql_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return mysql_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return mysql_errno($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + if ($offset == 0) + { + $offset = ''; + } + else + { + $offset .= ", "; + } + + return $sql."LIMIT ".$offset.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @mysql_close($conn_id); + } + +} + + +/* End of file mysql_driver.php */ +/* Location: ./system/database/drivers/mysql/mysql_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_forge.php b/system/database/drivers/mysql/mysql_forge.php new file mode 100644 index 000000000..ccacf99f8 --- /dev/null +++ b/system/database/drivers/mysql/mysql_forge.php @@ -0,0 +1,254 @@ +$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + if (array_key_exists('NAME', $attributes)) + { + $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; + } + + if (array_key_exists('TYPE', $attributes)) + { + $sql .= ' '.$attributes['TYPE']; + } + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes)) + { + $sql .= ($attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param mixed the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + + $sql .= $this->_process_fields($fields); + + if (count($primary_keys) > 0) + { + $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys)); + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key_name = $this->db->_protect_identifiers(implode('_', $key)); + $key = $this->db->_protect_identifiers($key); + } + else + { + $key_name = $this->db->_protect_identifiers($key); + $key = array($key_name); + } + + $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return string + */ + function _drop_table($table) + { + return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param array fields + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $fields, $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql.$this->db->_protect_identifiers($fields); + } + + $sql .= $this->_process_fields($fields); + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file mysql_forge.php */ +/* Location: ./system/database/drivers/mysql/mysql_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_result.php b/system/database/drivers/mysql/mysql_result.php new file mode 100644 index 000000000..acc586626 --- /dev/null +++ b/system/database/drivers/mysql/mysql_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @mysql_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + while ($field = mysql_fetch_field($this->result_id)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + while ($field = mysql_fetch_field($this->result_id)) + { + $F = new stdClass(); + $F->name = $field->name; + $F->type = $field->type; + $F->default = $field->def; + $F->max_length = $field->max_length; + $F->primary_key = $field->primary_key; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + mysql_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return mysql_data_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return mysql_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return mysql_fetch_object($this->result_id); + } + +} + + +/* End of file mysql_result.php */ +/* Location: ./system/database/drivers/mysql/mysql_result.php */ \ No newline at end of file diff --git a/system/database/drivers/mysql/mysql_utility.php b/system/database/drivers/mysql/mysql_utility.php new file mode 100644 index 000000000..3277b0ff7 --- /dev/null +++ b/system/database/drivers/mysql/mysql_utility.php @@ -0,0 +1,245 @@ +db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + return "REPAIR TABLE ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + /** + * MySQL Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + if (count($params) == 0) + { + return FALSE; + } + + // Extract the prefs for simplicity + extract($params); + + // Build the output + $output = ''; + foreach ((array)$tables as $table) + { + // Is the table in the "ignore" list? + if (in_array($table, (array)$ignore, TRUE)) + { + continue; + } + + // Get the table schema + $query = $this->db->query("SHOW CREATE TABLE `".$this->db->database.'`.'.$table); + + // No result means the table name was invalid + if ($query === FALSE) + { + continue; + } + + // Write out the table schema + $output .= '#'.$newline.'# TABLE STRUCTURE FOR: '.$table.$newline.'#'.$newline.$newline; + + if ($add_drop == TRUE) + { + $output .= 'DROP TABLE IF EXISTS '.$table.';'.$newline.$newline; + } + + $i = 0; + $result = $query->result_array(); + foreach ($result[0] as $val) + { + if ($i++ % 2) + { + $output .= $val.';'.$newline.$newline; + } + } + + // If inserts are not needed we're done... + if ($add_insert == FALSE) + { + continue; + } + + // Grab all the data from the current table + $query = $this->db->query("SELECT * FROM $table"); + + if ($query->num_rows() == 0) + { + continue; + } + + // Fetch the field names and determine if the field is an + // integer type. We use this info to decide whether to + // surround the data with quotes or not + + $i = 0; + $field_str = ''; + $is_int = array(); + while ($field = mysql_fetch_field($query->result_id)) + { + // Most versions of MySQL store timestamp as a string + $is_int[$i] = (in_array( + strtolower(mysql_field_type($query->result_id, $i)), + array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'), + TRUE) + ) ? TRUE : FALSE; + + // Create a string of field names + $field_str .= '`'.$field->name.'`, '; + $i++; + } + + // Trim off the end comma + $field_str = preg_replace( "/, $/" , "" , $field_str); + + + // Build the insert string + foreach ($query->result_array() as $row) + { + $val_str = ''; + + $i = 0; + foreach ($row as $v) + { + // Is the value NULL? + if ($v === NULL) + { + $val_str .= 'NULL'; + } + else + { + // Escape the data if it's not an integer + if ($is_int[$i] == FALSE) + { + $val_str .= $this->db->escape($v); + } + else + { + $val_str .= $v; + } + } + + // Append a comma + $val_str .= ', '; + $i++; + } + + // Remove the comma at the end of the string + $val_str = preg_replace( "/, $/" , "" , $val_str); + + // Build the INSERT string + $output .= 'INSERT INTO '.$table.' ('.$field_str.') VALUES ('.$val_str.');'.$newline; + } + + $output .= $newline.$newline; + } + + return $output; + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + +} + +/* End of file mysql_utility.php */ +/* Location: ./system/database/drivers/mysql/mysql_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/index.html b/system/database/drivers/mysqli/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/mysqli/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_driver.php b/system/database/drivers/mysqli/mysqli_driver.php new file mode 100644 index 000000000..5d7200fbd --- /dev/null +++ b/system/database/drivers/mysqli/mysqli_driver.php @@ -0,0 +1,671 @@ +port != '') + { + return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database, $this->port); + } + else + { + return @mysqli_connect($this->hostname, $this->username, $this->password, $this->database); + } + + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return $this->db_connect(); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + if (mysqli_ping($this->conn_id) === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return @mysqli_select_db($this->conn_id, $this->database); + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access private + * @param string + * @param string + * @return resource + */ + function _db_set_charset($charset, $collation) + { + return @mysqli_query($this->conn_id, "SET NAMES '".$this->escape_str($charset)."' COLLATE '".$this->escape_str($collation)."'"); + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + $result = @mysqli_query($this->conn_id, $sql); + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + // "DELETE FROM TABLE" returns 0 affected rows This hack modifies + // the query so that it returns the number of affected rows + if ($this->delete_hack === TRUE) + { + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) + { + $sql = preg_replace("/^\s*DELETE\s+FROM\s+(\S+)\s*$/", "DELETE FROM \\1 WHERE 1=1", $sql); + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('SET AUTOCOMMIT=0'); + $this->simple_query('START TRANSACTION'); // can also be BEGIN or BEGIN WORK + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK'); + $this->simple_query('SET AUTOCOMMIT=1'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + if (function_exists('mysqli_real_escape_string') AND is_object($this->conn_id)) + { + $str = mysqli_real_escape_string($this->conn_id, $str); + } + elseif (function_exists('mysql_escape_string')) + { + $str = mysql_escape_string($str); + } + else + { + $str = addslashes($str); + } + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace(array('%', '_'), array('\\%', '\\_'), $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @mysqli_affected_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @mysqli_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES FROM ".$this->_escape_char.$this->database.$this->_escape_char; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%'"; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return mysqli_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return mysqli_errno($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $sql .= "LIMIT ".$limit; + + if ($offset > 0) + { + $sql .= " OFFSET ".$offset; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @mysqli_close($conn_id); + } + + +} + + +/* End of file mysqli_driver.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_forge.php b/system/database/drivers/mysqli/mysqli_forge.php new file mode 100644 index 000000000..262d491ed --- /dev/null +++ b/system/database/drivers/mysqli/mysqli_forge.php @@ -0,0 +1,254 @@ +$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + if (array_key_exists('NAME', $attributes)) + { + $sql .= ' '.$this->db->_protect_identifiers($attributes['NAME']).' '; + } + + if (array_key_exists('TYPE', $attributes)) + { + $sql .= ' '.$attributes['TYPE']; + } + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes)) + { + $sql .= ($attributes['NULL'] === TRUE) ? ' NULL' : ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param mixed the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + + $sql .= $this->_process_fields($fields); + + if (count($primary_keys) > 0) + { + $key_name = $this->db->_protect_identifiers(implode('_', $primary_keys)); + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY ".$key_name." (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key_name = $this->db->_protect_identifiers(implode('_', $key)); + $key = $this->db->_protect_identifiers($key); + } + else + { + $key_name = $this->db->_protect_identifiers($key); + $key = array($key_name); + } + + $sql .= ",\n\tKEY {$key_name} (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n) DEFAULT CHARACTER SET {$this->db->char_set} COLLATE {$this->db->dbcollat};"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return string + */ + function _drop_table($table) + { + return "DROP TABLE IF EXISTS ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param array fields + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $fields, $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type "; + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql.$this->db->_protect_identifiers($fields); + } + + $sql .= $this->_process_fields($fields); + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + +} + +/* End of file mysqli_forge.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_result.php b/system/database/drivers/mysqli/mysqli_result.php new file mode 100644 index 000000000..81d22cc9c --- /dev/null +++ b/system/database/drivers/mysqli/mysqli_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @mysqli_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + while ($field = mysqli_fetch_field($this->result_id)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + while ($field = mysqli_fetch_field($this->result_id)) + { + $F = new stdClass(); + $F->name = $field->name; + $F->type = $field->type; + $F->default = $field->def; + $F->max_length = $field->max_length; + $F->primary_key = ($field->flags & MYSQLI_PRI_KEY_FLAG) ? 1 : 0; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_object($this->result_id)) + { + mysqli_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return mysqli_data_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return mysqli_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return mysqli_fetch_object($this->result_id); + } + +} + + +/* End of file mysqli_result.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_result.php */ \ No newline at end of file diff --git a/system/database/drivers/mysqli/mysqli_utility.php b/system/database/drivers/mysqli/mysqli_utility.php new file mode 100644 index 000000000..44fd0f7aa --- /dev/null +++ b/system/database/drivers/mysqli/mysqli_utility.php @@ -0,0 +1,123 @@ +db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + return "REPAIR TABLE ".$this->db->_escape_identifiers($table); + } + + // -------------------------------------------------------------------- + + /** + * MySQLi Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // Currently unsupported + return $this->db->display_error('db_unsuported_feature'); + } + + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + +} + +/* End of file mysqli_utility.php */ +/* Location: ./system/database/drivers/mysqli/mysqli_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/oci8/index.html b/system/database/drivers/oci8/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/oci8/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/oci8/oci8_driver.php b/system/database/drivers/oci8/oci8_driver.php new file mode 100644 index 000000000..758192358 --- /dev/null +++ b/system/database/drivers/oci8/oci8_driver.php @@ -0,0 +1,780 @@ +username, $this->password, $this->hostname); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return @ociplogon($this->username, $this->password, $this->hostname); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + // not implemented in oracle + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return ociserverversion($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + // oracle must parse the query before it is run. All of the actions with + // the query are based on the statement id returned by ociparse + $this->stmt_id = FALSE; + $this->_set_stmt_id($sql); + ocisetprefetch($this->stmt_id, 1000); + return @ociexecute($this->stmt_id, $this->_commit); + } + + /** + * Generate a statement ID + * + * @access private + * @param string an SQL query + * @return none + */ + function _set_stmt_id($sql) + { + if ( ! is_resource($this->stmt_id)) + { + $this->stmt_id = ociparse($this->conn_id, $this->_prep_query($sql)); + } + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * getCursor. Returns a cursor from the datbase + * + * @access public + * @return cursor id + */ + function get_cursor() + { + $this->curs_id = ocinewcursor($this->conn_id); + return $this->curs_id; + } + + // -------------------------------------------------------------------- + + /** + * Stored Procedure. Executes a stored procedure + * + * @access public + * @param package package stored procedure is in + * @param procedure stored procedure to execute + * @param params array of parameters + * @return array + * + * params array keys + * + * KEY OPTIONAL NOTES + * name no the name of the parameter should be in : format + * value no the value of the parameter. If this is an OUT or IN OUT parameter, + * this should be a reference to a variable + * type yes the type of the parameter + * length yes the max size of the parameter + */ + function stored_procedure($package, $procedure, $params) + { + if ($package == '' OR $procedure == '' OR ! is_array($params)) + { + if ($this->db_debug) + { + log_message('error', 'Invalid query: '.$package.'.'.$procedure); + return $this->display_error('db_invalid_query'); + } + return FALSE; + } + + // build the query string + $sql = "begin $package.$procedure("; + + $have_cursor = FALSE; + foreach($params as $param) + { + $sql .= $param['name'] . ","; + + if (array_key_exists('type', $param) && ($param['type'] == OCI_B_CURSOR)) + { + $have_cursor = TRUE; + } + } + $sql = trim($sql, ",") . "); end;"; + + $this->stmt_id = FALSE; + $this->_set_stmt_id($sql); + $this->_bind_params($params); + $this->query($sql, FALSE, $have_cursor); + } + + // -------------------------------------------------------------------- + + /** + * Bind parameters + * + * @access private + * @return none + */ + function _bind_params($params) + { + if ( ! is_array($params) OR ! is_resource($this->stmt_id)) + { + return; + } + + foreach ($params as $param) + { + foreach (array('name', 'value', 'type', 'length') as $val) + { + if ( ! isset($param[$val])) + { + $param[$val] = ''; + } + } + + ocibindbyname($this->stmt_id, $param['name'], $param['value'], $param['length'], $param['type']); + } + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->_commit = OCI_DEFAULT; + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = OCIcommit($this->conn_id); + $this->_commit = OCI_COMMIT_ON_SUCCESS; + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = OCIrollback($this->conn_id); + $this->_commit = OCI_COMMIT_ON_SUCCESS; + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + // Access the CI object + $CI =& get_instance(); + + $str = $CI->input->_remove_invisible_characters($str); + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace( array('%', '_', $this->_like_escape_chr), + array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), + $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @ocirowcount($this->stmt_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + // not supported in oracle + return $this->display_error('db_unsupported_function'); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query == FALSE) + { + return 0; + } + + $row = $query->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT TABLE_NAME FROM ALL_TABLES"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " WHERE TABLE_NAME LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_char); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SELECT COLUMN_NAME FROM all_tab_columns WHERE table_name = '$table'"; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." where rownum = 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + $error = ocierror($this->conn_id); + return $error['message']; + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + $error = ocierror($this->conn_id); + return $error['code']; + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return implode(', ', $tables); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE TABLE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $limit = $offset + $limit; + $newsql = "SELECT * FROM (select inner_query.*, rownum rnum FROM ($sql) inner_query WHERE rownum < $limit)"; + + if ($offset != 0) + { + $newsql .= " WHERE rnum >= $offset"; + } + + // remember that we used limits + $this->limit_used = TRUE; + + return $newsql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @ocilogoff($conn_id); + } + + +} + + + +/* End of file oci8_driver.php */ +/* Location: ./system/database/drivers/oci8/oci8_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/oci8/oci8_forge.php b/system/database/drivers/oci8/oci8_forge.php new file mode 100644 index 000000000..d77ed8de6 --- /dev/null +++ b/system/database/drivers/oci8/oci8_forge.php @@ -0,0 +1,248 @@ +db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tUNIQUE COLUMNS (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return bool + */ + function _drop_table($table) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + + +} + +/* End of file oci8_forge.php */ +/* Location: ./system/database/drivers/oci8/oci8_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/oci8/oci8_result.php b/system/database/drivers/oci8/oci8_result.php new file mode 100644 index 000000000..cab538e21 --- /dev/null +++ b/system/database/drivers/oci8/oci8_result.php @@ -0,0 +1,249 @@ +result_array()); + @ociexecute($this->stmt_id); + + if ($this->curs_id) + { + @ociexecute($this->curs_id); + } + + return $rowcount; + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + $count = @ocinumcols($this->stmt_id); + + // if we used a limit we subtract it + if ($this->limit_used) + { + $count = $count - 1; + } + + return $count; + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + $fieldCount = $this->num_fields(); + for ($c = 1; $c <= $fieldCount; $c++) + { + $field_names[] = ocicolumnname($this->stmt_id, $c); + } + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + $fieldCount = $this->num_fields(); + for ($c = 1; $c <= $fieldCount; $c++) + { + $F = new stdClass(); + $F->name = ocicolumnname($this->stmt_id, $c); + $F->type = ocicolumntype($this->stmt_id, $c); + $F->max_length = ocicolumnsize($this->stmt_id, $c); + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + ocifreestatement($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc(&$row) + { + $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; + + return ocifetchinto($id, $row, OCI_ASSOC + OCI_RETURN_NULLS); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + $result = array(); + + // If PHP 5 is being used we can fetch an result object + if (function_exists('oci_fetch_object')) + { + $id = ($this->curs_id) ? $this->curs_id : $this->stmt_id; + + return @oci_fetch_object($id); + } + + // If PHP 4 is being used we have to build our own result + foreach ($this->result_array() as $key => $val) + { + $obj = new stdClass(); + if (is_array($val)) + { + foreach ($val as $k => $v) + { + $obj->$k = $v; + } + } + else + { + $obj->$key = $val; + } + + $result[] = $obj; + } + + return $result; + } + + // -------------------------------------------------------------------- + + /** + * Query result. "array" version. + * + * @access public + * @return array + */ + function result_array() + { + if (count($this->result_array) > 0) + { + return $this->result_array; + } + + // oracle's fetch functions do not return arrays. + // The information is returned in reference parameters + $row = NULL; + while ($this->_fetch_assoc($row)) + { + $this->result_array[] = $row; + } + + return $this->result_array; + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return FALSE; // Not needed + } + +} + + +/* End of file oci8_result.php */ +/* Location: ./system/database/drivers/oci8/oci8_result.php */ \ No newline at end of file diff --git a/system/database/drivers/oci8/oci8_utility.php b/system/database/drivers/oci8/oci8_utility.php new file mode 100644 index 000000000..74670eaab --- /dev/null +++ b/system/database/drivers/oci8/oci8_utility.php @@ -0,0 +1,122 @@ +db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access public + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return FALSE; + } + +} + +/* End of file oci8_utility.php */ +/* Location: ./system/database/drivers/oci8/oci8_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/index.html b/system/database/drivers/odbc/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/odbc/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_driver.php b/system/database/drivers/odbc/odbc_driver.php new file mode 100644 index 000000000..6cb3080d4 --- /dev/null +++ b/system/database/drivers/odbc/odbc_driver.php @@ -0,0 +1,639 @@ +_random_keyword = ' RND('.time().')'; // database specific random keyword + } + + /** + * Non-persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_connect() + { + return @odbc_connect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return @odbc_pconnect($this->hostname, $this->username, $this->password); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + // not implemented in odbc + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // Not needed for ODBC + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @odbc_exec($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + return odbc_autocommit($this->conn_id, FALSE); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = odbc_commit($this->conn_id); + odbc_autocommit($this->conn_id, TRUE); + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $ret = odbc_rollback($this->conn_id); + odbc_autocommit($this->conn_id, TRUE); + return $ret; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + // Access the CI object + $CI =& get_instance(); + + // ODBC doesn't require escaping + $str = $CI->input->_remove_invisible_characters($str); + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace( array('%', '_', $this->_like_escape_chr), + array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), + $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @odbc_num_rows($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @odbc_insert_id($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SHOW TABLES FROM `".$this->database."`"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_char); + return FALSE; // not currently supported + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SHOW COLUMNS FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT TOP 1 FROM ".$table; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return odbc_errormsg($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return odbc_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return $this->_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + // Does ODBC doesn't use the LIMIT clause? + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @odbc_close($conn_id); + } + + +} + + + +/* End of file odbc_driver.php */ +/* Location: ./system/database/drivers/odbc/odbc_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_forge.php b/system/database/drivers/odbc/odbc_forge.php new file mode 100644 index 000000000..1ae559b6e --- /dev/null +++ b/system/database/drivers/odbc/odbc_forge.php @@ -0,0 +1,266 @@ +db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + // ODBC has no "drop database" command since it's + // designed to connect to an existing database + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + if ($if_not_exists === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return bool + */ + function _drop_table($table) + { + // Not a supported ODBC feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + + +} + +/* End of file odbc_forge.php */ +/* Location: ./system/database/drivers/odbc/odbc_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_result.php b/system/database/drivers/odbc/odbc_result.php new file mode 100644 index 000000000..5ae46df62 --- /dev/null +++ b/system/database/drivers/odbc/odbc_result.php @@ -0,0 +1,228 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @odbc_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $field_names[] = odbc_field_name($this->result_id, $i); + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $F = new stdClass(); + $F->name = odbc_field_name($this->result_id, $i); + $F->type = odbc_field_type($this->result_id, $i); + $F->max_length = odbc_field_len($this->result_id, $i); + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + odbc_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + if (function_exists('odbc_fetch_object')) + { + return odbc_fetch_array($this->result_id); + } + else + { + return $this->_odbc_fetch_array($this->result_id); + } + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + if (function_exists('odbc_fetch_object')) + { + return odbc_fetch_object($this->result_id); + } + else + { + return $this->_odbc_fetch_object($this->result_id); + } + } + + + /** + * Result - object + * + * subsititutes the odbc_fetch_object function when + * not available (odbc_fetch_object requires unixODBC) + * + * @access private + * @return object + */ + function _odbc_fetch_object(& $odbc_result) { + $rs = array(); + $rs_obj = false; + if (odbc_fetch_into($odbc_result, $rs)) { + foreach ($rs as $k=>$v) { + $field_name= odbc_field_name($odbc_result, $k+1); + $rs_obj->$field_name = $v; + } + } + return $rs_obj; + } + + + /** + * Result - array + * + * subsititutes the odbc_fetch_array function when + * not available (odbc_fetch_array requires unixODBC) + * + * @access private + * @return array + */ + function _odbc_fetch_array(& $odbc_result) { + $rs = array(); + $rs_assoc = false; + if (odbc_fetch_into($odbc_result, $rs)) { + $rs_assoc=array(); + foreach ($rs as $k=>$v) { + $field_name= odbc_field_name($odbc_result, $k+1); + $rs_assoc[$field_name] = $v; + } + } + return $rs_assoc; + } + +} + + +/* End of file odbc_result.php */ +/* Location: ./system/database/drivers/odbc/odbc_result.php */ \ No newline at end of file diff --git a/system/database/drivers/odbc/odbc_utility.php b/system/database/drivers/odbc/odbc_utility.php new file mode 100644 index 000000000..4e6848e82 --- /dev/null +++ b/system/database/drivers/odbc/odbc_utility.php @@ -0,0 +1,148 @@ +db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Optimize table query + * + * Generates a platform-specific query so that a table can be optimized + * + * @access private + * @param string the table name + * @return object + */ + function _optimize_table($table) + { + // Not a supported ODBC feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Generates a platform-specific query so that a table can be repaired + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + // Not a supported ODBC feature + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * ODBC Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // Currently unsupported + return $this->db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database() + { + // ODBC has no "create database" command since it's + // designed to connect to an existing database + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + // ODBC has no "drop database" command since it's + // designed to connect to an existing database + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return FALSE; + } +} + +/* End of file odbc_utility.php */ +/* Location: ./system/database/drivers/odbc/odbc_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/index.html b/system/database/drivers/postgre/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/postgre/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_driver.php b/system/database/drivers/postgre/postgre_driver.php new file mode 100644 index 000000000..bac179d40 --- /dev/null +++ b/system/database/drivers/postgre/postgre_driver.php @@ -0,0 +1,684 @@ + 'host', + 'port' => 'port', + 'database' => 'dbname', + 'username' => 'user', + 'password' => 'password' + ); + + $connect_string = ""; + foreach ($components as $key => $val) + { + if (isset($this->$key) && $this->$key != '') + { + $connect_string .= " $val=".$this->$key; + } + } + return trim($connect_string); + } + + // -------------------------------------------------------------------- + + /** + * Non-persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_connect() + { + return @pg_connect($this->_connect_string()); + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + return @pg_pconnect($this->_connect_string()); + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + if (pg_ping($this->conn_id) === FALSE) + { + $this->conn_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + // Not needed for Postgre so we'll return TRUE + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return "SELECT version() AS ver"; + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @pg_query($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + return @pg_exec($this->conn_id, "begin"); + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + return @pg_exec($this->conn_id, "commit"); + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + return @pg_exec($this->conn_id, "rollback"); + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + $str = pg_escape_string($str); + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace( array('%', '_', $this->_like_escape_chr), + array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), + $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return @pg_affected_rows($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + $v = $this->_version(); + $v = $v['server']; + + $table = func_num_args() > 0 ? func_get_arg(0) : null; + $column = func_num_args() > 1 ? func_get_arg(1) : null; + + if ($table == null && $v >= '8.1') + { + $sql='SELECT LASTVAL() as ins_id'; + } + elseif ($table != null && $column != null && $v >= '8.0') + { + $sql = sprintf("SELECT pg_get_serial_sequence('%s','%s') as seq", $table, $column); + $query = $this->query($sql); + $row = $query->row(); + $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $row->seq); + } + elseif ($table != null) + { + // seq_name passed in table parameter + $sql = sprintf("SELECT CURRVAL('%s') as ins_id", $table); + } + else + { + return pg_last_oid($this->result_id); + } + $query = $this->query($sql); + $row = $query->row(); + return $row->ins_id; + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * Show table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " AND table_name LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_char); + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + return "SELECT column_name FROM information_schema.columns WHERE table_name ='".$table."'"; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return pg_last_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return ''; + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return implode(', ', $tables); + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return "TRUNCATE ".$table; + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + $sql .= "LIMIT ".$limit; + + if ($offset > 0) + { + $sql .= " OFFSET ".$offset; + } + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @pg_close($conn_id); + } + + +} + + +/* End of file postgre_driver.php */ +/* Location: ./system/database/drivers/postgre/postgre_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_forge.php b/system/database/drivers/postgre/postgre_forge.php new file mode 100644 index 000000000..c98ef425d --- /dev/null +++ b/system/database/drivers/postgre/postgre_forge.php @@ -0,0 +1,248 @@ +db->_escape_identifiers($table)." ("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tFOREIGN KEY (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n);"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * @access private + * @return bool + */ + function _drop_table($table) + { + return "DROP TABLE ".$this->db->_escape_identifiers($table)." CASCADE"; + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + return $sql; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } + + +} + +/* End of file postgre_forge.php */ +/* Location: ./system/database/drivers/postgre/postgre_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_result.php b/system/database/drivers/postgre/postgre_result.php new file mode 100644 index 000000000..545f413e8 --- /dev/null +++ b/system/database/drivers/postgre/postgre_result.php @@ -0,0 +1,169 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @pg_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $field_names[] = pg_field_name($this->result_id, $i); + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $F = new stdClass(); + $F->name = pg_field_name($this->result_id, $i); + $F->type = pg_field_type($this->result_id, $i); + $F->max_length = pg_field_size($this->result_id, $i); + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + if (is_resource($this->result_id)) + { + pg_free_result($this->result_id); + $this->result_id = FALSE; + } + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return pg_result_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return pg_fetch_assoc($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + return pg_fetch_object($this->result_id); + } + +} + + +/* End of file postgre_result.php */ +/* Location: ./system/database/drivers/postgre/postgre_result.php */ \ No newline at end of file diff --git a/system/database/drivers/postgre/postgre_utility.php b/system/database/drivers/postgre/postgre_utility.php new file mode 100644 index 000000000..dda22ddb0 --- /dev/null +++ b/system/database/drivers/postgre/postgre_utility.php @@ -0,0 +1,124 @@ +db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access private + * @param string the database name + * @return bool + */ + function _create_database($name) + { + return "CREATE DATABASE ".$name; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + return "DROP DATABASE ".$name; + } + + +} + + +/* End of file postgre_utility.php */ +/* Location: ./system/database/drivers/postgre/postgre_utility.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/index.html b/system/database/drivers/sqlite/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/drivers/sqlite/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_driver.php b/system/database/drivers/sqlite/sqlite_driver.php new file mode 100644 index 000000000..05e38848a --- /dev/null +++ b/system/database/drivers/sqlite/sqlite_driver.php @@ -0,0 +1,657 @@ +database, FILE_WRITE_MODE, $error)) + { + log_message('error', $error); + + if ($this->db_debug) + { + $this->display_error($error, '', TRUE); + } + + return FALSE; + } + + return $conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Persistent database connection + * + * @access private called by the base class + * @return resource + */ + function db_pconnect() + { + if ( ! $conn_id = @sqlite_popen($this->database, FILE_WRITE_MODE, $error)) + { + log_message('error', $error); + + if ($this->db_debug) + { + $this->display_error($error, '', TRUE); + } + + return FALSE; + } + + return $conn_id; + } + + // -------------------------------------------------------------------- + + /** + * Reconnect + * + * Keep / reestablish the db connection if no queries have been + * sent for a length of time exceeding the server's idle timeout + * + * @access public + * @return void + */ + function reconnect() + { + // not implemented in SQLite + } + + // -------------------------------------------------------------------- + + /** + * Select the database + * + * @access private called by the base class + * @return resource + */ + function db_select() + { + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Set client character set + * + * @access public + * @param string + * @param string + * @return resource + */ + function db_set_charset($charset, $collation) + { + // @todo - add support if needed + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Version number query string + * + * @access public + * @return string + */ + function _version() + { + return sqlite_libversion(); + } + + // -------------------------------------------------------------------- + + /** + * Execute the query + * + * @access private called by the base class + * @param string an SQL query + * @return resource + */ + function _execute($sql) + { + $sql = $this->_prep_query($sql); + return @sqlite_query($this->conn_id, $sql); + } + + // -------------------------------------------------------------------- + + /** + * Prep the query + * + * If needed, each database adapter can prep the query string + * + * @access private called by execute() + * @param string an SQL query + * @return string + */ + function _prep_query($sql) + { + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Begin Transaction + * + * @access public + * @return bool + */ + function trans_begin($test_mode = FALSE) + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + // Reset the transaction failure flag. + // If the $test_mode flag is set to TRUE transactions will be rolled back + // even if the queries produce a successful result. + $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE; + + $this->simple_query('BEGIN TRANSACTION'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Commit Transaction + * + * @access public + * @return bool + */ + function trans_commit() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('COMMIT'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Rollback Transaction + * + * @access public + * @return bool + */ + function trans_rollback() + { + if ( ! $this->trans_enabled) + { + return TRUE; + } + + // When transactions are nested we only begin/commit/rollback the outermost ones + if ($this->_trans_depth > 0) + { + return TRUE; + } + + $this->simple_query('ROLLBACK'); + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Escape String + * + * @access public + * @param string + * @param bool whether or not the string will be used in a LIKE condition + * @return string + */ + function escape_str($str, $like = FALSE) + { + if (is_array($str)) + { + foreach($str as $key => $val) + { + $str[$key] = $this->escape_str($val, $like); + } + + return $str; + } + + $str = sqlite_escape_string($str); + + // escape LIKE condition wildcards + if ($like === TRUE) + { + $str = str_replace( array('%', '_', $this->_like_escape_chr), + array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr), + $str); + } + + return $str; + } + + // -------------------------------------------------------------------- + + /** + * Affected Rows + * + * @access public + * @return integer + */ + function affected_rows() + { + return sqlite_changes($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Insert ID + * + * @access public + * @return integer + */ + function insert_id() + { + return @sqlite_last_insert_rowid($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * "Count All" query + * + * Generates a platform-specific query string that counts all records in + * the specified database + * + * @access public + * @param string + * @return string + */ + function count_all($table = '') + { + if ($table == '') + { + return 0; + } + + $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE)); + + if ($query->num_rows() == 0) + { + return 0; + } + + $row = $query->row(); + return (int) $row->numrows; + } + + // -------------------------------------------------------------------- + + /** + * List table query + * + * Generates a platform-specific query string so that the table names can be fetched + * + * @access private + * @param boolean + * @return string + */ + function _list_tables($prefix_limit = FALSE) + { + $sql = "SELECT name from sqlite_master WHERE type='table'"; + + if ($prefix_limit !== FALSE AND $this->dbprefix != '') + { + $sql .= " AND 'name' LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_char); + } + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Show column query + * + * Generates a platform-specific query string so that the column names can be fetched + * + * @access public + * @param string the table name + * @return string + */ + function _list_columns($table = '') + { + // Not supported + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Field data query + * + * Generates a platform-specific query so that the column data can be retrieved + * + * @access public + * @param string the table name + * @return object + */ + function _field_data($table) + { + return "SELECT * FROM ".$table." LIMIT 1"; + } + + // -------------------------------------------------------------------- + + /** + * The error message string + * + * @access private + * @return string + */ + function _error_message() + { + return sqlite_error_string(sqlite_last_error($this->conn_id)); + } + + // -------------------------------------------------------------------- + + /** + * The error message number + * + * @access private + * @return integer + */ + function _error_number() + { + return sqlite_last_error($this->conn_id); + } + + // -------------------------------------------------------------------- + + /** + * Escape the SQL Identifiers + * + * This function escapes column and table names + * + * @access private + * @param string + * @return string + */ + function _escape_identifiers($item) + { + if ($this->_escape_char == '') + { + return $item; + } + + foreach ($this->_reserved_identifiers as $id) + { + if (strpos($item, '.'.$id) !== FALSE) + { + $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item); + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + } + + if (strpos($item, '.') !== FALSE) + { + $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char; + } + else + { + $str = $this->_escape_char.$item.$this->_escape_char; + } + + // remove duplicates if the user already included the escape + return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str); + } + + // -------------------------------------------------------------------- + + /** + * From Tables + * + * This function implicitly groups FROM tables so there is no confusion + * about operator precedence in harmony with SQL standards + * + * @access public + * @param type + * @return type + */ + function _from_tables($tables) + { + if ( ! is_array($tables)) + { + $tables = array($tables); + } + + return '('.implode(', ', $tables).')'; + } + + // -------------------------------------------------------------------- + + /** + * Insert statement + * + * Generates a platform-specific insert string from the supplied data + * + * @access public + * @param string the table name + * @param array the insert keys + * @param array the insert values + * @return string + */ + function _insert($table, $keys, $values) + { + return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")"; + } + + // -------------------------------------------------------------------- + + /** + * Update statement + * + * Generates a platform-specific update string from the supplied data + * + * @access public + * @param string the table name + * @param array the update data + * @param array the where clause + * @param array the orderby clause + * @param array the limit clause + * @return string + */ + function _update($table, $values, $where, $orderby = array(), $limit = FALSE) + { + foreach($values as $key => $val) + { + $valstr[] = $key." = ".$val; + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):''; + + $sql = "UPDATE ".$table." SET ".implode(', ', $valstr); + + $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : ''; + + $sql .= $orderby.$limit; + + return $sql; + } + + + // -------------------------------------------------------------------- + + /** + * Truncate statement + * + * Generates a platform-specific truncate string from the supplied data + * If the database does not support the truncate() command + * This function maps to "DELETE FROM table" + * + * @access public + * @param string the table name + * @return string + */ + function _truncate($table) + { + return $this->_delete($table); + } + + // -------------------------------------------------------------------- + + /** + * Delete statement + * + * Generates a platform-specific delete string from the supplied data + * + * @access public + * @param string the table name + * @param array the where clause + * @param string the limit clause + * @return string + */ + function _delete($table, $where = array(), $like = array(), $limit = FALSE) + { + $conditions = ''; + + if (count($where) > 0 OR count($like) > 0) + { + $conditions = "\nWHERE "; + $conditions .= implode("\n", $this->ar_where); + + if (count($where) > 0 && count($like) > 0) + { + $conditions .= " AND "; + } + $conditions .= implode("\n", $like); + } + + $limit = ( ! $limit) ? '' : ' LIMIT '.$limit; + + return "DELETE FROM ".$table.$conditions.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Limit string + * + * Generates a platform-specific LIMIT clause + * + * @access public + * @param string the sql query string + * @param integer the number of rows to limit the query to + * @param integer the offset value + * @return string + */ + function _limit($sql, $limit, $offset) + { + if ($offset == 0) + { + $offset = ''; + } + else + { + $offset .= ", "; + } + + return $sql."LIMIT ".$offset.$limit; + } + + // -------------------------------------------------------------------- + + /** + * Close DB Connection + * + * @access public + * @param resource + * @return void + */ + function _close($conn_id) + { + @sqlite_close($conn_id); + } + + +} + + +/* End of file sqlite_driver.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_driver.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_forge.php b/system/database/drivers/sqlite/sqlite_forge.php new file mode 100644 index 000000000..b7d25e755 --- /dev/null +++ b/system/database/drivers/sqlite/sqlite_forge.php @@ -0,0 +1,265 @@ +db->database) OR ! @unlink($this->db->database)) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unable_to_drop'); + } + return FALSE; + } + return TRUE; + } + // -------------------------------------------------------------------- + + /** + * Create Table + * + * @access private + * @param string the table name + * @param array the fields + * @param mixed primary key(s) + * @param mixed key(s) + * @param boolean should 'IF NOT EXISTS' be added to the SQL + * @return bool + */ + function _create_table($table, $fields, $primary_keys, $keys, $if_not_exists) + { + $sql = 'CREATE TABLE '; + + // IF NOT EXISTS added to SQLite in 3.3.0 + if ($if_not_exists === TRUE && version_compare($this->_version(), '3.3.0', '>=') === TRUE) + { + $sql .= 'IF NOT EXISTS '; + } + + $sql .= $this->db->_escape_identifiers($table)."("; + $current_field_count = 0; + + foreach ($fields as $field=>$attributes) + { + // Numeric field names aren't allowed in databases, so if the key is + // numeric, we know it was assigned by PHP and the developer manually + // entered the field information, so we'll simply add it to the list + if (is_numeric($field)) + { + $sql .= "\n\t$attributes"; + } + else + { + $attributes = array_change_key_case($attributes, CASE_UPPER); + + $sql .= "\n\t".$this->db->_protect_identifiers($field); + + $sql .= ' '.$attributes['TYPE']; + + if (array_key_exists('CONSTRAINT', $attributes)) + { + $sql .= '('.$attributes['CONSTRAINT'].')'; + } + + if (array_key_exists('UNSIGNED', $attributes) && $attributes['UNSIGNED'] === TRUE) + { + $sql .= ' UNSIGNED'; + } + + if (array_key_exists('DEFAULT', $attributes)) + { + $sql .= ' DEFAULT \''.$attributes['DEFAULT'].'\''; + } + + if (array_key_exists('NULL', $attributes) && $attributes['NULL'] === TRUE) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if (array_key_exists('AUTO_INCREMENT', $attributes) && $attributes['AUTO_INCREMENT'] === TRUE) + { + $sql .= ' AUTO_INCREMENT'; + } + } + + // don't add a comma on the end of the last field + if (++$current_field_count < count($fields)) + { + $sql .= ','; + } + } + + if (count($primary_keys) > 0) + { + $primary_keys = $this->db->_protect_identifiers($primary_keys); + $sql .= ",\n\tPRIMARY KEY (" . implode(', ', $primary_keys) . ")"; + } + + if (is_array($keys) && count($keys) > 0) + { + foreach ($keys as $key) + { + if (is_array($key)) + { + $key = $this->db->_protect_identifiers($key); + } + else + { + $key = array($this->db->_protect_identifiers($key)); + } + + $sql .= ",\n\tUNIQUE (" . implode(', ', $key) . ")"; + } + } + + $sql .= "\n)"; + + return $sql; + } + + // -------------------------------------------------------------------- + + /** + * Drop Table + * + * Unsupported feature in SQLite + * + * @access private + * @return bool + */ + function _drop_table($table) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unsuported_feature'); + } + return array(); + } + + // -------------------------------------------------------------------- + + /** + * Alter table query + * + * Generates a platform-specific query so that a table can be altered + * Called by add_column(), drop_column(), and column_alter(), + * + * @access private + * @param string the ALTER type (ADD, DROP, CHANGE) + * @param string the column name + * @param string the table name + * @param string the column definition + * @param string the default value + * @param boolean should 'NOT NULL' be added + * @param string the field after which we should add the new field + * @return object + */ + function _alter_table($alter_type, $table, $column_name, $column_definition = '', $default_value = '', $null = '', $after_field = '') + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table)." $alter_type ".$this->db->_protect_identifiers($column_name); + + // DROP has everything it needs now. + if ($alter_type == 'DROP') + { + // SQLite does not support dropping columns + // http://www.sqlite.org/omitted.html + // http://www.sqlite.org/faq.html#q11 + return FALSE; + } + + $sql .= " $column_definition"; + + if ($default_value != '') + { + $sql .= " DEFAULT \"$default_value\""; + } + + if ($null === NULL) + { + $sql .= ' NULL'; + } + else + { + $sql .= ' NOT NULL'; + } + + if ($after_field != '') + { + $sql .= ' AFTER ' . $this->db->_protect_identifiers($after_field); + } + + return $sql; + + } + + // -------------------------------------------------------------------- + + /** + * Rename a table + * + * Generates a platform-specific query so that a table can be renamed + * + * @access private + * @param string the old table name + * @param string the new table name + * @return string + */ + function _rename_table($table_name, $new_table_name) + { + $sql = 'ALTER TABLE '.$this->db->_protect_identifiers($table_name)." RENAME TO ".$this->db->_protect_identifiers($new_table_name); + return $sql; + } +} + +/* End of file sqlite_forge.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_forge.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_result.php b/system/database/drivers/sqlite/sqlite_result.php new file mode 100644 index 000000000..7b0631221 --- /dev/null +++ b/system/database/drivers/sqlite/sqlite_result.php @@ -0,0 +1,179 @@ +result_id); + } + + // -------------------------------------------------------------------- + + /** + * Number of fields in the result set + * + * @access public + * @return integer + */ + function num_fields() + { + return @sqlite_num_fields($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Fetch Field Names + * + * Generates an array of column names + * + * @access public + * @return array + */ + function list_fields() + { + $field_names = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $field_names[] = sqlite_field_name($this->result_id, $i); + } + + return $field_names; + } + + // -------------------------------------------------------------------- + + /** + * Field data + * + * Generates an array of objects containing field meta-data + * + * @access public + * @return array + */ + function field_data() + { + $retval = array(); + for ($i = 0; $i < $this->num_fields(); $i++) + { + $F = new stdClass(); + $F->name = sqlite_field_name($this->result_id, $i); + $F->type = 'varchar'; + $F->max_length = 0; + $F->primary_key = 0; + $F->default = ''; + + $retval[] = $F; + } + + return $retval; + } + + // -------------------------------------------------------------------- + + /** + * Free the result + * + * @return null + */ + function free_result() + { + // Not implemented in SQLite + } + + // -------------------------------------------------------------------- + + /** + * Data Seek + * + * Moves the internal pointer to the desired offset. We call + * this internally before fetching results to make sure the + * result set starts at zero + * + * @access private + * @return array + */ + function _data_seek($n = 0) + { + return sqlite_seek($this->result_id, $n); + } + + // -------------------------------------------------------------------- + + /** + * Result - associative array + * + * Returns the result set as an array + * + * @access private + * @return array + */ + function _fetch_assoc() + { + return sqlite_fetch_array($this->result_id); + } + + // -------------------------------------------------------------------- + + /** + * Result - object + * + * Returns the result set as an object + * + * @access private + * @return object + */ + function _fetch_object() + { + if (function_exists('sqlite_fetch_object')) + { + return sqlite_fetch_object($this->result_id); + } + else + { + $arr = sqlite_fetch_array($this->result_id, SQLITE_ASSOC); + if (is_array($arr)) + { + $obj = (object) $arr; + return $obj; + } else { + return NULL; + } + } + } + +} + + +/* End of file sqlite_result.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_result.php */ \ No newline at end of file diff --git a/system/database/drivers/sqlite/sqlite_utility.php b/system/database/drivers/sqlite/sqlite_utility.php new file mode 100644 index 000000000..5629dac99 --- /dev/null +++ b/system/database/drivers/sqlite/sqlite_utility.php @@ -0,0 +1,141 @@ +db_debug) + { + return $this->display_error('db_unsuported_feature'); + } + return array(); + } + + // -------------------------------------------------------------------- + + /** + * Optimize table query + * + * Is optimization even supported in SQLite? + * + * @access private + * @param string the table name + * @return object + */ + function _optimize_table($table) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * Repair table query + * + * Are table repairs even supported in SQLite? + * + * @access private + * @param string the table name + * @return object + */ + function _repair_table($table) + { + return FALSE; + } + + // -------------------------------------------------------------------- + + /** + * SQLite Export + * + * @access private + * @param array Preferences + * @return mixed + */ + function _backup($params = array()) + { + // Currently unsupported + return $this->db->display_error('db_unsuported_feature'); + } + + /** + * + * The functions below have been deprecated as of 1.6, and are only here for backwards + * compatibility. They now reside in dbforge(). The use of dbutils for database manipulation + * is STRONGLY discouraged in favour if using dbforge. + * + */ + + /** + * Create database + * + * @access public + * @param string the database name + * @return bool + */ + function _create_database() + { + // In SQLite, a database is created when you connect to the database. + // We'll return TRUE so that an error isn't generated + return TRUE; + } + + // -------------------------------------------------------------------- + + /** + * Drop database + * + * @access private + * @param string the database name + * @return bool + */ + function _drop_database($name) + { + if ( ! @file_exists($this->db->database) OR ! @unlink($this->db->database)) + { + if ($this->db->db_debug) + { + return $this->db->display_error('db_unable_to_drop'); + } + return FALSE; + } + return TRUE; + } + +} + +/* End of file sqlite_utility.php */ +/* Location: ./system/database/drivers/sqlite/sqlite_utility.php */ \ No newline at end of file diff --git a/system/database/index.html b/system/database/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/database/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/fonts/index.html b/system/fonts/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/fonts/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/fonts/texb.ttf b/system/fonts/texb.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a7aa6469ac968cadb32438bf01e364c300d4ba6b GIT binary patch literal 143821 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 0) // 8 = FILE_APPEND flag + { + $mode = FOPEN_WRITE_CREATE; + } + else + { + $mode = FOPEN_WRITE_CREATE_DESTRUCTIVE; + } + + // Check if we're using the include path + if (($flags & 1) > 0) // 1 = FILE_USE_INCLUDE_PATH flag + { + $use_include_path = TRUE; + } + else + { + $use_include_path = FALSE; + } + + $fp = @fopen($filename, $mode, $use_include_path); + + if ($fp === FALSE) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'file_put_contents('.htmlentities($filename).') failed to open stream', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + if (($flags & LOCK_EX) > 0) + { + if ( ! flock($fp, LOCK_EX)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'file_put_contents('.htmlentities($filename).') unable to acquire an exclusive lock on file', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + } + + // write it + if (($written = @fwrite($fp, $data)) === FALSE) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'file_put_contents('.htmlentities($filename).') failed to write to '.htmlentities($filename), $backtrace[0]['file'], $backtrace[0]['line']); + } + + // Close the handle + @fclose($fp); + + // Return length + return $written; + } +} + +// ------------------------------------------------------------------------ + +/** + * fputcsv() + * + * Format line as CSV and write to file pointer + * http://us.php.net/manual/en/function.fputcsv.php + * + * @access public + * @param resource file pointer + * @param array data to be written + * @param string delimiter + * @param string enclosure + * @return int length of written string + */ +if ( ! function_exists('fputcsv')) +{ + function fputcsv($handle, $fields, $delimiter = ',', $enclosure = '"') + { + // Checking for a handle resource + if ( ! is_resource($handle)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'fputcsv() expects parameter 1 to be stream resource, '.gettype($handle).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // OK, it is a resource, but is it a stream? + if (get_resource_type($handle) !== 'stream') + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'fputcsv() expects parameter 1 to be stream resource, '.get_resource_type($handle).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // Checking for an array of fields + if ( ! is_array($fields)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'fputcsv() expects parameter 2 to be array, '.gettype($fields).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // validate delimiter + if (strlen($delimiter) > 1) + { + $delimiter = substr($delimiter, 0, 1); + $backtrace = debug_backtrace(); + _exception_handler(E_NOTICE, 'fputcsv() delimiter must be one character long, "'.htmlentities($delimiter).'" used', $backtrace[0]['file'], $backtrace[0]['line']); + } + + // validate enclosure + if (strlen($enclosure) > 1) + { + $enclosure = substr($enclosure, 0, 1); + $backtrace = debug_backtrace(); + _exception_handler(E_NOTICE, 'fputcsv() enclosure must be one character long, "'.htmlentities($enclosure).'" used', $backtrace[0]['file'], $backtrace[0]['line']); + + } + + $out = ''; + + foreach ($fields as $cell) + { + $cell = str_replace($enclosure, $enclosure.$enclosure, $cell); + + if (strpos($cell, $delimiter) !== FALSE OR strpos($cell, $enclosure) !== FALSE OR strpos($cell, "\n") !== FALSE) + { + $out .= $enclosure.$cell.$enclosure.$delimiter; + } + else + { + $out .= $cell.$delimiter; + } + } + + $length = @fwrite($handle, substr($out, 0, -1)."\n"); + + return $length; + } +} + +// ------------------------------------------------------------------------ + +/** + * stripos() + * + * Find position of first occurrence of a case-insensitive string + * http://us.php.net/manual/en/function.stripos.php + * + * @access public + * @param string haystack + * @param string needle + * @param int offset + * @return int numeric position of the first occurrence of needle in the haystack + */ +if ( ! function_exists('stripos')) +{ + function stripos($haystack, $needle, $offset = NULL) + { + // Cast non string scalar values + if (is_scalar($haystack)) + { + settype($haystack, 'STRING'); + } + + if ( ! is_string($haystack)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'stripos() expects parameter 1 to be string, '.gettype($haystack).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + if ( ! is_scalar($needle)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'stripos() needle is not a string or an integer in '.$backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + if (is_float($offset)) + { + $offset = (int)$offset; + } + + if ( ! is_int($offset) && ! is_bool($offset) && ! is_null($offset)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'stripos() expects parameter 3 to be long, '.gettype($offset).' given', $backtrace[0]['file'], $backtrace[0]['line']); + return NULL; + } + + return strpos(strtolower($haystack), strtolower($needle), $offset); + } +} + +// ------------------------------------------------------------------------ + +/** + * str_ireplace() + * + * Find position of first occurrence of a case-insensitive string + * http://us.php.net/manual/en/function.str-ireplace.php + * (parameter 4, $count, is not supported as to do so in PHP 4 would make + * it a required parameter) + * + * @access public + * @param mixed search + * @param mixed replace + * @param mixed subject + * @return int numeric position of the first occurrence of needle in the haystack + */ +if ( ! function_exists('str_ireplace')) +{ + function str_ireplace($search, $replace, $subject) + { + // Nothing to do here + if ($search === NULL OR $subject === NULL) + { + return $subject; + } + + // Crazy arguments + if (is_scalar($search) && is_array($replace)) + { + $backtrace = debug_backtrace(); + + if (is_object($replace)) + { + show_error('Object of class '.get_class($replace).' could not be converted to string in '.$backtrace[0]['file'].' on line '.$backtrace[0]['line']); + } + else + { + _exception_handler(E_USER_NOTICE, 'Array to string conversion in '.$backtrace[0]['file'], $backtrace[0]['line']); + } + } + + // Searching for an array + if (is_array($search)) + { + // Replacing with an array + if (is_array($replace)) + { + $search = array_values($search); + $replace = array_values($replace); + + if (count($search) >= count($replace)) + { + $replace = array_pad($replace, count($search), ''); + } + else + { + $replace = array_slice($replace, 0, count($search)); + } + } + else + { + // Replacing with a string all positions + $replace = array_fill(0, count($search), $replace); + } + } + else + { + //Searching for a string and replacing with a string. + $search = array((string)$search); + $replace = array((string)$replace); + } + + // Prepare the search array + foreach ($search as $search_key => $search_value) + { + $search[$search_key] = '/'.preg_quote($search_value, '/').'/i'; + } + + // Prepare the replace array (escape backreferences) + foreach ($replace as $k => $v) + { + $replace[$k] = str_replace(array(chr(92), '$'), array(chr(92).chr(92), '\$'), $v); + } + + // do the replacement + $result = preg_replace($search, $replace, (array)$subject); + + // Check if subject was initially a string and return it as a string + if ( ! is_array($subject)) + { + return current($result); + } + + // Otherwise, just return the array + return $result; + } +} + +// ------------------------------------------------------------------------ + +/** + * http_build_query() + * + * Generate URL-encoded query string + * http://us.php.net/manual/en/function.http-build-query.php + * + * @access public + * @param array form data + * @param string numeric prefix + * @param string argument separator + * @return string URL-encoded string + */ +if ( ! function_exists('http_build_query')) +{ + function http_build_query($formdata, $numeric_prefix = NULL, $separator = NULL) + { + // Check the data + if ( ! is_array($formdata) && ! is_object($formdata)) + { + $backtrace = debug_backtrace(); + _exception_handler(E_USER_WARNING, 'http_build_query() Parameter 1 expected to be Array or Object. Incorrect value given', $backtrace[0]['file'], $backtrace[0]['line']); + return FALSE; + } + + // Cast it as array + if (is_object($formdata)) + { + $formdata = get_object_vars($formdata); + } + + // If the array is empty, return NULL + if (empty($formdata)) + { + return NULL; + } + + // Argument separator + if ($separator === NULL) + { + $separator = ini_get('arg_separator.output'); + + if (strlen($separator) == 0) + { + $separator = '&'; + } + } + + // Start building the query + $tmp = array(); + + foreach ($formdata as $key => $val) + { + if ($val === NULL) + { + continue; + } + + if (is_integer($key) && $numeric_prefix != NULL) + { + $key = $numeric_prefix.$key; + } + + if (is_resource($val)) + { + return NULL; + } + + // hand it off to a recursive parser + $tmp[] = _http_build_query_helper($key, $val, $separator); + } + + return implode($separator, $tmp); + } + + + // Helper helper. Remind anyone of college? + // Required to handle recursion in nested arrays. + // + // You could shave fractions of fractions of a second by moving where + // the urlencoding takes place, but it's much less intuitive, and if + // your application has 10,000 form fields, well, you have other problems ;) + function _http_build_query_helper($key, $val, $separator = '&') + { + if (is_scalar($val)) + { + return urlencode($key).'='.urlencode($val); + } + else + { + // arrays please + if (is_object($val)) + { + $val = get_object_vars($val); + } + + foreach ($val as $k => $v) + { + $tmp[] = _http_build_query_helper($key.'['.$k.']', $v, $separator); + } + } + + return implode($separator, $tmp); + } +} + + +/* End of file compatibility_helper.php */ +/* Location: ./system/helpers/compatibility_helper.php */ \ No newline at end of file diff --git a/system/helpers/cookie_helper.php b/system/helpers/cookie_helper.php new file mode 100644 index 000000000..40afadb57 --- /dev/null +++ b/system/helpers/cookie_helper.php @@ -0,0 +1,144 @@ +config->item('cookie_prefix') != '') + { + $prefix = $CI->config->item('cookie_prefix'); + } + if ($domain == '' AND $CI->config->item('cookie_domain') != '') + { + $domain = $CI->config->item('cookie_domain'); + } + if ($path == '/' AND $CI->config->item('cookie_path') != '/') + { + $path = $CI->config->item('cookie_path'); + } + + if ( ! is_numeric($expire)) + { + $expire = time() - 86500; + } + else + { + if ($expire > 0) + { + $expire = time() + $expire; + } + else + { + $expire = 0; + } + } + + setcookie($prefix.$name, $value, $expire, $path, $domain, 0); + } +} + +// -------------------------------------------------------------------- + +/** + * Fetch an item from the COOKIE array + * + * @access public + * @param string + * @param bool + * @return mixed + */ +if ( ! function_exists('get_cookie')) +{ + function get_cookie($index = '', $xss_clean = FALSE) + { + $CI =& get_instance(); + + $prefix = ''; + + if ( ! isset($_COOKIE[$index]) && config_item('cookie_prefix') != '') + { + $prefix = config_item('cookie_prefix'); + } + + return $CI->input->cookie($prefix.$index, $xss_clean); + } +} + +// -------------------------------------------------------------------- + +/** + * Delete a COOKIE + * + * @param mixed + * @param string the cookie domain. Usually: .yourdomain.com + * @param string the cookie path + * @param string the cookie prefix + * @return void + */ +if ( ! function_exists('delete_cookie')) +{ + function delete_cookie($name = '', $domain = '', $path = '/', $prefix = '') + { + set_cookie($name, '', '', $domain, $path, $prefix); + } +} + + +/* End of file cookie_helper.php */ +/* Location: ./system/helpers/cookie_helper.php */ \ No newline at end of file diff --git a/system/helpers/date_helper.php b/system/helpers/date_helper.php new file mode 100644 index 000000000..0e9781666 --- /dev/null +++ b/system/helpers/date_helper.php @@ -0,0 +1,611 @@ +config->item('time_reference')) == 'gmt') + { + $now = time(); + $system_time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now)); + + if (strlen($system_time) < 10) + { + $system_time = time(); + log_message('error', 'The Date class could not set a proper GMT timestamp so the local time() value was used.'); + } + + return $system_time; + } + else + { + return time(); + } + } +} + +// ------------------------------------------------------------------------ + +/** + * Convert MySQL Style Datecodes + * + * This function is identical to PHPs date() function, + * except that it allows date codes to be formatted using + * the MySQL style, where each code letter is preceded + * with a percent sign: %Y %m %d etc... + * + * The benefit of doing dates this way is that you don't + * have to worry about escaping your text letters that + * match the date codes. + * + * @access public + * @param string + * @param integer + * @return integer + */ +if ( ! function_exists('mdate')) +{ + function mdate($datestr = '', $time = '') + { + if ($datestr == '') + return ''; + + if ($time == '') + $time = now(); + + $datestr = str_replace('%\\', '', preg_replace("/([a-z]+?){1}/i", "\\\\\\1", $datestr)); + return date($datestr, $time); + } +} + +// ------------------------------------------------------------------------ + +/** + * Standard Date + * + * Returns a date formatted according to the submitted standard. + * + * @access public + * @param string the chosen format + * @param integer Unix timestamp + * @return string + */ +if ( ! function_exists('standard_date')) +{ + function standard_date($fmt = 'DATE_RFC822', $time = '') + { + $formats = array( + 'DATE_ATOM' => '%Y-%m-%dT%H:%i:%s%Q', + 'DATE_COOKIE' => '%l, %d-%M-%y %H:%i:%s UTC', + 'DATE_ISO8601' => '%Y-%m-%dT%H:%i:%s%O', + 'DATE_RFC822' => '%D, %d %M %y %H:%i:%s %O', + 'DATE_RFC850' => '%l, %d-%M-%y %H:%m:%i UTC', + 'DATE_RFC1036' => '%D, %d %M %y %H:%i:%s %O', + 'DATE_RFC1123' => '%D, %d %M %Y %H:%i:%s %O', + 'DATE_RSS' => '%D, %d %M %Y %H:%i:%s %O', + 'DATE_W3C' => '%Y-%m-%dT%H:%i:%s%Q' + ); + + if ( ! isset($formats[$fmt])) + { + return FALSE; + } + + return mdate($formats[$fmt], $time); + } +} + +// ------------------------------------------------------------------------ + +/** + * Timespan + * + * Returns a span of seconds in this format: + * 10 days 14 hours 36 minutes 47 seconds + * + * @access public + * @param integer a number of seconds + * @param integer Unix timestamp + * @return integer + */ +if ( ! function_exists('timespan')) +{ + function timespan($seconds = 1, $time = '') + { + $CI =& get_instance(); + $CI->lang->load('date'); + + if ( ! is_numeric($seconds)) + { + $seconds = 1; + } + + if ( ! is_numeric($time)) + { + $time = time(); + } + + if ($time <= $seconds) + { + $seconds = 1; + } + else + { + $seconds = $time - $seconds; + } + + $str = ''; + $years = floor($seconds / 31536000); + + if ($years > 0) + { + $str .= $years.' '.$CI->lang->line((($years > 1) ? 'date_years' : 'date_year')).', '; + } + + $seconds -= $years * 31536000; + $months = floor($seconds / 2628000); + + if ($years > 0 OR $months > 0) + { + if ($months > 0) + { + $str .= $months.' '.$CI->lang->line((($months > 1) ? 'date_months' : 'date_month')).', '; + } + + $seconds -= $months * 2628000; + } + + $weeks = floor($seconds / 604800); + + if ($years > 0 OR $months > 0 OR $weeks > 0) + { + if ($weeks > 0) + { + $str .= $weeks.' '.$CI->lang->line((($weeks > 1) ? 'date_weeks' : 'date_week')).', '; + } + + $seconds -= $weeks * 604800; + } + + $days = floor($seconds / 86400); + + if ($months > 0 OR $weeks > 0 OR $days > 0) + { + if ($days > 0) + { + $str .= $days.' '.$CI->lang->line((($days > 1) ? 'date_days' : 'date_day')).', '; + } + + $seconds -= $days * 86400; + } + + $hours = floor($seconds / 3600); + + if ($days > 0 OR $hours > 0) + { + if ($hours > 0) + { + $str .= $hours.' '.$CI->lang->line((($hours > 1) ? 'date_hours' : 'date_hour')).', '; + } + + $seconds -= $hours * 3600; + } + + $minutes = floor($seconds / 60); + + if ($days > 0 OR $hours > 0 OR $minutes > 0) + { + if ($minutes > 0) + { + $str .= $minutes.' '.$CI->lang->line((($minutes > 1) ? 'date_minutes' : 'date_minute')).', '; + } + + $seconds -= $minutes * 60; + } + + if ($str == '') + { + $str .= $seconds.' '.$CI->lang->line((($seconds > 1) ? 'date_seconds' : 'date_second')).', '; + } + + return substr(trim($str), 0, -1); + } +} + +// ------------------------------------------------------------------------ + +/** + * Number of days in a month + * + * Takes a month/year as input and returns the number of days + * for the given month/year. Takes leap years into consideration. + * + * @access public + * @param integer a numeric month + * @param integer a numeric year + * @return integer + */ +if ( ! function_exists('days_in_month')) +{ + function days_in_month($month = 0, $year = '') + { + if ($month < 1 OR $month > 12) + { + return 0; + } + + if ( ! is_numeric($year) OR strlen($year) != 4) + { + $year = date('Y'); + } + + if ($month == 2) + { + if ($year % 400 == 0 OR ($year % 4 == 0 AND $year % 100 != 0)) + { + return 29; + } + } + + $days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); + return $days_in_month[$month - 1]; + } +} + +// ------------------------------------------------------------------------ + +/** + * Converts a local Unix timestamp to GMT + * + * @access public + * @param integer Unix timestamp + * @return integer + */ +if ( ! function_exists('local_to_gmt')) +{ + function local_to_gmt($time = '') + { + if ($time == '') + $time = time(); + + return mktime( gmdate("H", $time), gmdate("i", $time), gmdate("s", $time), gmdate("m", $time), gmdate("d", $time), gmdate("Y", $time)); + } +} + +// ------------------------------------------------------------------------ + +/** + * Converts GMT time to a localized value + * + * Takes a Unix timestamp (in GMT) as input, and returns + * at the local value based on the timezone and DST setting + * submitted + * + * @access public + * @param integer Unix timestamp + * @param string timezone + * @param bool whether DST is active + * @return integer + */ +if ( ! function_exists('gmt_to_local')) +{ + function gmt_to_local($time = '', $timezone = 'UTC', $dst = FALSE) + { + if ($time == '') + { + return now(); + } + + $time += timezones($timezone) * 3600; + + if ($dst == TRUE) + { + $time += 3600; + } + + return $time; + } +} + +// ------------------------------------------------------------------------ + +/** + * Converts a MySQL Timestamp to Unix + * + * @access public + * @param integer Unix timestamp + * @return integer + */ +if ( ! function_exists('mysql_to_unix')) +{ + function mysql_to_unix($time = '') + { + // We'll remove certain characters for backward compatibility + // since the formatting changed with MySQL 4.1 + // YYYY-MM-DD HH:MM:SS + + $time = str_replace('-', '', $time); + $time = str_replace(':', '', $time); + $time = str_replace(' ', '', $time); + + // YYYYMMDDHHMMSS + return mktime( + substr($time, 8, 2), + substr($time, 10, 2), + substr($time, 12, 2), + substr($time, 4, 2), + substr($time, 6, 2), + substr($time, 0, 4) + ); + } +} + +// ------------------------------------------------------------------------ + +/** + * Unix to "Human" + * + * Formats Unix timestamp to the following prototype: 2006-08-21 11:35 PM + * + * @access public + * @param integer Unix timestamp + * @param bool whether to show seconds + * @param string format: us or euro + * @return string + */ +if ( ! function_exists('unix_to_human')) +{ + function unix_to_human($time = '', $seconds = FALSE, $fmt = 'us') + { + $r = date('Y', $time).'-'.date('m', $time).'-'.date('d', $time).' '; + + if ($fmt == 'us') + { + $r .= date('h', $time).':'.date('i', $time); + } + else + { + $r .= date('H', $time).':'.date('i', $time); + } + + if ($seconds) + { + $r .= ':'.date('s', $time); + } + + if ($fmt == 'us') + { + $r .= ' '.date('A', $time); + } + + return $r; + } +} + +// ------------------------------------------------------------------------ + +/** + * Convert "human" date to GMT + * + * Reverses the above process + * + * @access public + * @param string format: us or euro + * @return integer + */ +if ( ! function_exists('human_to_unix')) +{ + function human_to_unix($datestr = '') + { + if ($datestr == '') + { + return FALSE; + } + + $datestr = trim($datestr); + $datestr = preg_replace("/\040+/", "\040", $datestr); + + if ( ! preg_match('/^[0-9]{2,4}\-[0-9]{1,2}\-[0-9]{1,2}\s[0-9]{1,2}:[0-9]{1,2}(?::[0-9]{1,2})?(?:\s[AP]M)?$/i', $datestr)) + { + return FALSE; + } + + $split = preg_split("/\040/", $datestr); + + $ex = explode("-", $split['0']); + + $year = (strlen($ex['0']) == 2) ? '20'.$ex['0'] : $ex['0']; + $month = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1']; + $day = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2']; + + $ex = explode(":", $split['1']); + + $hour = (strlen($ex['0']) == 1) ? '0'.$ex['0'] : $ex['0']; + $min = (strlen($ex['1']) == 1) ? '0'.$ex['1'] : $ex['1']; + + if (isset($ex['2']) && preg_match('/[0-9]{1,2}/', $ex['2'])) + { + $sec = (strlen($ex['2']) == 1) ? '0'.$ex['2'] : $ex['2']; + } + else + { + // Unless specified, seconds get set to zero. + $sec = '00'; + } + + if (isset($split['2'])) + { + $ampm = strtolower($split['2']); + + if (substr($ampm, 0, 1) == 'p' AND $hour < 12) + $hour = $hour + 12; + + if (substr($ampm, 0, 1) == 'a' AND $hour == 12) + $hour = '00'; + + if (strlen($hour) == 1) + $hour = '0'.$hour; + } + + return mktime($hour, $min, $sec, $month, $day, $year); + } +} + +// ------------------------------------------------------------------------ + +/** + * Timezone Menu + * + * Generates a drop-down menu of timezones. + * + * @access public + * @param string timezone + * @param string classname + * @param string menu name + * @return string + */ +if ( ! function_exists('timezone_menu')) +{ + function timezone_menu($default = 'UTC', $class = "", $name = 'timezones') + { + $CI =& get_instance(); + $CI->lang->load('date'); + + if ($default == 'GMT') + $default = 'UTC'; + + $menu = '"; + + return $menu; + } +} + +// ------------------------------------------------------------------------ + +/** + * Timezones + * + * Returns an array of timezones. This is a helper function + * for various other ones in this library + * + * @access public + * @param string timezone + * @return string + */ +if ( ! function_exists('timezones')) +{ + function timezones($tz = '') + { + // Note: Don't change the order of these even though + // some items appear to be in the wrong order + + $zones = array( + 'UM12' => -12, + 'UM11' => -11, + 'UM10' => -10, + 'UM95' => -9.5, + 'UM9' => -9, + 'UM8' => -8, + 'UM7' => -7, + 'UM6' => -6, + 'UM5' => -5, + 'UM45' => -4.5, + 'UM4' => -4, + 'UM35' => -3.5, + 'UM3' => -3, + 'UM2' => -2, + 'UM1' => -1, + 'UTC' => 0, + 'UP1' => +1, + 'UP2' => +2, + 'UP3' => +3, + 'UP35' => +3.5, + 'UP4' => +4, + 'UP45' => +4.5, + 'UP5' => +5, + 'UP55' => +5.5, + 'UP575' => +5.75, + 'UP6' => +6, + 'UP65' => +6.5, + 'UP7' => +7, + 'UP8' => +8, + 'UP875' => +8.75, + 'UP9' => +9, + 'UP95' => +9.5, + 'UP10' => +10, + 'UP105' => +10.5, + 'UP11' => +11, + 'UP115' => +11.5, + 'UP12' => +12, + 'UP1275' => +12.75, + 'UP13' => +13, + 'UP14' => +14 + ); + + if ($tz == '') + { + return $zones; + } + + if ($tz == 'GMT') + $tz = 'UTC'; + + return ( ! isset($zones[$tz])) ? 0 : $zones[$tz]; + } +} + + +/* End of file date_helper.php */ +/* Location: ./system/helpers/date_helper.php */ \ No newline at end of file diff --git a/system/helpers/directory_helper.php b/system/helpers/directory_helper.php new file mode 100644 index 000000000..791cf0d10 --- /dev/null +++ b/system/helpers/directory_helper.php @@ -0,0 +1,84 @@ + 0) + { + $data =& fread($fp, filesize($file)); + } + + flock($fp, LOCK_UN); + fclose($fp); + + return $data; + } +} + +// ------------------------------------------------------------------------ + +/** + * Write File + * + * Writes data to the file specified in the path. + * Creates a new file if non-existent. + * + * @access public + * @param string path to file + * @param string file data + * @return bool + */ +if ( ! function_exists('write_file')) +{ + function write_file($path, $data, $mode = FOPEN_WRITE_CREATE_DESTRUCTIVE) + { + if ( ! $fp = @fopen($path, $mode)) + { + return FALSE; + } + + flock($fp, LOCK_EX); + fwrite($fp, $data); + flock($fp, LOCK_UN); + fclose($fp); + + return TRUE; + } +} + +// ------------------------------------------------------------------------ + +/** + * Delete Files + * + * Deletes all files contained in the supplied directory path. + * Files must be writable or owned by the system in order to be deleted. + * If the second parameter is set to TRUE, any directories contained + * within the supplied base directory will be nuked as well. + * + * @access public + * @param string path to file + * @param bool whether to delete any directories found in the path + * @return bool + */ +if ( ! function_exists('delete_files')) +{ + function delete_files($path, $del_dir = FALSE, $level = 0) + { + // Trim the trailing slash + $path = rtrim($path, DIRECTORY_SEPARATOR); + + if ( ! $current_dir = @opendir($path)) + return; + + while(FALSE !== ($filename = @readdir($current_dir))) + { + if ($filename != "." and $filename != "..") + { + if (is_dir($path.DIRECTORY_SEPARATOR.$filename)) + { + // Ignore empty folders + if (substr($filename, 0, 1) != '.') + { + delete_files($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $level + 1); + } + } + else + { + unlink($path.DIRECTORY_SEPARATOR.$filename); + } + } + } + @closedir($current_dir); + + if ($del_dir == TRUE AND $level > 0) + { + @rmdir($path); + } + } +} + +// ------------------------------------------------------------------------ + +/** + * Get Filenames + * + * Reads the specified directory and builds an array containing the filenames. + * Any sub-folders contained within the specified path are read as well. + * + * @access public + * @param string path to source + * @param bool whether to include the path as part of the filename + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ +if ( ! function_exists('get_filenames')) +{ + function get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE) + { + static $_filedata = array(); + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + while (FALSE !== ($file = readdir($fp))) + { + if (@is_dir($source_dir.$file) && strncmp($file, '.', 1) !== 0) + { + get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE); + } + elseif (strncmp($file, '.', 1) !== 0) + { + $_filedata[] = ($include_path == TRUE) ? $source_dir.$file : $file; + } + } + return $_filedata; + } + else + { + return FALSE; + } + } +} + +// -------------------------------------------------------------------- + +/** + * Get Directory File Information + * + * Reads the specified directory and builds an array containing the filenames, + * filesize, dates, and permissions + * + * Any sub-folders contained within the specified path are read as well. + * + * @access public + * @param string path to source + * @param bool whether to include the path as part of the filename + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ +if ( ! function_exists('get_dir_file_info')) +{ + function get_dir_file_info($source_dir, $include_path = FALSE, $_recursion = FALSE) + { + static $_filedata = array(); + $relative_path = $source_dir; + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === FALSE) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + while (FALSE !== ($file = readdir($fp))) + { + if (@is_dir($source_dir.$file) && strncmp($file, '.', 1) !== 0) + { + get_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE); + } + elseif (strncmp($file, '.', 1) !== 0) + { + $_filedata[$file] = get_file_info($source_dir.$file); + $_filedata[$file]['relative_path'] = $relative_path; + } + } + return $_filedata; + } + else + { + return FALSE; + } + } +} + +// -------------------------------------------------------------------- + +/** +* Get File Info +* +* Given a file and path, returns the name, path, size, date modified +* Second parameter allows you to explicitly declare what information you want returned +* Options are: name, server_path, size, date, readable, writable, executable, fileperms +* Returns FALSE if the file cannot be found. +* +* @access public +* @param string path to file +* @param mixed array or comma separated string of information returned +* @return array +*/ +if ( ! function_exists('get_file_info')) +{ + function get_file_info($file, $returned_values = array('name', 'server_path', 'size', 'date')) + { + + if ( ! file_exists($file)) + { + return FALSE; + } + + if (is_string($returned_values)) + { + $returned_values = explode(',', $returned_values); + } + + foreach ($returned_values as $key) + { + switch ($key) + { + case 'name': + $fileinfo['name'] = substr(strrchr($file, DIRECTORY_SEPARATOR), 1); + break; + case 'server_path': + $fileinfo['server_path'] = $file; + break; + case 'size': + $fileinfo['size'] = filesize($file); + break; + case 'date': + $fileinfo['date'] = filectime($file); + break; + case 'readable': + $fileinfo['readable'] = is_readable($file); + break; + case 'writable': + // There are known problems using is_weritable on IIS. It may not be reliable - consider fileperms() + $fileinfo['writable'] = is_writable($file); + break; + case 'executable': + $fileinfo['executable'] = is_executable($file); + break; + case 'fileperms': + $fileinfo['fileperms'] = fileperms($file); + break; + } + } + + return $fileinfo; + } +} + +// -------------------------------------------------------------------- + +/** + * Get Mime by Extension + * + * Translates a file extension into a mime type based on config/mimes.php. + * Returns FALSE if it can't determine the type, or open the mime config file + * + * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience + * It should NOT be trusted, and should certainly NOT be used for security + * + * @access public + * @param string path to file + * @return mixed + */ +if ( ! function_exists('get_mime_by_extension')) +{ + function get_mime_by_extension($file) + { + $extension = substr(strrchr($file, '.'), 1); + + global $mimes; + + if ( ! is_array($mimes)) + { + if ( ! require_once(APPPATH.'config/mimes.php')) + { + return FALSE; + } + } + + if (array_key_exists($extension, $mimes)) + { + if (is_array($mimes[$extension])) + { + // Multiple mime types, just give the first one + return current($mimes[$extension]); + } + else + { + return $mimes[$extension]; + } + } + else + { + return FALSE; + } + } +} + +// -------------------------------------------------------------------- + +/** + * Symbolic Permissions + * + * Takes a numeric value representing a file's permissions and returns + * standard symbolic notation representing that value + * + * @access public + * @param int + * @return string + */ +if ( ! function_exists('symbolic_permissions')) +{ + function symbolic_permissions($perms) + { + if (($perms & 0xC000) == 0xC000) + { + $symbolic = 's'; // Socket + } + elseif (($perms & 0xA000) == 0xA000) + { + $symbolic = 'l'; // Symbolic Link + } + elseif (($perms & 0x8000) == 0x8000) + { + $symbolic = '-'; // Regular + } + elseif (($perms & 0x6000) == 0x6000) + { + $symbolic = 'b'; // Block special + } + elseif (($perms & 0x4000) == 0x4000) + { + $symbolic = 'd'; // Directory + } + elseif (($perms & 0x2000) == 0x2000) + { + $symbolic = 'c'; // Character special + } + elseif (($perms & 0x1000) == 0x1000) + { + $symbolic = 'p'; // FIFO pipe + } + else + { + $symbolic = 'u'; // Unknown + } + + // Owner + $symbolic .= (($perms & 0x0100) ? 'r' : '-'); + $symbolic .= (($perms & 0x0080) ? 'w' : '-'); + $symbolic .= (($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-')); + + // Group + $symbolic .= (($perms & 0x0020) ? 'r' : '-'); + $symbolic .= (($perms & 0x0010) ? 'w' : '-'); + $symbolic .= (($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-')); + + // World + $symbolic .= (($perms & 0x0004) ? 'r' : '-'); + $symbolic .= (($perms & 0x0002) ? 'w' : '-'); + $symbolic .= (($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-')); + + return $symbolic; + } +} + +// -------------------------------------------------------------------- + +/** + * Octal Permissions + * + * Takes a numeric value representing a file's permissions and returns + * a three character string representing the file's octal permissions + * + * @access public + * @param int + * @return string + */ +if ( ! function_exists('octal_permissions')) +{ + function octal_permissions($perms) + { + return substr(sprintf('%o', $perms), -3); + } +} + + +/* End of file file_helper.php */ +/* Location: ./system/helpers/file_helper.php */ \ No newline at end of file diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php new file mode 100644 index 000000000..31b36efd0 --- /dev/null +++ b/system/helpers/form_helper.php @@ -0,0 +1,1025 @@ +config->site_url($action) : $action; + + $form = '
0) + { + $form .= form_hidden($hidden); + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Declaration - Multipart type + * + * Creates the opening portion of the form, but with "multipart/form-data". + * + * @access public + * @param string the URI segments of the form destination + * @param array a key/value pair of attributes + * @param array a key/value pair hidden data + * @return string + */ +if ( ! function_exists('form_open_multipart')) +{ + function form_open_multipart($action, $attributes = array(), $hidden = array()) + { + $attributes['enctype'] = 'multipart/form-data'; + return form_open($action, $attributes, $hidden); + } +} + +// ------------------------------------------------------------------------ + +/** + * Hidden Input Field + * + * Generates hidden fields. You can pass a simple key/value string or an associative + * array with multiple values. + * + * @access public + * @param mixed + * @param string + * @return string + */ +if ( ! function_exists('form_hidden')) +{ + function form_hidden($name, $value = '', $recursing = FALSE) + { + static $form; + + if ($recursing === FALSE) + { + $form = "\n"; + } + + if (is_array($name)) + { + foreach ($name as $key => $val) + { + form_hidden($key, $val, TRUE); + } + return $form; + } + + if ( ! is_array($value)) + { + $form .= ''."\n"; + } + else + { + foreach ($value as $k => $v) + { + $k = (is_int($k)) ? '' : $k; + form_hidden($name.'['.$k.']', $v, TRUE); + } + } + + return $form; + } +} + +// ------------------------------------------------------------------------ + +/** + * Text Input Field + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_input')) +{ + function form_input($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'text', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Password Field + * + * Identical to the input function but adds the "password" type + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_password')) +{ + function form_password($data = '', $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'password'; + return form_input($data, $value, $extra); + } +} + +// ------------------------------------------------------------------------ + +/** + * Upload Field + * + * Identical to the input function but adds the "file" type + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_upload')) +{ + function form_upload($data = '', $value = '', $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'file'; + return form_input($data, $value, $extra); + } +} + +// ------------------------------------------------------------------------ + +/** + * Textarea field + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_textarea')) +{ + function form_textarea($data = '', $value = '', $extra = '') + { + $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'cols' => '90', 'rows' => '12'); + + if ( ! is_array($data) OR ! isset($data['value'])) + { + $val = $value; + } + else + { + $val = $data['value']; + unset($data['value']); // textareas don't use the value attribute + } + + $name = (is_array($data)) ? $data['name'] : $data; + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Multi-select menu + * + * @access public + * @param string + * @param array + * @param mixed + * @param string + * @return type + */ +if (! function_exists('form_multiselect')) +{ + function form_multiselect($name = '', $options = array(), $selected = array(), $extra = '') + { + if ( ! strpos($extra, 'multiple')) + { + $extra .= ' multiple="multiple"'; + } + + return form_dropdown($name, $options, $selected, $extra); + } +} + +// -------------------------------------------------------------------- + +/** + * Drop-down Menu + * + * @access public + * @param string + * @param array + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_dropdown')) +{ + function form_dropdown($name = '', $options = array(), $selected = array(), $extra = '') + { + if ( ! is_array($selected)) + { + $selected = array($selected); + } + + // If no selected state was submitted we will attempt to set it automatically + if (count($selected) === 0) + { + // If the form name appears in the $_POST array we have a winner! + if (isset($_POST[$name])) + { + $selected = array($_POST[$name]); + } + } + + if ($extra != '') $extra = ' '.$extra; + + $multiple = (count($selected) > 1 && strpos($extra, 'multiple') === FALSE) ? ' multiple="multiple"' : ''; + + $form = ''; + + return $form; + } +} + +// ------------------------------------------------------------------------ + +/** + * Checkbox Field + * + * @access public + * @param mixed + * @param string + * @param bool + * @param string + * @return string + */ +if ( ! function_exists('form_checkbox')) +{ + function form_checkbox($data = '', $value = '', $checked = FALSE, $extra = '') + { + $defaults = array('type' => 'checkbox', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + if (is_array($data) AND array_key_exists('checked', $data)) + { + $checked = $data['checked']; + + if ($checked == FALSE) + { + unset($data['checked']); + } + else + { + $data['checked'] = 'checked'; + } + } + + if ($checked == TRUE) + { + $defaults['checked'] = 'checked'; + } + else + { + unset($defaults['checked']); + } + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Radio Button + * + * @access public + * @param mixed + * @param string + * @param bool + * @param string + * @return string + */ +if ( ! function_exists('form_radio')) +{ + function form_radio($data = '', $value = '', $checked = FALSE, $extra = '') + { + if ( ! is_array($data)) + { + $data = array('name' => $data); + } + + $data['type'] = 'radio'; + return form_checkbox($data, $value, $checked, $extra); + } +} + +// ------------------------------------------------------------------------ + +/** + * Submit Button + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_submit')) +{ + function form_submit($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'submit', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Reset Button + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_reset')) +{ + function form_reset($data = '', $value = '', $extra = '') + { + $defaults = array('type' => 'reset', 'name' => (( ! is_array($data)) ? $data : ''), 'value' => $value); + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Button + * + * @access public + * @param mixed + * @param string + * @param string + * @return string + */ +if ( ! function_exists('form_button')) +{ + function form_button($data = '', $content = '', $extra = '') + { + $defaults = array('name' => (( ! is_array($data)) ? $data : ''), 'type' => 'button'); + + if ( is_array($data) AND isset($data['content'])) + { + $content = $data['content']; + unset($data['content']); // content is not an attribute + } + + return ""; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Label Tag + * + * @access public + * @param string The text to appear onscreen + * @param string The id the label applies to + * @param string Additional attributes + * @return string + */ +if ( ! function_exists('form_label')) +{ + function form_label($label_text = '', $id = '', $attributes = array()) + { + + $label = ' 0) + { + foreach ($attributes as $key => $val) + { + $label .= ' '.$key.'="'.$val.'"'; + } + } + + $label .= ">$label_text"; + + return $label; + } +} + +// ------------------------------------------------------------------------ +/** + * Fieldset Tag + * + * Used to produce
text. To close fieldset + * use form_fieldset_close() + * + * @access public + * @param string The legend text + * @param string Additional attributes + * @return string + */ +if ( ! function_exists('form_fieldset')) +{ + function form_fieldset($legend_text = '', $attributes = array()) + { + $fieldset = "".$extra; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Close Tag + * + * @access public + * @param string + * @return string + */ +if ( ! function_exists('form_close')) +{ + function form_close($extra = '') + { + return "".$extra; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Prep + * + * Formats text so that it can be safely placed in a form field in the event it has HTML tags. + * + * @access public + * @param string + * @return string + */ +if ( ! function_exists('form_prep')) +{ + function form_prep($str = '', $field_name = '') + { + static $prepped_fields = array(); + + // if the field name is an array we do this recursively + if (is_array($str)) + { + foreach ($str as $key => $val) + { + $str[$key] = form_prep($val); + } + + return $str; + } + + if ($str === '') + { + return ''; + } + + // we've already prepped a field with this name + // @todo need to figure out a way to namespace this so + // that we know the *exact* field and not just one with + // the same name + if (isset($prepped_fields[$field_name])) + { + return $str; + } + + $str = htmlspecialchars($str); + + // In case htmlspecialchars misses these. + $str = str_replace(array("'", '"'), array("'", """), $str); + + if ($field_name != '') + { + $prepped_fields[$field_name] = $str; + } + + return $str; + } +} + +// ------------------------------------------------------------------------ + +/** + * Form Value + * + * Grabs a value from the POST array for the specified field so you can + * re-populate an input field or textarea. If Form Validation + * is active it retrieves the info from the validation class + * + * @access public + * @param string + * @return mixed + */ +if ( ! function_exists('set_value')) +{ + function set_value($field = '', $default = '') + { + if (FALSE === ($OBJ =& _get_validation_object())) + { + if ( ! isset($_POST[$field])) + { + return $default; + } + + return form_prep($_POST[$field], $field); + } + + return form_prep($OBJ->set_value($field, $default), $field); + } +} + +// ------------------------------------------------------------------------ + +/** + * Set Select + * + * Let's you set the selected value of a '; + + +Then, on the page that accepts the submission you'll have something like this: + + // First, delete old captchas + $expiration = time()-7200; // Two hour limit + $DB->query("DELETE FROM captcha WHERE captcha_time < ".$expiration); + + // Then see if a captcha exists: + $sql = "SELECT COUNT(*) AS count FROM captcha WHERE word = ? AND ip_address = ? AND date > ?"; + $binds = array($_POST['captcha'], $this->input->ip_address(), $expiration); + $query = $this->db->query($sql, $binds); + $row = $query->row(); + + if ($row->count == 0) + { + echo "You must submit the word that appears in the image"; + } + +*/ + + + +/** +|========================================================== +| Create Captcha +|========================================================== +| +*/ +function create_captcha($data = '', $img_path = '', $img_url = '', $font_path = '') +{ + $defaults = array('word' => '', 'img_path' => '', 'img_url' => '', 'img_width' => '150', 'img_height' => '30', 'font_path' => '', 'expiration' => 7200); + + foreach ($defaults as $key => $val) + { + if ( ! is_array($data)) + { + if ( ! isset($$key) OR $$key == '') + { + $$key = $val; + } + } + else + { + $$key = ( ! isset($data[$key])) ? $val : $data[$key]; + } + } + + if ($img_path == '' OR $img_url == '') + { + return FALSE; + } + + if ( ! @is_dir($img_path)) + { + return FALSE; + } + + if ( ! is_really_writable($img_path)) + { + return FALSE; + } + + if ( ! extension_loaded('gd')) + { + return FALSE; + } + + // ----------------------------------- + // Remove old images + // ----------------------------------- + + list($usec, $sec) = explode(" ", microtime()); + $now = ((float)$usec + (float)$sec); + + $current_dir = @opendir($img_path); + + while($filename = @readdir($current_dir)) + { + if ($filename != "." and $filename != ".." and $filename != "index.html") + { + $name = str_replace(".jpg", "", $filename); + + if (($name + $expiration) < $now) + { + @unlink($img_path.$filename); + } + } + } + + @closedir($current_dir); + + // ----------------------------------- + // Do we have a "word" yet? + // ----------------------------------- + + if ($word == '') + { + $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + $str = ''; + for ($i = 0; $i < 8; $i++) + { + $str .= substr($pool, mt_rand(0, strlen($pool) -1), 1); + } + + $word = $str; + } + + // ----------------------------------- + // Determine angle and position + // ----------------------------------- + + $length = strlen($word); + $angle = ($length >= 6) ? rand(-($length-6), ($length-6)) : 0; + $x_axis = rand(6, (360/$length)-16); + $y_axis = ($angle >= 0 ) ? rand($img_height, $img_width) : rand(6, $img_height); + + // ----------------------------------- + // Create image + // ----------------------------------- + + // PHP.net recommends imagecreatetruecolor(), but it isn't always available + if (function_exists('imagecreatetruecolor')) + { + $im = imagecreatetruecolor($img_width, $img_height); + } + else + { + $im = imagecreate($img_width, $img_height); + } + + // ----------------------------------- + // Assign colors + // ----------------------------------- + + $bg_color = imagecolorallocate ($im, 255, 255, 255); + $border_color = imagecolorallocate ($im, 153, 102, 102); + $text_color = imagecolorallocate ($im, 204, 153, 153); + $grid_color = imagecolorallocate($im, 255, 182, 182); + $shadow_color = imagecolorallocate($im, 255, 240, 240); + + // ----------------------------------- + // Create the rectangle + // ----------------------------------- + + ImageFilledRectangle($im, 0, 0, $img_width, $img_height, $bg_color); + + // ----------------------------------- + // Create the spiral pattern + // ----------------------------------- + + $theta = 1; + $thetac = 7; + $radius = 16; + $circles = 20; + $points = 32; + + for ($i = 0; $i < ($circles * $points) - 1; $i++) + { + $theta = $theta + $thetac; + $rad = $radius * ($i / $points ); + $x = ($rad * cos($theta)) + $x_axis; + $y = ($rad * sin($theta)) + $y_axis; + $theta = $theta + $thetac; + $rad1 = $radius * (($i + 1) / $points); + $x1 = ($rad1 * cos($theta)) + $x_axis; + $y1 = ($rad1 * sin($theta )) + $y_axis; + imageline($im, $x, $y, $x1, $y1, $grid_color); + $theta = $theta - $thetac; + } + + // ----------------------------------- + // Write the text + // ----------------------------------- + + $use_font = ($font_path != '' AND file_exists($font_path) AND function_exists('imagettftext')) ? TRUE : FALSE; + + if ($use_font == FALSE) + { + $font_size = 5; + $x = rand(0, $img_width/($length/3)); + $y = 0; + } + else + { + $font_size = 16; + $x = rand(0, $img_width/($length/1.5)); + $y = $font_size+2; + } + + for ($i = 0; $i < strlen($word); $i++) + { + if ($use_font == FALSE) + { + $y = rand(0 , $img_height/2); + imagestring($im, $font_size, $x, $y, substr($word, $i, 1), $text_color); + $x += ($font_size*2); + } + else + { + $y = rand($img_height/2, $img_height-3); + imagettftext($im, $font_size, $angle, $x, $y, $text_color, $font_path, substr($word, $i, 1)); + $x += $font_size; + } + } + + + // ----------------------------------- + // Create the border + // ----------------------------------- + + imagerectangle($im, 0, 0, $img_width-1, $img_height-1, $border_color); + + // ----------------------------------- + // Generate the image + // ----------------------------------- + + $img_name = $now.'.jpg'; + + ImageJPEG($im, $img_path.$img_name); + + $img = "\""; + + ImageDestroy($im); + + return array('word' => $word, 'time' => $now, 'image' => $img); +} + + +/* End of file captcha_pi.php */ +/* Location: ./system/plugins/captcha_pi.php */ \ No newline at end of file diff --git a/system/plugins/index.html b/system/plugins/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/plugins/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/plugins/js_calendar_pi.php b/system/plugins/js_calendar_pi.php new file mode 100644 index 000000000..3c304fd17 --- /dev/null +++ b/system/plugins/js_calendar_pi.php @@ -0,0 +1,629 @@ +load->plugin('js_calendar'); + +Once loaded you'll add the calendar script to the of your page like this: + + + +The above function will be passed the name of your form. + +Then to show the actual calendar you'll do this: + + +
+ +

Today

+
+ + +Note: The first parameter is the name of the field containing your date, the second parameter contains the "now" time, +and the third tells the calendar whether to highlight the current day or not. + +Lastly, you'll need some CSS for your calendar: + +.calendar { + border: 1px #6975A3 solid; + background-color: transparent; +} +.calheading { + background-color: #7C8BC0; + color: #fff; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + font-weight: bold; + text-align: center; +} +.calnavleft { + background-color: #7C8BC0; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 10px; + font-weight: bold; + color: #fff; + padding: 4px; + cursor: pointer; +} +.calnavright { + background-color: #7C8BC0; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 10px; + font-weight: bold; + color: #fff; + text-align: right; + padding: 4px; + cursor: pointer; +} +.caldayheading { + background-color: #000; + color: #fff; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 10px; + text-align: center; + padding: 6px 2px 6px 2px; +} +.caldaycells{ + color: #000; + background-color: #D1D7E6; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + text-align: center; + padding: 4px; + border: 1px #E0E5F1 solid; + cursor: pointer; +} +.caldaycellhover{ + color: #fff; + background-color: #B3BCD4; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + text-align: center; + padding: 4px; + border: 1px #B3BCD4 solid; + cursor: pointer; +} +.caldayselected{ + background-color: #737FAC; + color: #fff; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + font-weight: bold; + text-align: center; + border: 1px #566188 solid; + padding: 3px; + cursor: pointer; +} +.calblanktop { + background-color: #fff; + padding: 4px; +} +.calblankbot { + background-color: #fff; + padding: 4px; +} + + +*/ + +function js_calendar_script($form_name = 'entryform') +{ +$CI =& get_instance(); +$CI->load->language('calendar'); +ob_start(); +?> + + + var '.$field_id.' = new calendar("'.$field_id.'", '.$time.', '.(($highlight == TRUE) ? 'true' : 'false').'); + document.write('.$field_id.'.write()); + '; +} + + +/* End of file js_calendar_pi.php */ +/* Location: ./system/plugins/js_calendar_pi.php */ \ No newline at end of file diff --git a/system/scaffolding/Scaffolding.php b/system/scaffolding/Scaffolding.php new file mode 100644 index 000000000..d110626b1 --- /dev/null +++ b/system/scaffolding/Scaffolding.php @@ -0,0 +1,291 @@ +CI =& get_instance(); + + $this->CI->load->database("", FALSE, TRUE); + $this->CI->load->library('pagination'); + + // Turn off caching + $this->CI->db->cache_off(); + + /** + * Set the current table name + * This is done when initializing scaffolding: + * $this->load->scaffolding('table_name') + * + */ + $this->current_table = $db_table; + + /** + * Set the path to the "view" files + * We'll manually override the "view" path so that + * the load->view function knows where to look. + */ + + $this->CI->load->_ci_view_path = BASEPATH.'scaffolding/views/'; + + // Set the base URL + $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'both'); + $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); + + // Set a few globals + $data = array( + 'image_url' => $this->CI->config->system_url().'scaffolding/images/', + 'base_uri' => $this->base_uri, + 'base_url' => $this->base_url, + 'title' => $this->current_table + ); + + $this->CI->load->vars($data); + + // Load the language file and create variables + $this->lang = $this->CI->load->scaffold_language('scaffolding', '', TRUE); + $this->CI->load->vars($this->lang); + + // Load the helper files we plan to use + $this->CI->load->helper(array('url', 'form')); + + + log_message('debug', 'Scaffolding Class Initialized'); + } + + // -------------------------------------------------------------------- + + /** + * "Add" Page + * + * Shows a form representing the currently selected DB + * so that data can be inserted + * + * @access public + * @return string the HTML "add" page + */ + function add() + { + $data = array( + 'title' => ( ! isset($this->lang['scaff_add'])) ? 'Add Data' : $this->lang['scaff_add'], + 'fields' => $this->CI->db->field_data($this->current_table), + 'action' => $this->base_uri.'/insert' + ); + + $this->CI->load->view('add', $data); + } + + // -------------------------------------------------------------------- + + /** + * Insert the data + * + * @access public + * @return void redirects to the view page + */ + function insert() + { + if ($this->CI->db->insert($this->current_table, $_POST) === FALSE) + { + $this->add(); + } + else + { + redirect($this->base_uri.'/view/'); + } + } + + // -------------------------------------------------------------------- + + /** + * "View" Page + * + * Shows a table containing the data in the currently + * selected DB + * + * @access public + * @return string the HTML "view" page + */ + function view() + { + // Fetch the total number of DB rows + $total_rows = $this->CI->db->count_all($this->current_table); + + if ($total_rows < 1) + { + return $this->CI->load->view('no_data'); + } + + // Set the query limit/offset + $per_page = 20; + $offset = $this->CI->uri->segment(4, 0); + + // Run the query + $query = $this->CI->db->get($this->current_table, $per_page, $offset); + + // Now let's get the field names + $fields = $this->CI->db->list_fields($this->current_table); + + // We assume that the column in the first position is the primary field. + $primary = current($fields); + + // Pagination! + $this->CI->pagination->initialize( + array( + 'base_url' => $this->base_url.'/view', + 'total_rows' => $total_rows, + 'per_page' => $per_page, + 'uri_segment' => 4, + 'full_tag_open' => '

', + 'full_tag_close' => '

' + ) + ); + + $data = array( + 'title' => ( ! isset($this->lang['scaff_view'])) ? 'View Data' : $this->lang['scaff_view'], + 'query' => $query, + 'fields' => $fields, + 'primary' => $primary, + 'paginate' => $this->CI->pagination->create_links() + ); + + $this->CI->load->view('view', $data); + } + + // -------------------------------------------------------------------- + + /** + * "Edit" Page + * + * Shows a form representing the currently selected DB + * so that data can be edited + * + * @access public + * @return string the HTML "edit" page + */ + function edit() + { + if (FALSE === ($id = $this->CI->uri->segment(4))) + { + return $this->view(); + } + + // Fetch the primary field name + $primary = $this->CI->db->primary($this->current_table); + + // Run the query + $query = $this->CI->db->get_where($this->current_table, array($primary => $id)); + + $data = array( + 'title' => ( ! isset($this->lang['scaff_edit'])) ? 'Edit Data' : $this->lang['scaff_edit'], + 'fields' => $query->field_data(), + 'query' => $query->row(), + 'action' => $this->base_uri.'/update/'.$this->CI->uri->segment(4) + ); + + $this->CI->load->view('edit', $data); + } + + // -------------------------------------------------------------------- + + /** + * Update + * + * @access public + * @return void redirects to the view page + */ + function update() + { + // Fetch the primary key + $primary = $this->CI->db->primary($this->current_table); + + // Now do the query + $this->CI->db->update($this->current_table, $_POST, array($primary => $this->CI->uri->segment(4))); + + redirect($this->base_uri.'/view/'); + } + + // -------------------------------------------------------------------- + + /** + * Delete Confirmation + * + * @access public + * @return string the HTML "delete confirm" page + */ + function delete() + { + if ( ! isset($this->lang['scaff_del_confirm'])) + { + $message = 'Are you sure you want to delete the following row: '.$this->CI->uri->segment(4); + } + else + { + $message = $this->lang['scaff_del_confirm'].' '.$this->CI->uri->segment(4); + } + + $data = array( + 'title' => ( ! isset($this->lang['scaff_delete'])) ? 'Delete Data' : $this->lang['scaff_delete'], + 'message' => $message, + 'no' => anchor(array($this->base_uri, 'view'), ( ! isset($this->lang['scaff_no'])) ? 'No' : $this->lang['scaff_no']), + 'yes' => anchor(array($this->base_uri, 'do_delete', $this->CI->uri->segment(4)), ( ! isset($this->lang['scaff_yes'])) ? 'Yes' : $this->lang['scaff_yes']) + ); + + $this->CI->load->view('delete', $data); + } + + // -------------------------------------------------------------------- + + /** + * Delete + * + * @access public + * @return void redirects to the view page + */ + function do_delete() + { + // Fetch the primary key + $primary = $this->CI->db->primary($this->current_table); + + // Now do the query + $this->CI->db->where($primary, $this->CI->uri->segment(4)); + $this->CI->db->delete($this->current_table); + + header("Refresh:0;url=".site_url(array($this->base_uri, 'view'))); + exit; + } + +} + +/* End of file Scaffolding.php */ +/* Location: ./system/scaffolding/Scaffolding.php */ \ No newline at end of file diff --git a/system/scaffolding/images/background.jpg b/system/scaffolding/images/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d5bdcea1d5b12024146ac223011123defe25635 GIT binary patch literal 410 zcmex=C5UDGKfoZ!!BEV=#mp$kz$D1XEXer(2ty*! z+02YUS0VrtD?1k>st{080OUV@genGxz>7?5j0-mkiCh&6o5sHUdIYDWAcnU8w-|VU z<}(R03o_UTDGVclFtr^J1GVtmh-M!uS+`)H81DN0} iwv|qaLOQIREgl>6ml<_DjNt59WD&;F0MaS{|0VzeUqk@_ literal 0 HcmV?d00001 diff --git a/system/scaffolding/images/index.html b/system/scaffolding/images/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/scaffolding/images/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/scaffolding/images/logo.jpg b/system/scaffolding/images/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d6cc9a76db0b1174c74379f7f20df290e4ec7f86 GIT binary patch literal 4518 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 fMjns;GH;Ptst=l@%I0apKb7 + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/scaffolding/views/add.php b/system/scaffolding/views/add.php new file mode 100644 index 000000000..cbb12f6fb --- /dev/null +++ b/system/scaffolding/views/add.php @@ -0,0 +1,32 @@ +load->view('header'); ?> + +

+ + + + + + + +primary_key == 1) continue; ?> + + + + + type == 'blob'): ?> + + + + + + + +
name; echo ' '.$field->default; ?>
+ + + + + +load->view('footer'); +/* End of file add.php */ +/* Location: ./system/scaffolding/views/add.php */ diff --git a/system/scaffolding/views/delete.php b/system/scaffolding/views/delete.php new file mode 100644 index 000000000..d19542195 --- /dev/null +++ b/system/scaffolding/views/delete.php @@ -0,0 +1,9 @@ +load->view('header'); ?> + +

+ +

  |   + +load->view('footer'); +/* End of file delete.php */ +/* Location: ./system/scaffolding/views/delete.php */ diff --git a/system/scaffolding/views/edit.php b/system/scaffolding/views/edit.php new file mode 100644 index 000000000..fe553e591 --- /dev/null +++ b/system/scaffolding/views/edit.php @@ -0,0 +1,33 @@ +load->view('header'); ?> + + +

+ + + + + + + +primary_key == 1) continue; ?> + + + + + type == 'blob'): ?> + + + + + + + +
name; ?>
+ + + + + +load->view('footer'); +/* End of file edit.php */ +/* Location: ./system/scaffolding/views/edit.php */ \ No newline at end of file diff --git a/system/scaffolding/views/footer.php b/system/scaffolding/views/footer.php new file mode 100644 index 000000000..0e71401c9 --- /dev/null +++ b/system/scaffolding/views/footer.php @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/system/scaffolding/views/header.php b/system/scaffolding/views/header.php new file mode 100644 index 000000000..50f234a49 --- /dev/null +++ b/system/scaffolding/views/header.php @@ -0,0 +1,29 @@ + + + + +<?php echo $title; ?> + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/system/scaffolding/views/index.html b/system/scaffolding/views/index.html new file mode 100644 index 000000000..c942a79ce --- /dev/null +++ b/system/scaffolding/views/index.html @@ -0,0 +1,10 @@ + + + 403 Forbidden + + + +

Directory access is forbidden.

+ + + \ No newline at end of file diff --git a/system/scaffolding/views/no_data.php b/system/scaffolding/views/no_data.php new file mode 100644 index 000000000..bc81e7480 --- /dev/null +++ b/system/scaffolding/views/no_data.php @@ -0,0 +1,8 @@ +load->view('header'); ?> + +

+

+ +load->view('footer'); +/* End of file no_data.php */ +/* Location: ./system/scaffolding/views/no_data.php */ \ No newline at end of file diff --git a/system/scaffolding/views/stylesheet.css b/system/scaffolding/views/stylesheet.css new file mode 100644 index 000000000..3f487dd0b --- /dev/null +++ b/system/scaffolding/views/stylesheet.css @@ -0,0 +1,143 @@ +body { + margin: 0; + padding: 0; + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + color: #4F5155; + background: #fff url(background.jpg) repeat-x left top; +} + +a { + color: #8B0D00; + background-color: transparent; + text-decoration: none; + font-weight: bold; +} + +a:visited { + color: #8B0D00; + background-color: transparent; + text-decoration: none; +} + +a:hover { + color: #000; + text-decoration: none; + background-color: transparent; +} + + +#header { + margin: 0; + padding: 0; +} + +#header_left { + background-color: transparent; + float: left; + padding: 21px 0 0 32px; + margin: 0 +} + +#header_right { + background-color: transparent; + float: right; + text-align: right; + padding: 35px 50px 20px 0; + margin: 0 +} + +#footer { + margin: 20px 0 15px 0; + padding: 0; +} + +#footer p { + font-size: 10px; + color: #999; + text-align: center; +} + +#outer { + margin: 30px 40px 0 40px; +} + +img { + padding:0; + border: 0; + margin: 0; +} + +.nopad { + padding:0; + border: 0; + margin: 0; +} + +table { + background-color: #efefef; +} + +th { + background-color: #eee; + font-weight: bold; + padding: 6px; + text-align: left; +} + +td { + background-color: #fff; + padding: 6px; +} + + +form { + margin: 0; + padding: 0; +} + +.input { + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 11px; + width: 600px; + color: #333; + border: 1px solid #B3B4BD; + font-size: 11px; + height: 2em; + padding: 0; + margin: 0; +} + +.textarea { + font-family: Lucida Grande, Verdana, Geneva, Sans-serif; + font-size: 12px; + width: 600px; + color: #333; + border: 1px solid #B3B4BD; + padding: 0; + margin: 0; +} + +.select { + background-color: #fff; + font-size: 11px; + font-weight: normal; + color: #333; + padding: 0; + margin: 0 0 3px 0; +} + +.checkbox { + background-color: transparent; + padding: 0; + border: 0; +} + +.submit { + background-color: #8B0D00; + color: #FFF; + font-weight: normal; + border: 1px solid #000; + margin: 6px 0 0 0; + padding: 1px 5px 1px 5px; +} diff --git a/system/scaffolding/views/view.php b/system/scaffolding/views/view.php new file mode 100644 index 000000000..a81241d39 --- /dev/null +++ b/system/scaffolding/views/view.php @@ -0,0 +1,27 @@ +load->view('header'); ?> + + + + + + + + + + +result() as $row): ?> + + + + + + + + +
EditDelete
 $primary), $scaff_edit); ?> $primary), $scaff_delete); ?>$field);?>
+ + + +load->view('footer'); +/* End of file view.php */ +/* Location: ./system/scaffolding/views/view.php */ \ No newline at end of file