From 74008c64e0eb579732a74f79450c2fc7e655fad8 Mon Sep 17 00:00:00 2001 From: bmen Date: Mon, 16 Jan 2017 17:05:08 +0100 Subject: [PATCH] Initial Commit --- README.md | 148 ++ composer.json | 16 + index.php | 2908 +++++++++++++++++++++++++++++++ package.json | 21 + srdb-tests/DataSet.xml | 1559 +++++++++++++++++ srdb-tests/DataSetGenerator.php | 81 + srdb-tests/SrdbTest.php | 381 ++++ srdb-tests/charset-test.php | 93 + srdb-tests/db.sql | 45 + srdb.class.php | 1217 +++++++++++++ srdb.cli.php | 238 +++ 11 files changed, 6707 insertions(+) create mode 100755 README.md create mode 100755 composer.json create mode 100755 index.php create mode 100755 package.json create mode 100755 srdb-tests/DataSet.xml create mode 100755 srdb-tests/DataSetGenerator.php create mode 100755 srdb-tests/SrdbTest.php create mode 100755 srdb-tests/charset-test.php create mode 100755 srdb-tests/db.sql create mode 100755 srdb.class.php create mode 100755 srdb.cli.php diff --git a/README.md b/README.md new file mode 100755 index 0000000..5065f9a --- /dev/null +++ b/README.md @@ -0,0 +1,148 @@ +# Search Replace DB + +This script was made to aid the process of migrating PHP and MySQL based websites. It has additional features for WordPress and Drupal but works for most other similar CMSes. + +If you find a problem let us know in the issues area and if you can improve the code then please fork the repository and send us a pull request :) + +## Warnings & Limitations + +1. Three character UTF8 seems to break in certain cases. +2. We can't test every possible case, though we do our best. Backups and verifications are important. +3. The license for this script is GPL v3 and no longer WTFPL. Please bear this in mind if contributing or branching. +4. You use this script at your own risk and we have no responsibility for any problems it may cause. +5. *Do backups.* + +## Usage + +1. *Do backups.* +2. Migrate all your website files. +3. Upload the script folder to your web root or higher. +4. Browse to the script folder URL in your web browser. +5. Fill in the fields as needed. +6. Choose the `Dry run` button to do a dry run without searching/replacing. + +## Installation + +If you would like Search Replace DB to detect your WordPress installation, you should install it within a new subfolder within your WordPress installation. + +For example, if you have + + /website.com/index.php + + /website.com/wp-config.php + + /website.com/wordpress/ + + /website.com/wordpress/index.php + + /website.com/wordpress/wp-settings.php + + etc. + +You can copy Search Replace DB into the following location: + + /website.com/wordpress/Search-Replace-DB/ + + /website.com/wordpress/Search-Replace-DB/index.php + + /website.com/wordpress/Search-Replace-DB/srdb.class.php + + /website.com/wordpress/Search-Replace-DB/srdb.cli.php + + etc. + +### CLI script + +To invoke the script, nagivate in your shell to the directory to where you installed Search Replace DB. + +Type `php srdb.cli.php` to run the program. Type `php srdb.cli.php --help` for usage information: + + -h, --host + + Required. The hostname of the database server. + + -n, --name + + Required. Database name. + + -u, --user + + Required. Database user. + + -p, --pass + + Required. Database user's password. + + --port + + Optional. Port on database server to connect to. + The default is 3306. (MySQL default port). + + -s, --search + + String to search for or `preg_replace()` style regular + expression. + + -r, --replace + + None empty string to replace search with or + `preg_replace()` style replacement. + + -t, --tables + + If set only runs the script on the specified table, comma + separate for multiple values. + + -i, --include-cols + + If set only runs the script on the specified columns, comma + separate for multiple values. + + -x, --exclude-cols + + If set excludes the specified columns, comma separate for + multiple values. + + -g, --regex [no value] + + Treats value for -s or --search as a regular expression and + -r or --replace as a regular expression replacement. + + -l, --pagesize + + How many rows to fetch at a time from a table. + + -z, --dry-run [no value] + + Prevents any updates happening so you can preview the number + of changes to be made + + -e, --alter-engine + + Changes the database table to the specified database engine + eg. InnoDB or MyISAM. If specified search/replace arguments + are ignored. They will not be run simultaneously. + + -a, --alter-collation + + Changes the database table to the specified collation + eg. utf8_unicode_ci. If specified search/replace arguments + are ignored. They will not be run simultaneously. + + -v, --verbose [true|false] + + Defaults to true, can be set to false to run script silently. + + --help + + Displays this help message ;) + +## Troubleshooting + +### I get a popup saying there was an AJAX error + +This happens occasionally and could be for a couple of reasons: + + * When the script starts, it attempts to start your WordPress or Drupal installation to auto-detect your username and password settings. If this fails, you will see a message informing you that auto-detection failed. You will have to enter your details manually. + * Script was unable to set the timeout so PHP closed the connection before the table could be processed, this can happen on some server configurations. + * When using php-fpm (as you have with VVV) make sure that the socket is owned by the server user `chown www-data:www-data /var/run/php5-fpm.sock`. diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..8225797 --- /dev/null +++ b/composer.json @@ -0,0 +1,16 @@ +{ + "name": "interconnectit/search-replace-db", + "description": "A PHP search replace tool for quickly modifying a string throughout a database. Useful for changing the base URL when migrating a WordPress site from development to production.", + "license": "GPL-3.0", + "homepage": "https://github.com/interconnectit/Search-Replace-DB", + "require": { + "php": ">=5.3.0" + }, + "support": { + "issues": "https://github.com/interconnectit/Search-Replace-DB/issues" + }, + "bin": [ "srdb.cli.php" ], + "autoload": { + "files": [ "srdb.class.php" ] + } +} diff --git a/index.php b/index.php new file mode 100755 index 0000000..a60fbb1 --- /dev/null +++ b/index.php @@ -0,0 +1,2908 @@ +is_wordpress() ) { + + // prevent warnings if the charset and collate aren't defined + if ( !defined( 'DB_CHARSET') ) { + define( 'DB_CHARSET', 'utf8' ); + } + if ( !defined( 'DB_COLLATE') ) { + define( 'DB_COLLATE', '' ); + } + + // populate db details + $name = DB_NAME; + $user = DB_USER; + $pass = DB_PASSWORD; + + // Port and host need to be split apart. + if ( strstr( DB_HOST, ':' ) !== false ) { + $parts = explode( ':', DB_HOST ); + $host = $parts[0]; + $port_input = $parts[1]; + + $port = abs( (int)$port_input ); + } else { + $host = DB_HOST; + $port = 3306; + } + + $charset = DB_CHARSET; + $collate = DB_COLLATE; + + $this->response( $name, $user, $pass, $host, $port, $charset, $collate ); + + } elseif( $bootstrap && $this->is_drupal() ) { + $database = Database::getConnection(); + $database_opts = $database->getConnectionOptions(); + + // populate db details + $name = $database_opts[ 'database' ]; + $user = $database_opts[ 'username' ]; + $pass = $database_opts[ 'password' ]; + $host = $database_opts[ 'host' ]; + $port = $database_opts[ 'port' ]; + $charset = 'utf8'; + $collate = ''; + + $port_as_string = (string)$port ? (string)$port : "0"; + if ( (string)abs( (int)$port ) !== $port_as_string ) { + $port = 3306; + } else { + $port = (string)abs( (int)$port ); + } + + $this->response( $name, $user, $pass, $host, $port, $charset, $collate ); + + } else { + + $this->response(); + + } + + } + + + public function response( $name = '', $user = '', $pass = '', $host = '127.0.0.1', $port = 3306, $charset = 'utf8', $collate = '' ) { + + // always override with post data + if ( isset( $_POST[ 'name' ] ) ) { + $name = $_POST[ 'name' ]; // your database + $user = $_POST[ 'user' ]; // your db userid + $pass = $_POST[ 'pass' ]; // your db password + $host = $_POST[ 'host' ]; // normally localhost, but not necessarily. + + $port_input = $_POST[ 'port' ]; + + // Make sure that the string version of absint(port) is identical to the string input. + // This prevents expressions, decimals, spaces, etc. + $port_as_string = (string)$port_input ? (string)$port_input : "0"; + if ( (string)abs( (int)$port_input ) !== $port_as_string ) { + // Mangled port number: non numeric. + $this->add_error('Port number must be a positive integer. If you are unsure, try the default port 3306.', 'db'); + + // Force a bad run by supplying nonsense. + $port = "nonsense"; + } else { + $port = abs( (int)$port_input ); + } + + + $charset = 'utf8'; // isset( $_POST[ 'char' ] ) ? stripcslashes( $_POST[ 'char' ] ) : ''; // your db charset + $collate = ''; + } + + // Search replace details + $search = isset( $_POST[ 'search' ] ) ? $_POST[ 'search' ] : ''; + $replace = isset( $_POST[ 'replace' ] ) ? $_POST[ 'replace' ] : ''; + + // regex options + $regex = isset( $_POST[ 'regex' ] ); + $regex_i = isset( $_POST[ 'regex_i' ] ); + $regex_m = isset( $_POST[ 'regex_m' ] ); + $regex_s = isset( $_POST[ 'regex_s' ] ); + $regex_x = isset( $_POST[ 'regex_x' ] ); + + // Tables to scanned + $tables = isset( $_POST[ 'tables' ] ) && is_array( $_POST[ 'tables' ] ) ? $_POST[ 'tables' ] : array( ); + if ( isset( $_POST[ 'use_tables' ] ) && $_POST[ 'use_tables' ] == 'all' ) + $tables = array(); + + // exclude / include columns + $exclude_cols = isset( $_POST[ 'exclude_cols' ] ) ? $_POST[ 'exclude_cols' ] : array(); + $include_cols = isset( $_POST[ 'include_cols' ] ) ? $_POST[ 'include_cols' ] : array(); + + foreach( array( 'exclude_cols', 'include_cols' ) as $maybe_string_arg ) { + if ( is_string( $$maybe_string_arg ) ) + $$maybe_string_arg = array_filter( array_map( 'trim', explode( ',', $$maybe_string_arg ) ) ); + } + + // update class vars + $vars = array( + 'name', 'user', 'pass', 'host', 'port', + 'charset', 'collate', 'tables', + 'search', 'replace', + 'exclude_cols', 'include_cols', + 'regex', 'regex_i', 'regex_m', 'regex_s', 'regex_x' + ); + + foreach( $vars as $var ) { + if ( isset( $$var ) ) + $this->set( $var, $$var ); + } + + // are doing something? + $show = ''; + if ( isset( $_POST[ 'submit' ] ) ) { + if ( is_array( $_POST[ 'submit' ] ) ) + $show = key( $_POST[ 'submit' ] ); + if ( is_string( $_POST[ 'submit' ] ) ) + $show = preg_replace( '/submit\[([a-z0-9]+)\]/', '$1', $_POST[ 'submit' ] ); + } + + // is it an AJAX call + $ajax = isset( $_POST[ 'ajax' ] ); + + // body callback + $html = 'ui'; + + switch( $show ) { + + // remove search replace + case 'delete': + + // determine if it's the folder of compiled version + if ( basename( __FILE__ ) == 'index.php' ) + $path = str_replace( basename( __FILE__ ), '', __FILE__ ); + else + $path = __FILE__; + + $delete_script_success = $this->delete_script( $path ); + + if ( self::DELETE_SCRIPT_FAIL_UNSAFE === $delete_script_success) { + $this->add_error( 'Delete aborted! You seem to have placed Search/Replace into your WordPress or Drupal root. Please remove Search/Replace manually.', 'delete' ); + } else { + if ( ( self::DELETE_SCRIPT_SUCCESS === $delete_script_success ) && !( is_file( __FILE__ ) && file_exists( __FILE__ ) ) ) { + $this->add_error( 'Search/Replace has been successfully removed from your server', 'delete' ); + } else { + $this->add_error( 'Could not fully delete Search/Replace. You will have to delete it manually', 'delete' ); + } + } + + $html = 'deleted'; + + break; + + case 'liverun': + + // bsy-web, 20130621: Check live run was explicitly clicked and only set false then + $this->set( 'dry_run', false ); + + case 'dryrun': + + // build regex string + // non UI implements can just pass in complete regex string + if ( $this->regex ) { + $mods = ''; + if ( $this->regex_i ) $mods .= 'i'; + if ( $this->regex_s ) $mods .= 's'; + if ( $this->regex_m ) $mods .= 'm'; + if ( $this->regex_x ) $mods .= 'x'; + $this->search = '/' . $this->search . '/' . $mods; + } + + // call search replace class + $parent = parent::__construct( array( + 'name' => $this->get( 'name' ), + 'user' => $this->get( 'user' ), + 'pass' => $this->get( 'pass' ), + 'host' => $this->get( 'host' ), + 'port' => $this->get( 'port' ), + 'search' => $this->get( 'search' ), + 'replace' => $this->get( 'replace' ), + 'tables' => $this->get( 'tables' ), + 'dry_run' => $this->get( 'dry_run' ), + 'regex' => $this->get( 'regex' ), + 'exclude_cols' => $this->get( 'exclude_cols' ), + 'include_cols' => $this->get( 'include_cols' ) + ) ); + + break; + + case 'innodb': + + // call search replace class to alter engine + $parent = parent::__construct( array( + 'name' => $this->get( 'name' ), + 'user' => $this->get( 'user' ), + 'pass' => $this->get( 'pass' ), + 'host' => $this->get( 'host' ), + 'port' => $this->get( 'port' ), + 'tables' => $this->get( 'tables' ), + 'alter_engine' => 'InnoDB', + ) ); + + break; + + case 'utf8': + + // call search replace class to alter engine + $parent = parent::__construct( array( + 'name' => $this->get( 'name' ), + 'user' => $this->get( 'user' ), + 'pass' => $this->get( 'pass' ), + 'host' => $this->get( 'host' ), + 'port' => $this->get( 'port' ), + 'tables' => $this->get( 'tables' ), + 'alter_collation' => 'utf8_unicode_ci', + ) ); + + break; + + case 'utf8mb4': + + // call search replace class to alter engine + $parent = parent::__construct( array( + 'name' => $this->get( 'name' ), + 'user' => $this->get( 'user' ), + 'pass' => $this->get( 'pass' ), + 'host' => $this->get( 'host' ), + 'port' => $this->get( 'port' ), + 'tables' => $this->get( 'tables' ), + 'alter_collation' => 'utf8mb4_unicode_ci', + ) ); + + break; + + case 'update': + default: + + // get tables or error messages + $this->db_setup(); + + if ( $this->db_valid() ) { + + // get engines + $this->set( 'engines', $this->get_engines() ); + + // get tables + $this->set( 'all_tables', $this->get_tables() ); + + } + + break; + } + + $info = array( + 'table_select' => $this->table_select( false ), + 'engines' => $this->get( 'engines' ) + ); + + // set header again before output in case WP does it's thing + header( 'HTTP/1.1 200 OK' ); + + if ( ! $ajax ) { + $this->html( $html ); + } else { + + // return json version of results + header( 'Content-Type: application/json' ); + + echo json_encode( array( + 'errors' => $this->get( 'errors' ), + 'report' => $this->get( 'report' ), + 'info' => $info + ) ); + + exit; + + } + + } + + + public function exceptions( $exception ) { + $this->add_error( '

' . $exception->getMessage() . '

' ); + } + + public function errors( $no, $message, $file, $line ) { + $this->add_error( '

' . "{$no}: {$message} in {$file} on line {$line}" . '

', 'results' ); + } + + public function fatal_handler() { + $error = error_get_last(); + + if( $error !== NULL ) { + $errno = $error["type"]; + $errfile = $error["file"]; + $errline = $error["line"]; + $errstr = $error["message"]; + + if ( $errno == 1 ) { + header( 'HTTP/1.1 200 OK' ); + $this->add_error( '

Could not bootstrap environment.
' . "Fatal error in {$errfile}, line {$errline}. {$errstr}" . '

', 'environment' ); + $this->response(); + } + } + } + + /** + * Return an array of all files and directories recursively below $path. + * + * If $path is a file, returns an array containing just that filename. + * + * @param string $path directory/file path. + * + * @return array + */ + public function determine_all_files_below_path( $path ) { + // A file contains only 'itself'. + if ( is_file( $path ) ) { + return array( $path ); + } + + $directory_contents = glob( $path . '/*' ); + + $full_recursive_contents = array(); + + // Every directory contains all of its files, plus 'itself'. + foreach ( $directory_contents as $item_filename ) { + $full_recursive_contents = array_merge($full_recursive_contents, $this->determine_all_files_below_path( $item_filename ) ); + } + $full_recursive_contents = array_merge($full_recursive_contents, array( $path ) ); + + return $full_recursive_contents; + } + + /** + * @param $path Filename to inspect + * + * @return boolean true if it is most likely nothing to do with WordPress or Drupal. + */ + public function safe_to_delete_filename( $path ) { + // You'll have to edit this list if + // more files are included in SRDB. + + // Using an untargeted deletion operation is + // entirely unacceptable. + $srdb_filename_whitelist = array( + 'composer.json', + 'index.php', + 'package.json', + 'README.md', + 'srdb.class.php', + 'srdb.cli.php', + + 'srdb-tests', + 'charset-test.php', + 'DataSet.xml', + 'DataSetGenerator.php', + 'db.sql', + 'SrdbTest.php' + ); + + foreach ( $srdb_filename_whitelist as $whitelist_item ) { + if ( false !== stripos( $path, $whitelist_item ) ) { + return true; + } + } + + return false; + } + + /** + * Checks an array of fully qualified filenames to see if they are all + * SRDB filenames. + * + * @param array $array_of_paths + * + * @return boolean true if all paths are most likely SRDB files. + */ + public function safe_to_delete_all_filenames( $array_of_paths ) { + foreach ( $array_of_paths as $path ) { + if ( !$this->safe_to_delete_filename( $path ) ) { + return false; + } + } + + return true; + } + + const DELETE_SCRIPT_SUCCESS = 0; + const DELETE_SCRIPT_FAIL_CANT_DELETE = -1; + const DELETE_SCRIPT_FAIL_UNSAFE = -2; + + /** + * http://stackoverflow.com/questions/3349753/delete-directory-with-files-in-it + * + * @param string $path directory/file path + * + * @return integer DELETE_SCRIPT_SUCCESS for success, DELETE_SCRIPT_FAIL_CANT_DELETE for physical failure, DELETE_SCRIPT_FAIL_UNSAFE for 'Shouldn't delete wordpress' failure. + */ + public function delete_script( $path ) { + $all_targets = $this->determine_all_files_below_path( $path ); + + $all_targets_minus_containing_directory = $all_targets; + + // Proceed if all files identified (except the current directory) + // match a list of whitelisted deletable filenames. + array_pop( $all_targets_minus_containing_directory ); + $can_proceed = $this->safe_to_delete_all_filenames( $all_targets_minus_containing_directory ); + + if ( !$can_proceed ) return self::DELETE_SCRIPT_FAIL_UNSAFE; + + foreach ( $all_targets as $target_filename ) { + if ( is_file( $target_filename ) ) { + if ( false === @unlink( $target_filename ) ) return self::DELETE_SCRIPT_FAIL_CANT_DELETE; + } else { + if ( false === @rmdir( $target_filename ) ) return self::DELETE_SCRIPT_FAIL_CANT_DELETE; + } + } + + return self::DELETE_SCRIPT_SUCCESS; + } + + + /** + * Attempts to detect a WordPress installation and bootstraps the environment with it + * + * @return bool Whether it is a WP install and we have database credentials + */ + public function is_wordpress() { + + $path_mod = ''; + $depth = 0; + $max_depth = 4; + $bootstrap_file = 'wp-blog-header.php'; + + while( ! file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { + $path_mod .= '/..'; + if ( $depth++ >= $max_depth ) + break; + } + + if ( file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { + + // store WP path + $this->path = dirname( __FILE__ ) . $path_mod; + + // just in case we're white screening + try { + // need to make as many of the globals available as possible or things can break + // (globals suck) + global $wp, $wpdb, $wp_query, $wp_the_query, $wp_version, + $wp_db_version, $tinymce_version, $manifest_version, + $required_php_version, $required_mysql_version, + $post, $posts, $wp_locale, $authordata, $more, $numpages, + $currentday, $currentmonth, $page, $pages, $multipage, + $wp_rewrite, $wp_filesystem, $blog_id, $request, + $wp_styles, $wp_taxonomies, $wp_post_types, $wp_filter, + $wp_object_cache, $query_string, $single, $post_type, + $is_iphone, $is_chrome, $is_safari, $is_NS4, $is_opera, + $is_macIE, $is_winIE, $is_gecko, $is_lynx, $is_IE, + $is_apache, $is_iis7, $is_IIS; + + // prevent multisite redirect + define( 'WP_INSTALLING', true ); + + // prevent super/total cache + define( 'DONOTCACHEDB', true ); + define( 'DONOTCACHEPAGE', true ); + define( 'DONOTCACHEOBJECT', true ); + define( 'DONOTCDN', true ); + define( 'DONOTMINIFY', true ); + + // cancel batcache + if ( function_exists( 'batcache_cancel' ) ) + batcache_cancel(); + + // bootstrap WordPress + require( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ); + + $this->set( 'path', ABSPATH ); + + $this->set( 'is_wordpress', true ); + + return true; + + } catch( Exception $error ) { + + // try and get database values using regex approach + $db_details = $this->define_find( $this->path . '/wp-config.php' ); + + if ( $db_details ) { + + define( 'DB_NAME', $db_details[ 'name' ] ); + define( 'DB_USER', $db_details[ 'user' ] ); + define( 'DB_PASSWORD', $db_details[ 'pass' ] ); + define( 'DB_HOST', $db_details[ 'host' ] ); + define( 'DB_CHARSET', $db_details[ 'char' ] ); + define( 'DB_COLLATE', $db_details[ 'coll' ] ); + + // additional error message + $this->add_error( 'WordPress detected but could not bootstrap environment. There might be a PHP error, possibly caused by changes to the database', 'db' ); + + } + + if ( $db_details ) + return true; + + } + + } + + return false; + } + + + public function is_drupal() { + + $path_mod = ''; + $depth = 0; + $max_depth = 4; + $bootstrap_file = 'includes/bootstrap.inc'; + + while( ! file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { + $path_mod .= '/..'; + if ( $depth++ >= $max_depth ) + break; + } + + if ( file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { + + try { + // require the bootstrap include + require_once( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ); + + // define drupal root + if ( ! defined( 'DRUPAL_ROOT' ) ) + define( 'DRUPAL_ROOT', dirname( __FILE__ ) . $path_mod ); + + // load drupal + drupal_bootstrap( DRUPAL_BOOTSTRAP_FULL ); + + // confirm environment + $this->set( 'is_drupal', true ); + + return true; + + } catch( Exception $error ) { + // We can't add_error here as 'db' because if the db errors array is not empty, the interface doesn't activate! + // This is a consequence of the 'complete' method in JavaScript + $this->add_error( 'Drupal detected but could not bootstrap to retrieve configuration. There might be a PHP error, possibly caused by changes to the database', 'recoverable_db' ); + } + + } + + return false; + } + + + /** + * Search through the file name passed for a set of defines used to set up + * WordPress db access. + * + * @param string $filename The file name we need to scan for the defines. + * + * @return array List of db connection details. + */ + public function define_find( $filename = 'wp-config.php' ) { + + if ( $filename == 'wp-config.php' ) { + $filename = dirname( __FILE__ ) . '/' . basename( $filename ); + + // look up one directory if config file doesn't exist in current directory + if ( ! file_exists( $filename ) ) + $filename = dirname( __FILE__ ) . '/../' . basename( $filename ); + } + + if ( file_exists( $filename ) && is_file( $filename ) && is_readable( $filename ) ) { + $file = @fopen( $filename, 'r' ); + $file_content = fread( $file, filesize( $filename ) ); + @fclose( $file ); + } + + preg_match_all( '/define\s*?\(\s*?([\'"])(DB_NAME|DB_USER|DB_PASSWORD|DB_HOST|DB_CHARSET|DB_COLLATE)\1\s*?,\s*?([\'"])([^\3]*?)\3\s*?\)\s*?;/si', $file_content, $defines ); + + if ( ( isset( $defines[ 2 ] ) && ! empty( $defines[ 2 ] ) ) && ( isset( $defines[ 4 ] ) && ! empty( $defines[ 4 ] ) ) ) { + foreach( $defines[ 2 ] as $key => $define ) { + + switch( $define ) { + case 'DB_NAME': + $name = $defines[ 4 ][ $key ]; + break; + case 'DB_USER': + $user = $defines[ 4 ][ $key ]; + break; + case 'DB_PASSWORD': + $pass = $defines[ 4 ][ $key ]; + break; + case 'DB_HOST': + $host = $defines[ 4 ][ $key ]; + break; + case 'DB_CHARSET': + $char = $defines[ 4 ][ $key ]; + break; + case 'DB_COLLATE': + $coll = $defines[ 4 ][ $key ]; + break; + } + } + } + + return array( + 'host' => $host, + 'name' => $name, + 'user' => $user, + 'pass' => $pass, + 'char' => $char, + 'coll' => $coll + ); + } + + + /** + * Display the current url + * + */ + public function self_link() { + return 'http://' . $_SERVER[ 'HTTP_HOST' ] . rtrim( $_SERVER[ 'REQUEST_URI' ], '/' ); + } + + + /** + * Simple html escaping + * + * @param string $string Thing that needs escaping + * @param bool $echo Do we echo or return? + * + * @return string Escaped string. + */ + public function esc_html_attr( $string = '', $echo = false ) { + $output = htmlentities( $string, ENT_QUOTES, 'UTF-8' ); + if ( $echo ) + echo $output; + else + return $output; + } + + public function checked( $value, $value2, $echo = true ) { + $output = $value == $value2 ? ' checked="checked"' : ''; + if ( $echo ) + echo $output; + return $output; + } + + public function selected( $value, $value2, $echo = true ) { + $output = $value == $value2 ? ' selected="selected"' : ''; + if ( $echo ) + echo $output; + return $output; + } + + + public function get_errors( $type ) { + if ( ! isset( $this->errors[ $type ] ) || ! count( $this->errors[ $type ] ) ) + return; + + echo '
'; + foreach( $this->errors[ $type ] as $error ) { + if ( $error instanceof Exception ) + echo '

' . $error->getMessage() . '

'; + elseif ( is_string( $error ) ) + echo $error; + } + echo '
'; + } + + + public function get_report( $table = null ) { + + $report = $this->get( 'report' ); + + if ( empty( $report ) ) + return; + + $dry_run = $this->get( 'dry_run' ); + $search = $this->get( 'search' ); + $replace = $this->get( 'replace' ); + + // Calc the time taken. + $time = array_sum( explode( ' ', $report[ 'end' ] ) ) - array_sum( explode( ' ', $report[ 'start' ] ) ); + + $srch_rplc_input_phrase = $dry_run ? + 'searching for "' . $search . '" (to be replaced by "' . $replace . '")' : + 'replacing "' . $search . '" with "' . $replace . '"'; + + echo ' +
'; + + echo ' +

Report

'; + + echo ' +

'; + printf( + 'In the process of %s we scanned %d tables with a total of + %d rows, %d cells %s changed. + %d db updates were actually performed. + It all took %f seconds.', + $srch_rplc_input_phrase, + $report[ 'tables' ], + $report[ 'rows' ], + $report[ 'change' ], + $dry_run ? 'would have been' : 'were', + $report[ 'updates' ], + $time + ); + echo ' +

'; + + echo ' + + + + + + + + + + + '; + foreach( $report[ 'table_reports' ] as $table => $t_report ) { + + $t_time = array_sum( explode( ' ', $t_report[ 'end' ] ) ) - array_sum( explode( ' ', $t_report[ 'start' ] ) ); + + echo ' + '; + printf( ' + + + + + ', + $table, + $t_report[ 'rows' ], + $t_report[ 'change' ], + $t_report[ 'updates' ], + $t_time + ); + echo ' + '; + + } + echo ' + +
TableRowsCells changedUpdatesSeconds
%s:%d%d%d%f
'; + + echo ' +
'; + + } + + + public function table_select( $echo = true ) { + + $table_select = ''; + + if ( ! empty( $this->all_tables ) ) { + $table_select .= ''; + } + + if ( $echo ) + echo $table_select; + return $table_select; + } + + + public function ui() { + + // Warn if we're running in safe mode as we'll probably time out. + if ( ini_get( 'safe_mode' ) ) { + ?> +
+

Warning

+ Safe mode is on so you may run into problems if it takes longer than %s seconds to process your request.

', ini_get( 'max_execution_time' ) ); ?> +
+ +
+ + + + + +
+ +

db details

+ + get_errors( 'environment' ); ?> + + get_errors( 'recoverable_db' ); ?> + + get_errors( 'db' ); ?> + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ +

tables

+ + get_errors( 'tables' ); ?> + +
+ +
+ +
+ +
+ +
+ +
table_select(); ?>
+ +
+ +
+ +
+ + +
+
+ + +
+ +
+ +
+ + +
+ +

actions

+ + get_errors( 'results' ); ?> + +
+ + + + + db_valid() ) echo 'disabled="disabled"'; ?> class="db-required" /> + + db_valid() ) echo 'disabled="disabled"'; ?> class="db-required" /> + + / + + + + get( 'engines' ) ) ) { ?> + db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" /> + + + db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" /> + + db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" /> + + + +
+ + get_report(); ?> + +
+ + + +
+ +

delete

+ +
+

+ + Once you’re done click the delete me button to secure your server +

+
+ +
+ +
+ +
+ +

interconnect/it

+ +

Safe Search and Replace on Database with Serialized Data v3.1.0

+ +

This developer/sysadmin tool carries out search/replace functions on MySQL DBs and can handle serialised PHP Arrays and Objects.

+ +

WARNINGS! + Ensure data is backed up. + We take no responsibility for any damage caused by this script or its misuse. + DB Connection Settings are auto-filled when WordPress or Drupal is detected but can be confused by commented out settings so CHECK! + There is NO UNDO! + Be careful running this script on a production server.

+ +

Don't Forget to Remove Me!

+ +

Delete this utility from your + server after use by clicking the 'delete me' button. It represents a major security threat to your database if + maliciously used.

+ +

If you have feedback or want to contribute to this script click the delete button to find out how.

+ +

We don't put links on the search replace UI itself to avoid seeing URLs for the script in our access logs.

+ +

Again, use Of This Script Is Entirely At Your Own Risk

+ +

The easiest and safest way to use this script is to copy your site's files and DB to a new location. + You then, if required, fix up your .htaccess and wp-config.php appropriately. Once + done, run this script, select your tables (in most cases all of them) and then + enter the search replace strings. You can press back in your browser to do + this several times, as may be required in some cases.

+ +
+ + + + +
+ +

interconnect/it

+ + get_errors( 'delete' ); ?> + +
+

Thanks for using our search/replace tool! We’d really appreciate it if you took a + minute to join our mailing list and check out some of our other products.

+
+ +
+ + +
+ +

newsletter

+ +
+ + + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ + +
+ +

contribute

+ +
+ +

Got suggestions? Found a bug? Want to contribute code? Join us on Github!

+ +
+ +
+ +
+ +

blogs

+ + + +
+ + +
+ +

products

+ + + +
+ + + + regex ? 'regex-on' : 'regex-off'; + + ?> + + + + + interconnect/it : search replace db + + meta(); ?> + css(); ?> + js(); ?> + + + + + $body(); ?> + + + + + + + + + + + + + + + + id + content + url + serialised + + 1 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/1/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/57/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 2 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/2/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:21:"http://example.com/3/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 3 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/3/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/93/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 4 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/4/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/75/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 5 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/5/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/80/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 6 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/6/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/44/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 7 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/7/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/62/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 8 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/8/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/67/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 9 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/9/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/29/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 10 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/10/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/96/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 11 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/11/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:21:"http://example.com/9/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 12 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/12/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/82/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 13 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/13/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/48/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 14 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/14/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/78/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 15 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/15/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/84/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 16 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/16/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/93/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 17 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/17/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/47/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 18 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/18/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/47/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 19 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/19/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/36/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 20 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/20/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/80/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 21 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/21/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/46/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 22 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/22/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/21/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 23 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/23/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/35/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 24 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/24/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/77/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 25 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/25/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/87/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 26 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/26/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/51/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 27 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/27/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/27/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 28 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/28/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/28/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 29 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/29/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/86/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 30 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/30/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/78/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 31 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/31/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/83/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 32 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/32/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/95/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 33 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/33/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/57/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 34 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/34/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/98/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 35 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/35/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/85/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 36 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/36/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/20/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 37 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/37/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/44/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 38 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/38/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:21:"http://example.com/5/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 39 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/39/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/48/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 40 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/40/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/56/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 41 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/41/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/85/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 42 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/42/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/55/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 43 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/43/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/45/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 44 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/44/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/58/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 45 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/45/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/46/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 46 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/46/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/67/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 47 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/47/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/70/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 48 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/48/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/45/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 49 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/49/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/24/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ + + 50 + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+ ]]> +
+ http://example.com/50/ + a:7:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:22:"http://example.com/19/";s:6:"nested";a:6:{s:6:"number";i:123;s:5:"float";d:12.345000000000001;s:6:"string";s:17:"serialised string";s:8:"accented";s:12:"föó ßåŗ";s:7:"unicode";s:37:"❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹";s:3:"url";s:19:"http://example.com/";}} +
+ +
+
\ No newline at end of file diff --git a/srdb-tests/DataSetGenerator.php b/srdb-tests/DataSetGenerator.php new file mode 100755 index 0000000..e3db965 --- /dev/null +++ b/srdb-tests/DataSetGenerator.php @@ -0,0 +1,81 @@ +Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, + totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt + explicabo.

+

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur + magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor + sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore + magnam aliquam quaerat voluptatem.

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

+
+ Image +
+

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis + suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea + voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla + pariatur?

'; + +$serialised = array( + 'number' => 123, + 'float' => 12.345, + 'string' => 'serialised string', + 'accented' => 'föó ßåŗ', + 'unicode' => '❤ ☀ ☆ ☂ ☻ ♞ ☯ 😸 😹', + 'url' => 'http://example.com/' + ); + +$serialised[ 'nested' ] = $serialised; + +$numbers = range( 1, 100 ); +$letters = range( 'a', 'z' ); +//var_dump( $letters ); + +//mb_internal_encoding( 'UTF-8' ); + +header( 'Content-type: text/xml' ); +header( 'Charset: UTF-8' ); + +//var_dump( unserialize( serialize( $serialised ) ) ); + +echo ' + + + id + content + url + serialised'; + +for( $i = 1; $i < 51; $i++ ) { + + $s = $serialised; + $s[ 'url' ] .= $numbers[ array_rand( $numbers, 1 ) ] . '/'; + $row_content = str_replace( '~~~', $numbers[ array_rand( $numbers, 1 ) ], $content ); + $row_content = str_replace( '^^^', $letters[ array_rand( $letters, 1 ) ], $row_content ); + + echo ' + + ' . $i . ' + + + + http://example.com/' . $i . '/ + ' . serialize( $s ) . ' + + '; + + //var_dump( unserialize( serialize( $s ) ) ); + +} + +echo ' +
+
'; diff --git a/srdb-tests/SrdbTest.php b/srdb-tests/SrdbTest.php new file mode 100755 index 0000000..75b5597 --- /dev/null +++ b/srdb-tests/SrdbTest.php @@ -0,0 +1,381 @@ + '127.0.0.1', + 'name' => 'srdbtest', + 'user' => 'root', + 'pass' => '123', + 'table'=> 'posts' + ); + + public function setUp() { + parent::setUp(); + + // get class to test + require_once( dirname( __FILE__ ) . '/../srdb.class.php' ); + } + + public function tearDown() { + parent::tearDown(); + } + + /** + * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection + */ + public function getConnection() { + if ( $this->conn === null ) { + if ( self::$pdo == null ) + self::$pdo = new PDO( "mysql:host={$this->testdb['host']}", + $this->testdb[ 'user' ], + $this->testdb[ 'pass' ], + array( PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4' ) ); + + self::$pdo->query( "CREATE DATABASE IF NOT EXISTS `{$this->testdb[ 'name' ]}` CHARACTER SET = 'utf8mb4' COLLATE = 'utf8mb4_general_ci';" ); + self::$pdo->query( "CREATE TABLE IF NOT EXISTS `{$this->testdb[ 'name' ]}`.`{$this->testdb[ 'table' ]}` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `content` blob, + `url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `serialised` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;" ); + + self::$pdo->query( "USE `{$this->testdb[ 'name' ]}`;" ); + + $this->conn = $this->createDefaultDBConnection( self::$pdo, $this->testdb[ 'name' ] ); + + // Get the charset of the table. + $charset = $this->get_table_character_set( ); + if ( $charset ) + self::$pdo->query( "SET NAMES {$charset};" ); + } + return $this->conn; + } + + + public function get_table_character_set( ) { + $charset = self::$pdo->query( "SELECT c.`character_set_name` + FROM information_schema.`TABLES` t + LEFT JOIN information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` c + ON (t.`TABLE_COLLATION` = c.`COLLATION_NAME`) + WHERE t.table_schema = '{$this->testdb[ 'name' ]}' + AND t.table_name = '{$this->testdb[ 'table' ]}' + LIMIT 1;" ); + + $encoding = false; + if ( $charset ) { + $result = $charset->fetch( ); + if ( isset( $result[ 'character_set_name' ] ) ) + $encoding = $result[ 'character_set_name' ]; + } + + return $encoding; + } + + + /** + * @return PHPUnit_Extensions_Database_DataSet_IDataSet + */ + public function getDataSet() { + return $this->createXMLDataSet( dirname( __FILE__ ) . '/DataSet.xml' ); + } + + /* + * @test search replace + */ + public function testSearchReplace() { + + // search replace strings + $search = 'example.com'; + $replace = 'example.org'; + + // runs search/replace + $srdb = new icit_srdb( array_merge( array( + 'search' => $search, + 'replace' => $replace, + 'dry_run' => false + ), $this->testdb ) ); + + // results from sample data + + // no errors + $this->assertEmpty( $srdb->errors[ 'results' ], "Search replace script errors were found: \n" . implode( "\n", $srdb->errors[ 'results' ] ) ); + $this->assertEmpty( $srdb->errors[ 'db' ], "Search replace script database errors were found: \n" . implode( "\n", $srdb->errors[ 'db' ] ) ); + + // update statements run + $updates = $srdb->report[ 'updates' ]; + $this->assertEquals( 50, $updates, 'Wrong number of updates reported' ); + + // cells changed + $changes = $srdb->report[ 'change' ]; + $this->assertEquals( 150, $changes, 'Wrong number of cells changed reported' ); + + // test the database is actually changed + $modified = self::$pdo->query( "SELECT url FROM `{$this->testdb[ 'table' ]}` LIMIT 1;" )->fetchColumn(); + $this->assertRegExp( "/{$replace}/", $modified ); + + } + + public function testSearchReplaceUnicode() { + + // search replace strings + $search = 'perspiciatis'; + $replace = '😸'; + + // runs search/replace + $srdb = new icit_srdb( array_merge( array( + 'search' => $search, + 'replace' => $replace, + 'dry_run' => false + ), $this->testdb ) ); + + // results from sample data + + // no errors + $this->assertEmpty( $srdb->errors[ 'results' ], "Search replace script errors were found: \n" . implode( "\n", $srdb->errors[ 'results' ] ) ); + $this->assertEmpty( $srdb->errors[ 'db' ], "Search replace script database errors were found: \n" . implode( "\n", $srdb->errors[ 'db' ] ) ); + + // update statements run + $updates = $srdb->report[ 'updates' ]; + $this->assertEquals( 50, $updates, 'Wrong number of updates reported' ); + + // cells changed + $changes = $srdb->report[ 'change' ]; + $this->assertEquals( 50, $changes, 'Wrong number of cells changed reported' ); + + // test the database is actually changed + $modified = self::$pdo->query( "SELECT content FROM `{$this->testdb[ 'table' ]}` LIMIT 1;" )->fetchColumn(); + $this->assertRegExp( "/{$replace}/", $modified ); + + } + + + /* + * @test str_replace regex + */ + public function testRegexReplace() { + + // search replace strings + $search = '#https?://([a-z0-9\.-]+)/?#'; + $replace = 'https://$1/'; + + // class instance with regex enabled + $srdb = new icit_srdb( array_merge( array( + 'search' => $search, + 'replace' => $replace, + 'regex' => true, + 'dry_run' => false + ), $this->testdb ) ); + + // direct method invocation + $subject = 'http://example.com/'; + $result = 'https://example.com/'; + $replaced = $srdb->str_replace( $search, $replace, $subject ); + $this->assertEquals( $result, $replaced ); + + // results from sample data + + // no errors + $this->assertEmpty( $srdb->errors[ 'results' ], "Search replace script errors were found: \n" . implode( "\n", $srdb->errors[ 'results' ] ) ); + $this->assertEmpty( $srdb->errors[ 'db' ], "Search replace script database errors were found: \n" . implode( "\n", $srdb->errors[ 'db' ] ) ); + + // update statements run + $updates = $srdb->report[ 'updates' ]; + $this->assertEquals( 50, $updates, 'Wrong number of updates reported' ); + + // cells changed + $changes = $srdb->report[ 'change' ]; + $this->assertEquals( 150, $changes, 'Wrong number of changes reported' ); + + // test the database is actually changed + $modified = self::$pdo->query( "SELECT url FROM `{$this->testdb[ 'table' ]}` LIMIT 1;" )->fetchColumn(); + $this->assertRegExp( "#{$result}#", $modified, 'Database not updated, modified result is ' . $modified ); + + } + + /** + * @test str_replace serialised data + */ + public function testStrReplaceSerialised() { + + // search replace strings + $search = 'serialised string'; + $replace = 'longer serialised string'; + + // class instance with regex enabled + $srdb = new icit_srdb( array_merge( array( + 'search' => $search, + 'replace' => $replace, + 'dry_run' => false + ), $this->testdb ) ); + + // results from sample data + + // no errors + $this->assertEmpty( $srdb->errors[ 'results' ], "Search replace script errors were found: \n" . implode( "\n", $srdb->errors[ 'results' ] ) ); + $this->assertEmpty( $srdb->errors[ 'db' ], "Search replace script database errors were found: \n" . implode( "\n", $srdb->errors[ 'db' ] ) ); + + // update statements run + $updates = $srdb->report[ 'updates' ]; + $this->assertEquals( 50, $updates, 'Wrong number of updates reported' ); + + // cells changed + $changes = $srdb->report[ 'change' ]; + $this->assertEquals( 50, $changes, 'Wrong number of changes reported' ); + + // check unserialised values are what they should be + $modified = self::$pdo->query( "SELECT serialised FROM `{$this->testdb[ 'table' ]}` LIMIT 1;" )->fetchColumn(); + $from = unserialize( $modified ); + + $this->assertEquals( $replace, $from[ 'string' ], 'Unserialised array value not updated' ); + + } + + /* + * @test recursive unserialize replace + */ + public function testRecursiveUnserializeReplace() { + + // search replace strings + $search = 'serialised string'; + $replace = 'longer serialised string'; + + // class instance with regex enabled + $srdb = new icit_srdb( array_merge( array( + 'search' => $search, + 'replace' => $replace, + 'dry_run' => false + ), $this->testdb ) ); + + // results from sample data + + // no errors + $this->assertEmpty( $srdb->errors[ 'results' ], "Search replace script errors were found: \n" . implode( "\n", $srdb->errors[ 'results' ] ) ); + $this->assertEmpty( $srdb->errors[ 'db' ], "Search replace script database errors were found: \n" . implode( "\n", $srdb->errors[ 'db' ] ) ); + + // check unserialised values are what they should be + $modified = self::$pdo->query( "SELECT serialised FROM `{$this->testdb[ 'table' ]}` LIMIT 1;" )->fetchColumn(); + $from = unserialize( $modified ); + + $this->assertEquals( $replace, $from[ 'nested' ][ 'string' ], 'Unserialised nested array value not updated' ); + + } + + /* + * @test include columns + */ + public function testIncludeColumns() { + + // search replace strings + $search = 'example.com'; + $replace = 'example.org'; + + // class instance with regex enabled + $srdb = new icit_srdb( array_merge( array( + 'search' => $search, + 'replace' => $replace, + 'dry_run' => false, + 'include_cols' => array( 'url' ) + ), $this->testdb ) ); + + // results from sample data + + // no errors + $this->assertEmpty( $srdb->errors[ 'results' ], "Search replace script errors were found: \n" . implode( "\n", $srdb->errors[ 'results' ] ) ); + $this->assertEmpty( $srdb->errors[ 'db' ], "Search replace script database errors were found: \n" . implode( "\n", $srdb->errors[ 'db' ] ) ); + + // update statements run + $updates = $srdb->report[ 'updates' ]; + $this->assertEquals( 50, $updates, 'Wrong number of updates reported' ); + + // cells changed + $changes = $srdb->report[ 'change' ]; + $this->assertEquals( 50, $changes, 'Wrong number of changes reported' ); + + + // check unserialised values are what they should be + $modified = self::$pdo->query( "SELECT content, url FROM `{$this->testdb[ 'table' ]}` LIMIT 1;" )->fetchAll(); + $content = $modified[ 0 ][ 'content' ]; + $url = $modified[ 0 ][ 'url' ]; + + $this->assertRegExp( "/$search/", $content, 'Content column was modified' ); + $this->assertRegExp( "/$replace/", $url, 'URL column was not modified' ); + + } + + /* + * @test exclude columns + */ + public function testExcludeColumns() { + + // search replace strings + $search = 'example.com'; + $replace = 'example.org'; + + // class instance with regex enabled + $srdb = new icit_srdb( array_merge( array( + 'search' => $search, + 'replace' => $replace, + 'dry_run' => false, + 'exclude_cols' => array( 'url' ) + ), $this->testdb ) ); + + // results from sample data + + // no errors + $this->assertEmpty( $srdb->errors[ 'results' ], "Search replace script errors were found: \n" . implode( "\n", $srdb->errors[ 'results' ] ) ); + $this->assertEmpty( $srdb->errors[ 'db' ], "Search replace script database errors were found: \n" . implode( "\n", $srdb->errors[ 'db' ] ) ); + + // update statements run + $updates = $srdb->report[ 'updates' ]; + $this->assertEquals( 50, $updates, 'Wrong number of updates reported' ); + + // cells changed + $changes = $srdb->report[ 'change' ]; + $this->assertEquals( 100, $changes, 'Wrong number of changes reported' ); + + // check unserialised values are what they should be + $modified = self::$pdo->query( "SELECT content, url FROM `{$this->testdb[ 'table' ]}` LIMIT 1;" )->fetchAll(); + $content = $modified[ 0 ][ 'content' ]; + $url = $modified[ 0 ][ 'url' ]; + + $this->assertRegExp( "/$replace/", $content, 'Content column was not modified' ); + $this->assertRegExp( "/$search/", $url, 'URL column was modified' ); + + } + + /** + * @test multibyte string replacement method + */ + public function testMultibyteStrReplace() { + + $subject = 'föö ❤ ☀ ☆ ☂ ☻ ♞ ☯'; + $result = 'föö ❤ ☻ ♞ ☯ ☻ ♞ ☯'; + $replaced = icit_srdb::mb_str_replace( '☀ ☆ ☂', '☻ ♞ ☯', $subject ); + + $this->assertEquals( $result, $replaced ); + + } + +} diff --git a/srdb-tests/charset-test.php b/srdb-tests/charset-test.php new file mode 100755 index 0000000..846ef21 --- /dev/null +++ b/srdb-tests/charset-test.php @@ -0,0 +1,93 @@ + 123, + 'float' => 12.345, + 'string' => 'serialised string', + 'accented' => 'föó ßåŗ', + 'unicode' => '❤ ☀ ☆ ☂ ☻ ♞ ☯', + 'url' => 'http://example.com/' + ); +$serialised = serialize( $original ); + +// Connect +$x = new PDO( "mysql:host={$host}", $user, $pass ); + +// Create our schema +$x->query( "CREATE DATABASE IF NOT EXISTS `encode` CHARACTER SET = 'utf8' COLLATE = 'utf8_general_ci';" ); +$x->query( 'SET NAMES utf8;' ); + +// Create a table for each encoding type and stick the encoded array in it. +$charsets = $x->query( 'SELECT CHARACTER_SET_NAME as charset, COLLATION_NAME as collation FROM information_schema.COLLATION_CHARACTER_SET_APPLICABILITY;' ); + +if ( method_exists( $charsets, 'fetch' ) ) { + while( $collation = $charsets->fetch() ) { + + $col = $collation[ 'collation' ]; + $charset = $collation[ 'charset' ]; + $tbl_name = $collation[ 'collation' ]; + + // Create the table for the collation + $x->query( "DROP TABLE IF EXISTS `encode`.`{$tbl_name}`" ); + $x->query( "CREATE TABLE `encode`.`{$tbl_name}` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `number` decimal(10,0) NOT NULL, + `float` float NOT NULL, + `string` longtext COLLATE {$col} NOT NULL, + `accented` longtext COLLATE {$col} NOT NULL, + `unicode` longtext COLLATE {$col} NOT NULL, + `url` longtext COLLATE {$col} NOT NULL, + `serialised` longtext CHARACTER SET {$charset} NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET={$charset} COLLATE={$col};" ); + + // Set the name space to match to charset + switch( $charset ) { + // If I uncomment this utf-16 and utf-32 work, I don't know why though. + //case 'utf16': + //case 'utf32': + // $x->query( "SET NAMES utf8;" ); + // break; + + default: + $x->query( "SET NAMES {$charset};" ); + } + + // Insert our test data + if ( !$x->query( "INSERT INTO encode.`{$tbl_name}` ( number, float, string, accented, unicode, url, serialised ) + VALUES ( + {$original['number']}, + {$original['float']}, + '{$original['string']}', + '{$original['accented']}', + '{$original['unicode']}', + '{$original['url']}', + '{$serialised}' + );" ) ) + echo "
Insert Failed: {$col}:{$charset}
"; + + // Set names to match table's charset + $x->query( "SET NAMES {$charset};" ); + + // Reclaim what we just dumped into the db and compare + $q = $x->query( "SELECT serialised FROM encode.{$tbl_name} ORDER BY id DESC LIMIT 1;" ); + if ( method_exists( $q, 'fetch' ) ) { + while( $var = $q->fetch( )[0] ) { + $unserialized = @unserialize( $var ); + + if ( !$unserialized || array_diff( $unserialized, $original ) ) { + echo "
Failed: {$col}:{$charset}
"; + } + else { + echo "
Success: {$col}:{$charset}
"; + } + } + } + } +} diff --git a/srdb-tests/db.sql b/srdb-tests/db.sql new file mode 100755 index 0000000..cc4da09 --- /dev/null +++ b/srdb-tests/db.sql @@ -0,0 +1,45 @@ +CREATE DATABASE IF NOT EXISTS `srdbtest` /*!40100 DEFAULT CHARACTER SET utf8 */; +USE `srdbtest`; +-- MySQL dump 10.13 Distrib 5.5.16, for Win32 (x86) +-- +-- Host: localhost Database: srdbtest +-- ------------------------------------------------------ +-- Server version 5.5.24-log + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `posts` +-- + +DROP TABLE IF EXISTS `posts`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `posts` ( + `id` int(11) NOT NULL, + `content` blob, + `url` varchar(1024) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `serialised` varchar(2000) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2013-11-14 10:36:55 diff --git a/srdb.class.php b/srdb.class.php new file mode 100755 index 0000000..224e111 --- /dev/null +++ b/srdb.class.php @@ -0,0 +1,1217 @@ + array(), + 'db' => array(), + 'tables' => array(), + 'results' => array() + ); + + public $error_type = 'search'; + + + /** + * @var array Stores the report array + */ + public $report = array(); + + + /** + * @var int Number of modifications to return in report array + */ + public $report_change_num = 30; + + + /** + * @var bool Whether to echo report as script runs + */ + public $verbose = false; + + + /** + * @var resource Database connection + */ + public $db; + + + /** + * @var use PDO + */ + public $use_pdo = true; + + + /** + * @var int How many rows to select at a time when replacing + */ + public $page_size = 50000; + + + /** + * Searches for WP or Drupal context + * Checks for $_POST data + * Initialises database connection + * Handles ajax + * Runs replacement + * + * @param string $name database name + * @param string $user database username + * @param string $pass database password + * @param string $host database hostname + * @param string $port database connection port + * @param string $search search string / regex + * @param string $replace replacement string + * @param array $tables tables to run replcements against + * @param bool $live live run + * @param array $exclude_cols tables to run replcements against + * + * @return void + */ + public function __construct( $args ) { + + $args = array_merge( array( + 'name' => '', + 'user' => '', + 'pass' => '', + 'host' => '', + 'port' => 3306, + 'search' => '', + 'replace' => '', + 'tables' => array(), + 'exclude_cols' => array(), + 'include_cols' => array(), + 'dry_run' => true, + 'regex' => false, + 'pagesize' => 50000, + 'alter_engine' => false, + 'alter_collation' => false, + 'verbose' => false + ), $args ); + + // handle exceptions + set_exception_handler( array( $this, 'exceptions' ) ); + + // handle errors + set_error_handler( array( $this, 'errors' ), E_ERROR | E_WARNING ); + + // Setting this so that mb_split works correctly. + // BEAR IN MIND that this affects the handling of strings INTERNALLY rather than + // at the html output interface, the console interface, json interface, or the database interface. + // This means that if the DB has a different charset (utf16?), we need to make sure that it's + // normalised to utf-8 internally and output in the appropriate charset. + mb_regex_encoding( 'UTF-8' ); + + // allow a string for columns + foreach( array( 'exclude_cols', 'include_cols', 'tables' ) as $maybe_string_arg ) { + if ( is_string( $args[ $maybe_string_arg ] ) ) + $args[ $maybe_string_arg ] = array_filter( array_map( 'trim', explode( ',', $args[ $maybe_string_arg ] ) ) ); + } + + // verify that the port number is logical + // work around PHPs inability to stringify a zero without making it an empty string + // AND without casting away trailing characters if they are present. + $port_as_string = (string)$args['port'] ? (string)$args['port'] : "0"; + if ( (string)abs( (int)$args['port'] ) !== $port_as_string ) { + $port_error = 'Port number must be a positive integer if specified.'; + $this->add_error( $port_error, 'db' ); + if ( defined( 'STDIN' ) ) { + echo 'Error: ' . $port_error; + } + return; + } + + // set class vars + foreach( $args as $name => $value ) { + if ( is_string( $value ) ) + $value = stripcslashes( $value ); + if ( is_array( $value ) ) + $value = array_map( 'stripcslashes', $value ); + $this->set( $name, $value ); + } + + // only for non cli call, cli set no timeout, no memory limit + if( ! defined( 'STDIN' ) ) { + + // increase time out limit + @set_time_limit( 60 * 10 ); + + // try to push the allowed memory up, while we're at it + @ini_set( 'memory_limit', '1024M' ); + + } + + // set up db connection + $this->db_setup(); + + if ( $this->db_valid() ) { + + // update engines + if ( $this->alter_engine ) { + $report = $this->update_engine( $this->alter_engine, $this->tables ); + } + + // update collation + elseif ( $this->alter_collation ) { + $report = $this->update_collation( $this->alter_collation, $this->tables ); + } + + // default search/replace action + else { + $report = $this->replacer( $this->search, $this->replace, $this->tables ); + } + + } else { + + $report = $this->report; + + } + + // store report + $this->set( 'report', $report ); + return $report; + } + + + /** + * Terminates db connection + * + * @return void + */ + public function __destruct() { + if ( $this->db_valid() ) + $this->db_close(); + } + + + public function get( $property ) { + return $this->$property; + } + + public function set( $property, $value ) { + $this->$property = $value; + } + + + public function exceptions( $exception ) { + echo $exception->getMessage() . "\n"; + } + + + public function errors( $no, $message, $file, $line ) { + echo $message . "\n"; + } + + + public function log( $type = '' ) { + $args = array_slice( func_get_args(), 1 ); + if ( $this->get( 'verbose' ) ) { + echo "{$type}: "; + print_r( $args ); + echo "\n"; + } + return $args; + } + + + public function add_error( $error, $type = null ) { + if ( $type !== null ) + $this->error_type = $type; + $this->errors[ $this->error_type ][] = $error; + $this->log( 'error', $this->error_type, $error ); + } + + + public function use_pdo() { + return $this->get( 'use_pdo' ); + } + + + /** + * Setup connection, populate tables array + * Also responsible for selecting the type of connection to use. + * + * @return void + */ + public function db_setup() { + $mysqli_available = class_exists( 'mysqli' ); + $pdo_available = class_exists( 'PDO' ); + + $connection_type = ''; + + // Default to mysqli type. + // Only advance to PDO if all conditions are met. + if ( $mysqli_available ) + { + $connection_type = 'mysqli'; + } + + if ( $pdo_available ) { + // PDO is the interface, but it may not have the 'mysql' module. + $mysql_driver_present = in_array( 'mysql', pdo_drivers() ); + + if ( $mysql_driver_present ) { + $connection_type = 'pdo'; + } + } + + // Abort if mysqli and PDO are both broken. + if ( '' === $connection_type ) + { + $this->add_error( 'Could not find any MySQL database drivers. (MySQLi or PDO required.)', 'db' ); + return false; + } + + // connect + $this->set( 'db', $this->connect( $connection_type ) ); + + } + + + /** + * Database connection type router + * + * @param string $type + * + * @return callback + */ + public function connect( $type = '' ) { + $method = "connect_{$type}"; + return $this->$method(); + } + + + /** + * Creates the database connection using newer mysqli functions + * + * @return resource|bool + */ + public function connect_mysqli() { + + // switch off PDO + $this->set( 'use_pdo', false ); + + $connection = @mysqli_connect( $this->host, $this->user, $this->pass, $this->name, $this->port ); + + // unset if not available + if ( ! $connection ) { + $this->add_error( mysqli_connect_error( ), 'db' ); + $connection = false; + } + + return $connection; + } + + + /** + * Sets up database connection using PDO + * + * @return PDO|bool + */ + public function connect_pdo() { + + try { + $connection = new PDO( "mysql:host={$this->host};port={$this->port};dbname={$this->name}", $this->user, $this->pass ); + } catch( PDOException $e ) { + $this->add_error( $e->getMessage(), 'db' ); + $connection = false; + } + + // check if there's a problem with our database at this stage + if ( $connection && ! $connection->query( 'SHOW TABLES' ) ) { + $error_info = $connection->errorInfo(); + if ( !empty( $error_info ) && is_array( $error_info ) ) + $this->add_error( array_pop( $error_info ), 'db' ); // Array pop will only accept a $var.. + $connection = false; + } + + return $connection; + } + + + /** + * Retrieve all tables from the database + * + * @return array + */ + public function get_tables() { + // get tables + + // A clone of show table status but with character set for the table. + $show_table_status = "SELECT + t.`TABLE_NAME` as Name, + t.`ENGINE` as `Engine`, + t.`version` as `Version`, + t.`ROW_FORMAT` AS `Row_format`, + t.`TABLE_ROWS` AS `Rows`, + t.`AVG_ROW_LENGTH` AS `Avg_row_length`, + t.`DATA_LENGTH` AS `Data_length`, + t.`MAX_DATA_LENGTH` AS `Max_data_length`, + t.`INDEX_LENGTH` AS `Index_length`, + t.`DATA_FREE` AS `Data_free`, + t.`AUTO_INCREMENT` as `Auto_increment`, + t.`CREATE_TIME` AS `Create_time`, + t.`UPDATE_TIME` AS `Update_time`, + t.`CHECK_TIME` AS `Check_time`, + t.`TABLE_COLLATION` as Collation, + c.`CHARACTER_SET_NAME` as Character_set, + t.`Checksum`, + t.`Create_options`, + t.`table_Comment` as `Comment` + FROM information_schema.`TABLES` t + LEFT JOIN information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` c + ON ( t.`TABLE_COLLATION` = c.`COLLATION_NAME` ) + WHERE t.`TABLE_SCHEMA` = '{$this->name}'; + "; + + $all_tables_mysql = $this->db_query( $show_table_status ); + $all_tables = array(); + + if ( ! $all_tables_mysql ) { + + $this->add_error( $this->db_error( ), 'db' ); + + } else { + + // set the character set + //$this->db_set_charset( $this->get( 'charset' ) ); + + while ( $table = $this->db_fetch( $all_tables_mysql ) ) { + // ignore views + if ( $table[ 'Comment' ] == 'VIEW' ) + continue; + + $all_tables[ $table[0] ] = $table; + } + + } + + return $all_tables; + } + + + /** + * Get the character set for the current table + * + * @param string $table_name The name of the table we want to get the char + * set for + * + * @return string The character encoding; + */ + public function get_table_character_set( $table_name = '' ) { + $table_name = $this->db_escape( $table_name ); + $schema = $this->db_escape( $this->name ); + + $charset = $this->db_query( "SELECT c.`character_set_name` + FROM information_schema.`TABLES` t + LEFT JOIN information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` c + ON (t.`TABLE_COLLATION` = c.`COLLATION_NAME`) + WHERE t.table_schema = {$schema} + AND t.table_name = {$table_name} + LIMIT 1;" ); + + $encoding = false; + if ( ! $charset ) { + $this->add_error( $this->db_error( ), 'db' ); + } + else { + $result = $this->db_fetch( $charset ); + $encoding = isset( $result[ 'character_set_name' ] ) ? $result[ 'character_set_name' ] : false; + } + + return $encoding; + } + + + /** + * Retrieve all supported database engines + * + * @return array + */ + public function get_engines() { + + // get available engines + $mysql_engines = $this->db_query( 'SHOW ENGINES;' ); + $engines = array(); + + if ( ! $mysql_engines ) { + $this->add_error( $this->db_error( ), 'db' ); + } else { + while ( $engine = $this->db_fetch( $mysql_engines ) ) { + if ( in_array( $engine[ 'Support' ], array( 'YES', 'DEFAULT' ) ) ) + $engines[] = $engine[ 'Engine' ]; + } + } + + return $engines; + } + + + public function db_query( $query ) { + if ( $this->use_pdo() ) + return $this->db->query( $query ); + else + return mysqli_query( $this->db, $query ); + } + + public function db_update( $query ) { + if ( $this->use_pdo() ) + return $this->db->exec( $query ); + else + return mysqli_query( $this->db, $query ); + } + + public function db_error() { + if ( $this->use_pdo() ) { + $error_info = $this->db->errorInfo(); + return !empty( $error_info ) && is_array( $error_info ) ? array_pop( $error_info ) : 'Unknown error'; + } + else + return mysqli_error( $this->db ); + } + + public function db_fetch( $data ) { + if ( $this->use_pdo() ) + return $data->fetch(); + else + return mysqli_fetch_array( $data ); + } + + public function db_escape( $string ) { + if ( $this->use_pdo() ) + return $this->db->quote( $string ); + else + return "'" . mysqli_real_escape_string( $this->db, $string ) . "'"; + } + + public function db_free_result( $data ) { + if ( $this->use_pdo() ) + return $data->closeCursor(); + else + return mysqli_free_result( $data ); + } + + public function db_set_charset( $charset = '' ) { + if ( ! empty( $charset ) ) { + if ( ! $this->use_pdo() && function_exists( 'mysqli_set_charset' ) ) + mysqli_set_charset( $this->db, $charset ); + else + $this->db_query( 'SET NAMES ' . $charset ); + } + } + + public function db_close() { + if ( $this->use_pdo() ) + unset( $this->db ); + else + mysqli_close( $this->db ); + } + + public function db_valid() { + return (bool)$this->db; + } + + + /** + * Walk an array replacing one element for another. ( NOT USED ANY MORE ) + * + * @param string $find The string we want to replace. + * @param string $replace What we'll be replacing it with. + * @param array $data Used to pass any subordinate arrays back to the + * function for searching. + * + * @return array The original array with the replacements made. + */ + public function recursive_array_replace( $find, $replace, $data ) { + if ( is_array( $data ) ) { + foreach ( $data as $key => $value ) { + if ( is_array( $value ) ) { + $this->recursive_array_replace( $find, $replace, $data[ $key ] ); + } else { + // have to check if it's string to ensure no switching to string for booleans/numbers/nulls - don't need any nasty conversions + if ( is_string( $value ) ) + $data[ $key ] = $this->str_replace( $find, $replace, $value ); + } + } + } else { + if ( is_string( $data ) ) + $data = $this->str_replace( $find, $replace, $data ); + } + } + + + /** + * Take a serialised array and unserialise it replacing elements as needed and + * unserialising any subordinate arrays and performing the replace on those too. + * + * @param string $from String we're looking to replace. + * @param string $to What we want it to be replaced with + * @param array $data Used to pass any subordinate arrays back to in. + * @param bool $serialised Does the array passed via $data need serialising. + * + * @return array The original array with all elements replaced as needed. + */ + public function recursive_unserialize_replace( $from = '', $to = '', $data = '', $serialised = false ) { + + // some unserialised data cannot be re-serialised eg. SimpleXMLElements + try { + + if ( is_string( $data ) && ( $unserialized = @unserialize( $data ) ) !== false ) { + $data = $this->recursive_unserialize_replace( $from, $to, $unserialized, true ); + } + + elseif ( is_array( $data ) ) { + $_tmp = array( ); + foreach ( $data as $key => $value ) { + $_tmp[ $key ] = $this->recursive_unserialize_replace( $from, $to, $value, false ); + } + + $data = $_tmp; + unset( $_tmp ); + } + + // Submitted by Tina Matter + elseif ( is_object( $data ) ) { + // $data_class = get_class( $data ); + $_tmp = $data; // new $data_class( ); + $props = get_object_vars( $data ); + foreach ( $props as $key => $value ) { + $_tmp->$key = $this->recursive_unserialize_replace( $from, $to, $value, false ); + } + + $data = $_tmp; + unset( $_tmp ); + } + + else { + if ( is_string( $data ) ) { + $data = $this->str_replace( $from, $to, $data ); + + } + } + + if ( $serialised ) + return serialize( $data ); + + } catch( Exception $error ) { + + $this->add_error( $error->getMessage(), 'results' ); + + } + + return $data; + } + + + /** + * Regular expression callback to fix serialised string lengths + * + * @param array $matches matches from the regular expression + * + * @return string + */ + public function preg_fix_serialised_count( $matches ) { + $length = mb_strlen( $matches[ 2 ] ); + if ( $length !== intval( $matches[ 1 ] ) ) + return "s:{$length}:\"{$matches[2]}\";"; + return $matches[ 0 ]; + } + + + /** + * The main loop triggered in step 5. Up here to keep it out of the way of the + * HTML. This walks every table in the db that was selected in step 3 and then + * walks every row and column replacing all occurences of a string with another. + * We split large tables into 50,000 row blocks when dealing with them to save + * on memmory consumption. + * + * @param string $search What we want to replace + * @param string $replace What we want to replace it with. + * @param array $tables The tables we want to look at. + * + * @return array Collection of information gathered during the run. + */ + public function replacer( $search = '', $replace = '', $tables = array( ) ) { + $search = (string)$search; + // check we have a search string, bail if not + if ( '' === $search ) { + $this->add_error( 'Search string is empty', 'search' ); + return false; + } + + $report = array( 'tables' => 0, + 'rows' => 0, + 'change' => 0, + 'updates' => 0, + 'start' => microtime( ), + 'end' => microtime( ), + 'errors' => array( ), + 'table_reports' => array( ) + ); + + $table_report = array( + 'rows' => 0, + 'change' => 0, + 'changes' => array( ), + 'updates' => 0, + 'start' => microtime( ), + 'end' => microtime( ), + 'errors' => array( ), + ); + + $dry_run = $this->get( 'dry_run' ); + + if ( $this->get( 'dry_run' ) ) // Report this as a search-only run. + $this->add_error( 'The dry-run option was selected. No replacements will be made.', 'results' ); + + // if no tables selected assume all + if ( empty( $tables ) ) { + $all_tables = $this->get_tables(); + $tables = array_keys( $all_tables ); + } + + if ( is_array( $tables ) && ! empty( $tables ) ) { + + foreach( $tables as $table ) { + + $encoding = $this->get_table_character_set( $table ); + switch( $encoding ) { + + // Tables encoded with this work for me only when I set names to utf8. I don't trust this in the wild so I'm going to avoid. + case 'utf16': + case 'utf32': + //$encoding = 'utf8'; + $this->add_error( "The table \"{$table}\" is encoded using \"{$encoding}\" which is currently unsupported.", 'results' ); + continue; + break; + + default: + $this->db_set_charset( $encoding ); + break; + } + + + $report[ 'tables' ]++; + + // get primary key and columns + list( $primary_key, $columns ) = $this->get_columns( $table ); + + if ( $primary_key === null || empty( $primary_key ) ) { + $this->add_error( "The table \"{$table}\" has no primary key. Changes will have to be made manually.", 'results' ); + continue; + } + + // create new table report instance + $new_table_report = $table_report; + $new_table_report[ 'start' ] = microtime(); + + $this->log( 'search_replace_table_start', $table, $search, $replace ); + + // Count the number of rows we have in the table if large we'll split into blocks, This is a mod from Simon Wheatley + $row_count = $this->db_query( "SELECT COUNT(*) FROM `{$table}`" ); + $rows_result = $this->db_fetch( $row_count ); + $row_count = $rows_result[ 0 ]; + + $page_size = $this->get( 'page_size' ); + $pages = ceil( $row_count / $page_size ); + + for( $page = 0; $page < $pages; $page++ ) { + + $start = $page * $page_size; + + // Grab the content of the table + $data = $this->db_query( sprintf( 'SELECT * FROM `%s` LIMIT %d, %d', $table, $start, $page_size ) ); + + if ( ! $data ) + $this->add_error( $this->db_error( ), 'results' ); + + while ( $row = $this->db_fetch( $data ) ) { + + $report[ 'rows' ]++; // Increment the row counter + $new_table_report[ 'rows' ]++; + + $update_sql = array( ); + $where_sql = array( ); + $update = false; + + foreach( $columns as $column ) { + + $edited_data = $data_to_fix = $row[ $column ]; + + if ( in_array( $column, $primary_key ) ) { + $where_sql[] = "`{$column}` = " . $this->db_escape( $data_to_fix ); + continue; + } + + // exclude cols + if ( in_array( $column, $this->exclude_cols ) ) + continue; + + // include cols + if ( ! empty( $this->include_cols ) && ! in_array( $column, $this->include_cols ) ) + continue; + + // Run a search replace on the data that'll respect the serialisation. + $edited_data = $this->recursive_unserialize_replace( $search, $replace, $data_to_fix ); + + // Something was changed + if ( $edited_data != $data_to_fix ) { + + $report[ 'change' ]++; + $new_table_report[ 'change' ]++; + + // log first x changes + if ( $new_table_report[ 'change' ] <= $this->get( 'report_change_num' ) ) { + $new_table_report[ 'changes' ][] = array( + 'row' => $new_table_report[ 'rows' ], + 'column' => $column, + 'from' => ( $data_to_fix ), + 'to' => ( $edited_data ) + ); + } + + $update_sql[] = "`{$column}` = " . $this->db_escape( $edited_data ); + $update = true; + + } + + } + + if ( $dry_run ) { + // nothing for this state + } elseif ( $update && ! empty( $where_sql ) ) { + + $sql = 'UPDATE ' . $table . ' SET ' . implode( ', ', $update_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) ); + + $result = $this->db_update( $sql ); + + if ( ! is_int( $result ) && ! $result ) { + + $this->add_error( $this->db_error( ), 'results' ); + + } else { + + $report[ 'updates' ]++; + $new_table_report[ 'updates' ]++; + } + + } + + } + + $this->db_free_result( $data ); + + } + + $new_table_report[ 'end' ] = microtime(); + + // store table report in main + $report[ 'table_reports' ][ $table ] = $new_table_report; + + // log result + $this->log( 'search_replace_table_end', $table, $new_table_report ); + } + + } + + $report[ 'end' ] = microtime( ); + + $this->log( 'search_replace_end', $search, $replace, $report ); + + return $report; + } + + + public function get_columns( $table ) { + $primary_key = array(); + $columns = array( ); + + // Get a list of columns in this table + $fields = $this->db_query( "DESCRIBE {$table}" ); + if ( ! $fields ) { + $this->add_error( $this->db_error( ), 'db' ); + } else { + while( $column = $this->db_fetch( $fields ) ) { + $columns[] = $column[ 'Field' ]; + if ( $column[ 'Key' ] == 'PRI' ) + $primary_key[] = $column[ 'Field' ]; + } + } + + return array( $primary_key, $columns ); + } + + + public function do_column() { + + } + + + /** + * Convert table engines + * + * @param string $engine Engine type + * @param array $tables + * + * @return array Modification report + */ + public function update_engine( $engine = 'MyISAM', $tables = array() ) { + + $report = false; + + if ( empty( $this->engines ) ) + $this->set( 'engines', $this->get_engines() ); + + if ( in_array( $engine, $this->get( 'engines' ) ) ) { + + $report = array( 'engine' => $engine, 'converted' => array() ); + + $all_tables = $this->get_tables(); + + if ( empty( $tables ) ) { + $tables = array_keys( $all_tables ); + } + + foreach( $tables as $table ) { + $table_info = $all_tables[ $table ]; + + // are we updating the engine? + if ( $table_info[ 'Engine' ] != $engine ) { + $engine_converted = $this->db_query( "alter table {$table} engine = {$engine};" ); + if ( ! $engine_converted ) + $this->add_error( $this->db_error( ), 'results' ); + else + $report[ 'converted' ][ $table ] = true; + continue; + } else { + $report[ 'converted' ][ $table ] = false; + } + + if ( isset( $report[ 'converted' ][ $table ] ) ) + $this->log( 'update_engine', $table, $report, $engine ); + } + + } else { + + $this->add_error( 'Cannot convert tables to unsupported table engine ”' . $engine . '“', 'results' ); + + } + + return $report; + } + + + /** + * Updates the characterset and collation on the specified tables + * + * @param string $collate table collation + * @param array $tables tables to modify + * + * @return array Modification report + */ + public function update_collation( $collation = 'utf8_unicode_ci', $tables = array() ) { + + $report = false; + + if ( is_string( $collation ) ) { + + $report = array( 'collation' => $collation, 'converted' => array() ); + + $all_tables = $this->get_tables(); + + if ( empty( $tables ) ) { + $tables = array_keys( $all_tables ); + } + + // charset is same as collation up to first underscore + $charset = preg_replace( '/^([^_]+).*$/', '$1', $collation ); + + foreach( $tables as $table ) { + $table_info = $all_tables[ $table ]; + + // are we updating the engine? + if ( $table_info[ 'Collation' ] != $collation ) { + $engine_converted = $this->db_query( "alter table {$table} convert to character set {$charset} collate {$collation};" ); + if ( ! $engine_converted ) + $this->add_error( $this->db_error( ), 'results' ); + else + $report[ 'converted' ][ $table ] = true; + continue; + } else { + $report[ 'converted' ][ $table ] = false; + } + + if ( isset( $report[ 'converted' ][ $table ] ) ) + $this->log( 'update_collation', $table, $report, $collation ); + } + + } else { + + $this->add_error( 'Collation must be a valid string', 'results' ); + + } + + return $report; + } + + + /** + * Replace all occurrences of the search string with the replacement string. + * + * @author Sean Murphy + * @copyright Copyright 2012 Sean Murphy. All rights reserved. + * @license http://creativecommons.org/publicdomain/zero/1.0/ + * @link http://php.net/manual/function.str-replace.php + * + * @param mixed $search + * @param mixed $replace + * @param mixed $subject + * @param int $count + * @return mixed + */ + public static function mb_str_replace( $search, $replace, $subject, &$count = 0 ) { + if ( ! is_array( $subject ) ) { + // Normalize $search and $replace so they are both arrays of the same length + $searches = is_array( $search ) ? array_values( $search ) : array( $search ); + $replacements = is_array( $replace ) ? array_values( $replace ) : array( $replace ); + $replacements = array_pad( $replacements, count( $searches ), '' ); + + foreach ( $searches as $key => $search ) { + $parts = mb_split( preg_quote( $search ), $subject ); + $count += count( $parts ) - 1; + $subject = implode( $replacements[ $key ], $parts ); + } + } else { + // Call mb_str_replace for each subject in array, recursively + foreach ( $subject as $key => $value ) { + $subject[ $key ] = self::mb_str_replace( $search, $replace, $value, $count ); + } + } + + return $subject; + } + + + /** + * Wrapper for regex/non regex search & replace + * + * @param string $search + * @param string $replace + * @param string $string + * @param int $count + * + * @return string + */ + public function str_replace( $search, $replace, $string, &$count = 0 ) { + if ( $this->get( 'regex' ) ) { + return preg_replace( $search, $replace, $string, -1, $count ); + } elseif( function_exists( 'mb_split' ) ) { + return self::mb_str_replace( $search, $replace, $string, $count ); + } else { + return str_replace( $search, $replace, $string, $count ); + } + } + + /** + * Convert a string containing unicode into HTML entities for front end display + * + * @param string $string + * + * @return string + */ + public function charset_decode_utf_8( $string ) { + /* Only do the slow convert if there are 8-bit characters */ + /* avoid using 0xA0 (\240) in ereg ranges. RH73 does not like that */ + if ( ! preg_match( "/[\200-\237]/", $string ) and ! preg_match( "/[\241-\377]/", $string ) ) + return $string; + + // decode three byte unicode characters + $string = preg_replace( "/([\340-\357])([\200-\277])([\200-\277])/e", + "'&#'.((ord('\\1')-224)*4096 + (ord('\\2')-128)*64 + (ord('\\3')-128)).';'", + $string ); + + // decode two byte unicode characters + $string = preg_replace( "/([\300-\337])([\200-\277])/e", + "'&#'.((ord('\\1')-192)*64+(ord('\\2')-128)).';'", + $string ); + + return $string; + } + +} diff --git a/srdb.cli.php b/srdb.cli.php new file mode 100755 index 0000000..fe970ff --- /dev/null +++ b/srdb.cli.php @@ -0,0 +1,238 @@ +#!/usr/bin/php -q + 'host:', + 'n:' => 'name:', + 'u:' => 'user:', + 'p:' => 'pass:', + 'c:' => 'char:', + 's:' => 'search:', + 'r:' => 'replace:', + 't:' => 'tables:', + 'i:' => 'include-cols:', + 'x:' => 'exclude-cols:', + 'g' => 'regex', + 'l:' => 'pagesize:', + 'z' => 'dry-run', + 'e:' => 'alter-engine:', + 'a:' => 'alter-collation:', + 'v:' => 'verbose:', + 'port:', + 'help' +); + +$required = array( + 'h:', + 'n:', + 'u:', + 'p:' +); + +function strip_colons( $string ) { + return str_replace( ':', '', $string ); +} + +// store arg values +$arg_count = $_SERVER[ 'argc' ]; +$args_array = $_SERVER[ 'argv' ]; + +$short_opts = array_keys( $opts ); +$short_opts_normal = array_map( 'strip_colons', $short_opts ); + +$long_opts = array_values( $opts ); +$long_opts_normal = array_map( 'strip_colons', $long_opts ); + +// store array of options and values +$options = getopt( implode( '', $short_opts ), $long_opts ); + +if ( isset( $options[ 'help' ] ) ) { + echo " +##################################################################### + +interconnect/it Safe Search & Replace tool + +##################################################################### + +This script allows you to search and replace strings in your database +safely without breaking serialised PHP. + +Please report any bugs or fork and contribute to this script via +Github: https://github.com/interconnectit/search-replace-db + +Argument values are strings unless otherwise specified. + +ARGS + -h, --host + Required. The hostname of the database server. + -n, --name + Required. Database name. + -u, --user + Required. Database user. + -p, --pass + Required. Database user's password. + --port + Optional. Port on database server to connect to. + The default is 3306. (MySQL default port). + -s, --search + String to search for or `preg_replace()` style + regular expression. + -r, --replace + None empty string to replace search with or + `preg_replace()` style replacement. + -t, --tables + If set only runs the script on the specified table, comma + separate for multiple values. + -i, --include-cols + If set only runs the script on the specified columns, comma + separate for multiple values. + -x, --exclude-cols + If set excludes the specified columns, comma separate for + multiple values. + -g, --regex [no value] + Treats value for -s or --search as a regular expression and + -r or --replace as a regular expression replacement. + -l, --pagesize + How rows to fetch at a time from a table. + -z, --dry-run [no value] + Prevents any updates happening so you can preview the number + of changes to be made + -e, --alter-engine + Changes the database table to the specified database engine + eg. InnoDB or MyISAM. If specified search/replace arguments + are ignored. They will not be run simultaneously. + -a, --alter-collation + Changes the database table to the specified collation + eg. utf8_unicode_ci. If specified search/replace arguments + are ignored. They will not be run simultaneously. + -v, --verbose [true|false] + Defaults to true, can be set to false to run script silently. + --help + Displays this help message ;) +"; + exit; +} + +// missing field flag, show all missing instead of 1 at a time +$missing_arg = false; + +// check required args are passed +foreach( $required as $key ) { + $short_opt = strip_colons( $key ); + $long_opt = strip_colons( $opts[ $key ] ); + if ( ! isset( $options[ $short_opt ] ) && ! isset( $options[ $long_opt ] ) ) { + fwrite( STDERR, "Error: Missing argument, -{$short_opt} or --{$long_opt} is required.\n" ); + $missing_arg = true; + } +} + +// bail if requirements not met +if ( $missing_arg ) { + fwrite( STDERR, "Please enter the missing arguments.\n" ); + exit( 1 ); +} + +// new args array +$args = array( + 'verbose' => true, + 'dry_run' => false +); + +// create $args array +foreach( $options as $key => $value ) { + + // transpose keys + if ( ( $is_short = array_search( $key, $short_opts_normal ) ) !== false ) + $key = $long_opts_normal[ $is_short ]; + + // boolean options as is, eg. a no value arg should be set true + if ( in_array( $key, $long_opts ) ) + $value = true; + + switch ( $key ) { + // boolean options. + case 'verbose': + $value = (boolean)filter_var( $value, FILTER_VALIDATE_BOOLEAN ); + break; + } + + // change to underscores + $key = str_replace( '-', '_', $key ); + + $args[ $key ] = $value; +} + +// modify the log output +class icit_srdb_cli extends icit_srdb { + + public function log( $type = '' ) { + + $args = array_slice( func_get_args(), 1 ); + + $output = ""; + + switch( $type ) { + case 'error': + list( $error_type, $error ) = $args; + $output .= "$error_type: $error"; + break; + case 'search_replace_table_start': + list( $table, $search, $replace ) = $args; + $output .= "{$table}: replacing {$search} with {$replace}"; + break; + case 'search_replace_table_end': + list( $table, $report ) = $args; + $time = number_format( $report[ 'end' ] - $report[ 'start' ], 8 ); + $output .= "{$table}: {$report['rows']} rows, {$report['change']} changes found, {$report['updates']} updates made in {$time} seconds"; + break; + case 'search_replace_end': + list( $search, $replace, $report ) = $args; + $time = number_format( $report[ 'end' ] - $report[ 'start' ], 8 ); + $dry_run_string = $this->dry_run ? "would have been" : "were"; + $output .= " +Replacing {$search} with {$replace} on {$report['tables']} tables with {$report['rows']} rows +{$report['change']} changes {$dry_run_string} made +{$report['updates']} updates were actually made +It took {$time} seconds"; + break; + case 'update_engine': + list( $table, $report, $engine ) = $args; + $output .= $table . ( $report[ 'converted' ][ $table ] ? ' has been' : 'has not been' ) . ' converted to ' . $engine; + break; + case 'update_collation': + list( $table, $report, $collation ) = $args; + $output .= $table . ( $report[ 'converted' ][ $table ] ? ' has been' : 'has not been' ) . ' converted to ' . $collation; + break; + } + + if ( $this->verbose ) + echo $output . "\n"; + + } + +} + +$report = new icit_srdb_cli( $args ); + +// Only print a separating newline if verbose mode is on to separate verbose output from result +if ($args[ 'verbose' ]) { + echo "\n"; +} + +if ( $report && ( ( isset( $args[ 'dry_run' ] ) && $args[ 'dry_run' ] ) || empty( $report->errors[ 'results' ] ) ) ) { + echo "And we're done!\n"; +} else { + echo "Check the output for errors. You may need to ensure verbose output is on by using -v or --verbose.\n"; +}