├── configs ├── config.inc.php ├── default.conf ├── wp-config.php ├── db-config.php ├── php.ini ├── default.vcl └── db.php ├── README.md └── stackFile.yml /configs/config.inc.php: -------------------------------------------------------------------------------- 1 | queries. It is not 7 | * a constant because you might want to use it momentarily. 8 | * Default: false 9 | */ 10 | $wpdb->save_queries = false; 11 | 12 | /** 13 | * persistent (bool) 14 | * This determines whether to use mysql_connect or mysql_pconnect. The effects 15 | * of this setting may vary and should be carefully tested. 16 | * Default: false 17 | */ 18 | $wpdb->persistent = false; 19 | 20 | /** 21 | * max_connections (int) 22 | * This is the number of mysql connections to keep open. Increase if you expect 23 | * to reuse a lot of connections to different servers. This is ignored if you 24 | * enable persistent connections. 25 | * Default: 10 26 | */ 27 | $wpdb->max_connections = 10; 28 | 29 | /** 30 | * check_tcp_responsiveness 31 | * Enables checking TCP responsiveness by fsockopen prior to mysql_connect or 32 | * mysql_pconnect. This was added because PHP's mysql functions do not provide 33 | * a variable timeout setting. Disabling it may improve average performance by 34 | * a very tiny margin but lose protection against connections failing slowly. 35 | * Default: true 36 | */ 37 | $wpdb->check_tcp_responsiveness = true; 38 | 39 | /** Configuration Functions **/ 40 | 41 | /** 42 | * This is the most basic way to add a server to HyperDB using only the 43 | * required parameters: host, user, password, name. 44 | * This adds the DB defined in wp-config.php as a read/write server for 45 | * the 'global' dataset. (Every table is in 'global' by default.) 46 | */ 47 | $wpdb->add_database(array( 48 | 'host' => DB_HOST, // If port is other than 3306, use host:port. 49 | 'user' => DB_USER, 50 | 'password' => DB_PASSWORD, 51 | 'name' => DB_NAME, 52 | )); 53 | 54 | /** 55 | * This adds the same server again, only this time it is configured as a slave. 56 | * The last three parameters are set to the defaults but are shown for clarity. 57 | */ 58 | $wpdb->add_database(array( 59 | 'host' => DB_SLAVE, // If port is other than 3306, use host:port. 60 | 'user' => DB_USER, 61 | 'password' => DB_PASSWORD, 62 | 'name' => DB_NAME, 63 | 'write' => 0, 64 | 'read' => 1, 65 | 'dataset' => 'global', 66 | 'timeout' => 0.2, 67 | )); 68 | -------------------------------------------------------------------------------- /stackFile.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | services: 3 | - name: db 4 | type: mariadb 5 | version: 10.4 6 | configuration: MasterSlave 7 | database: wordpress 8 | username: wordpress 9 | # Ask password on create 10 | userPassword: null 11 | password: null 12 | ports: 13 | - 3306 14 | remoteAccess: false 15 | comments: 16 | username: Database will be created with the same name 17 | deployTo: 18 | - main-one 19 | - main-two 20 | 21 | - name: cache 22 | type: redis 23 | version: 5.0 24 | remoteAccess: false 25 | ports: 26 | - 6379 27 | deployTo: 28 | - edge 29 | 30 | - name: blog 31 | type: docker 32 | image: d2cio/wordpress 33 | version: latest 34 | volumes: 35 | - directory: $MAIN_PATH/wp-content/uploads 36 | sync: true 37 | - directory: $MAIN_PATH/wp-content/themes 38 | sync: true 39 | - directory: $MAIN_PATH/wp-content/plugins 40 | sync: true 41 | ports: 42 | - 80 43 | remoteAccess: false 44 | configFiles: 45 | - dest: $MAIN_PATH/wp-config.php 46 | src: ./configs/wp-config.php 47 | - dest: $MAIN_PATH/db-config.php 48 | src: ./configs/db-config.php 49 | - dest: $MAIN_PATH/wp-content/db.php 50 | src: ./configs/db.php 51 | - dest: /usr/local/etc/php/php.ini 52 | src: ./configs/php.ini 53 | - dest: /etc/nginx/conf.d/default.conf 54 | src: ./configs/default.conf 55 | localDeps: | 56 | cmd extract 57 | initialCommands: | 58 | cmd wp core install --url={{=service('varnish').getNginxDomain()}} --title='"$WP_TITLE"' --admin_user=$WP_ADMIN_USERNAME --admin_password=$WP_ADMIN_PASSWORD --admin_email=$WP_ADMIN_EMAIL --path=/var/www/{{=service('blog').get('name')}} 59 | cmd wp plugin install varnish-http-purge --activate --path=/var/www/{{=service('blog').get('name')}} 60 | cmd wp plugin install redis-cache --activate --path=/var/www/{{=service('blog').get('name')}} 61 | cmd wp redis enable --path=/var/www/{{=service('blog').get('name')}} 62 | env: 63 | SERVICE_NAME: "{{=service('blog').get('name')}}" 64 | WP_TITLE: null 65 | WP_ADMIN_USERNAME: null 66 | WP_ADMIN_PASSWORD: null 67 | WP_ADMIN_EMAIL: null 68 | comments: 69 | env: 70 | WP_TITLE: The title of your Wordpress website 71 | WP_ADMIN_USERNAME: Username of administrator account 72 | WP_ADMIN_PASSWORD: Password of administrator account 73 | WP_ADMIN_EMAIL: Email of administrator account 74 | volumesUID: 33 75 | deployTo: 76 | - main-one 77 | - main-two 78 | 79 | - name: cache 80 | type: redis 81 | version: 5.0 82 | remoteAccess: false 83 | ports: 84 | - 6379 85 | deployTo: 86 | - edge 87 | 88 | - name: balancer 89 | type: haproxy 90 | version: latest 91 | ports: 92 | - 80 93 | - 443 94 | remoteAccess: false 95 | serviceFiles: 96 | - name: blog 97 | sticky: true 98 | deployTo: 99 | - edge 100 | 101 | - name: varnish 102 | type: docker 103 | image: d2cio/varnish 104 | version: 6.0 105 | ports: 106 | - 80 107 | remoteAccess: false 108 | configFiles: 109 | - dest: /etc/varnish/default.vcl 110 | src: ./configs/default.vcl 111 | deployTo: 112 | - edge 113 | 114 | - name: pma 115 | type: docker 116 | image: phpmyadmin/phpmyadmin 117 | version: latest 118 | ports: 119 | - 80 120 | remoteAccess: false 121 | env: 122 | PMA_HOST: "{{=service('db').getMasterAlias()}}" 123 | PMA_PORT: "{{=service('db').getMainPort()}}" 124 | deployTo: 125 | - edge 126 | 127 | - name: web 128 | type: nginx 129 | version: 1.17 130 | ports: 131 | - 80 132 | - 443 133 | remoteAccess: true 134 | serviceFiles: 135 | - name: varnish 136 | - name: pma 137 | deployTo: 138 | - edge 139 | 140 | hosts: 141 | - name: main-one 142 | requirements: 143 | cores: 1 144 | memory: 2 145 | 146 | - name: main-two 147 | requirements: 148 | cores: 1 149 | memory: 2 150 | 151 | - name: edge 152 | requirements: 153 | cores: 1 154 | memory: 1 155 | -------------------------------------------------------------------------------- /configs/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | 3 | ; Enable the PHP scripting language engine under Apache. 4 | ; http://php.net/engine 5 | engine = On 6 | 7 | ; Output buffering 8 | ; http://php.net/output-buffering 9 | output_buffering = 4096 10 | 11 | ; Transparent output compression using the zlib library 12 | ; http://php.net/zlib.output-compression 13 | zlib.output_compression = Off 14 | 15 | ; http://php.net/zlib.output-compression-level 16 | ;zlib.output_compression_level = -1 17 | 18 | ; You cannot specify additional output handlers if zlib.output_compression 19 | ; is activated here. This setting does the same as output_handler but in 20 | ; a different order. 21 | ; http://php.net/zlib.output-handler 22 | ;zlib.output_handler = 23 | 24 | opcache.memory_consumption=128 25 | opcache.interned_strings_buffer=8 26 | opcache.max_accelerated_files=4000 27 | opcache.revalidate_freq=60 28 | opcache.fast_shutdown=1 29 | opcache.enable_cli=1 30 | 31 | ; http://php.net/implicit-flush 32 | implicit_flush = Off 33 | 34 | unserialize_callback_func = 35 | serialize_precision = -1 36 | 37 | ; This directive allows you to disable certain functions for security reasons. 38 | ; It receives a comma-delimited list of function names. 39 | ; http://php.net/disable-functions 40 | disable_functions = 41 | 42 | ; This directive allows you to disable certain classes for security reasons. 43 | ; It receives a comma-delimited list of class names. 44 | ; http://php.net/disable-classes 45 | disable_classes = 46 | 47 | ; Decides whether PHP may expose the fact that it is installed on the server 48 | ; http://php.net/expose-php 49 | expose_php = On 50 | 51 | ; Maximum execution time of each script, in seconds 52 | ; http://php.net/max-execution-time 53 | max_execution_time = 30 54 | 55 | ; Maximum amount of time each script may spend parsing request data. 56 | ; http://php.net/max-input-time 57 | max_input_time = 60 58 | 59 | ; Maximum input variable nesting level 60 | ; http://php.net/max-input-nesting-level 61 | ;max_input_nesting_level = 64 62 | 63 | ; How many GET/POST/COOKIE input variables may be accepted 64 | ; max_input_vars = 1000 65 | 66 | ; Maximum amount of memory a script may consume (128MB) 67 | ; http://php.net/memory-limit 68 | memory_limit = 256M 69 | 70 | ; Error reporting 71 | ; http://php.net/error-reporting 72 | error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT 73 | 74 | ; Display errors 75 | ; http://php.net/display-errors 76 | display_errors = Off 77 | 78 | ; http://php.net/display-startup-errors 79 | display_startup_errors = Off 80 | 81 | ; http://php.net/log-errors 82 | log_errors = On 83 | 84 | error_log = /proc/self/fd/2 85 | 86 | ; http://php.net/log-errors-max-len 87 | log_errors_max_len = 0 88 | 89 | upload_tmp_dir = /tmp 90 | 91 | ; Do not log repeated messages. Repeated errors must occur in same file on same 92 | ; line unless ignore_repeated_source is set true. 93 | ; http://php.net/ignore-repeated-errors 94 | ignore_repeated_errors = Off 95 | 96 | ; Ignore source of message when ignoring repeated messages. When this setting 97 | ; is On you will not log errors with repeated messages from different files or 98 | ; source lines. 99 | ; http://php.net/ignore-repeated-source 100 | ignore_repeated_source = Off 101 | 102 | ; If this parameter is set to Off, then memory leaks will not be shown (on 103 | ; stdout or in the log). This has only effect in a debug compile, and if 104 | ; error reporting includes E_WARNING in the allowed list 105 | ; http://php.net/report-memleaks 106 | report_memleaks = On 107 | 108 | ; http://php.net/track-errors 109 | track_errors = Off 110 | 111 | ; http://php.net/html-errors 112 | html_errors = On 113 | 114 | ; http://php.net/variables-order 115 | variables_order = "EGPCS" 116 | 117 | ; http://php.net/request-order 118 | request_order = "GP" 119 | 120 | ; http://php.net/register-argc-argv 121 | register_argc_argv = Off 122 | 123 | ; http://php.net/auto-globals-jit 124 | auto_globals_jit = On 125 | 126 | ; Maximum size of POST data that PHP will accept. 127 | ; http://php.net/post-max-size 128 | post_max_size = 16M 129 | 130 | ; Automatically add files before PHP document. 131 | ; http://php.net/auto-prepend-file 132 | auto_prepend_file = 133 | 134 | ; Automatically add files after PHP document. 135 | ; http://php.net/auto-append-file 136 | auto_append_file = 137 | 138 | ; PHP's built-in default media type is set to text/html. 139 | ; http://php.net/default-mimetype 140 | default_mimetype = "text/html" 141 | 142 | ; PHP's default character set is set to UTF-8. 143 | ; http://php.net/default-charset 144 | default_charset = "UTF-8" 145 | 146 | ; http://php.net/enable-dl 147 | enable_dl = Off 148 | 149 | ; http://php.net/file-uploads 150 | file_uploads = On 151 | 152 | ; Maximum allowed size for uploaded files. 153 | ; http://php.net/upload-max-filesize 154 | upload_max_filesize = 16M 155 | 156 | ; Maximum number of files that can be uploaded via a single request 157 | max_file_uploads = 20 158 | 159 | ; Whether to allow the treatment of URLs (like http:// or ftp://) as files. 160 | ; http://php.net/allow-url-fopen 161 | allow_url_fopen = On 162 | 163 | ; Whether to allow include/require to open URLs (like http:// or ftp://) as files. 164 | ; http://php.net/allow-url-include 165 | allow_url_include = Off 166 | 167 | ; Default timeout for socket based streams (seconds) 168 | ; http://php.net/default-socket-timeout 169 | default_socket_timeout = 60 170 | 171 | [Date] 172 | ; Defines the default timezone used by the date functions 173 | ; http://php.net/date.timezone 174 | date.timezone = UTC 175 | -------------------------------------------------------------------------------- /configs/default.vcl: -------------------------------------------------------------------------------- 1 | vcl 4.0; 2 | 3 | import directors; 4 | import std; 5 | 6 | backend default { 7 | .host = "{{=service('balancer').getAppAlias()}}"; 8 | .port = "{{=service('balancer').getMainPort()}}"; 9 | .max_connections = 800; # That's it 10 | .first_byte_timeout = 300s; # How long to wait before we receive a first byte from our backend? 11 | .connect_timeout = 300s; # How long to wait for a backend connection? 12 | .between_bytes_timeout = 300s; # How long to wait between bytes received from our backend? 13 | } 14 | 15 | sub vcl_init { 16 | new backends = directors.round_robin(); 17 | backends.add_backend(default); 18 | } 19 | 20 | acl purge { 21 | "172.16.0.0/16"; 22 | "127.0.0.1"; 23 | "localhost"; 24 | } 25 | 26 | # Regex purging 27 | # Treat the request URL as a regular expression. 28 | sub purge_regex { 29 | ban("obj.http.X-VC-Req-URL ~ " + req.url + " && obj.http.X-VC-Req-Host == " + req.http.host); 30 | } 31 | 32 | # Exact purging 33 | # Use the exact request URL (including any query params) 34 | sub purge_exact { 35 | ban("obj.http.X-VC-Req-URL == " + req.url + " && obj.http.X-VC-Req-Host == " + req.http.host); 36 | } 37 | 38 | # Page purging (default) 39 | # Use the exact request URL, but ignore any query params 40 | sub purge_page { 41 | set req.url = regsub(req.url, "\?.*$", ""); 42 | ban("obj.http.X-VC-Req-URL-Base == " + req.url + " && obj.http.X-VC-Req-Host == " + req.http.host); 43 | } 44 | 45 | # The purge behavior can be controlled with the X-VC-Purge-Method header. 46 | # 47 | # Setting the X-VC-Purge-Method header to contain "regex" or "exact" will use 48 | # those respective behaviors. Any other value for the X-Purge header will 49 | # use the default ("page") behavior. 50 | # 51 | # The X-VC-Purge-Method header is not case-sensitive. 52 | # 53 | # If no X-VC-Purge-Method header is set, the request url is inspected to attempt 54 | # a best guess as to what purge behavior is expected. This should work for 55 | # most cases, although if you want to guarantee some behavior you should 56 | # always set the X-VC-Purge-Method header. 57 | 58 | sub vcl_recv { 59 | set req.backend_hint = backends.backend(); 60 | set req.http.X-VC-My-Purge-Key = ""; 61 | if (req.method == "PURGE") { 62 | if (req.http.X-VC-Purge-Key == req.http.X-VC-My-Purge-Key) { 63 | set req.http.X-VC-Purge-Key-Auth = "true"; 64 | } else { 65 | set req.http.X-VC-Purge-Key-Auth = "false"; 66 | } 67 | if (client.ip !~ purge && req.http.X-VC-Purge-Key-Auth != "true") { 68 | return (synth(405, "Not allowed from " + client.ip)); 69 | } 70 | 71 | if (req.http.X-VC-Purge-Method) { 72 | if (req.http.X-VC-Purge-Method ~ "(?i)regex") { 73 | call purge_regex; 74 | } elsif (req.http.X-VC-Purge-Method ~ "(?i)exact") { 75 | call purge_exact; 76 | } else { 77 | call purge_page; 78 | } 79 | } else { 80 | # No X-VC-Purge-Method header was specified. 81 | # Do our best to figure out which one they want. 82 | if (req.url ~ "\.\*" || req.url ~ "^\^" || req.url ~ "\$$" || req.url ~ "\\[.?*+^$|()]") { 83 | call purge_regex; 84 | } elsif (req.url ~ "\?") { 85 | call purge_exact; 86 | } else { 87 | call purge_page; 88 | } 89 | } 90 | return (synth(200,"Purged " + req.url + " " + req.http.host)); 91 | } 92 | unset req.http.X-VC-My-Purge-Key; 93 | # unset Varnish Caching custom headers from client 94 | unset req.http.X-VC-Cacheable; 95 | unset req.http.X-VC-Debug; 96 | } 97 | 98 | sub vcl_backend_response { 99 | set beresp.http.X-VC-Req-Host = bereq.http.host; 100 | set beresp.http.X-VC-Req-URL = bereq.url; 101 | set beresp.http.X-VC-Req-URL-Base = regsub(bereq.url, "\?.*$", ""); 102 | } 103 | 104 | sub vcl_deliver { 105 | unset resp.http.X-VC-Req-Host; 106 | unset resp.http.X-VC-Req-URL; 107 | unset resp.http.X-VC-Req-URL-Base; 108 | if (obj.hits > 0) { 109 | set resp.http.X-VC-Cache = "HIT"; 110 | } else { 111 | set resp.http.X-VC-Cache = "MISS"; 112 | } 113 | 114 | if (req.http.X-VC-Debug ~ "true" || resp.http.X-VC-Debug ~ "true") { 115 | set resp.http.X-VC-Hash = req.http.hash; 116 | if (req.http.X-VC-DebugMessage) { 117 | set resp.http.X-VC-DebugMessage = req.http.X-VC-DebugMessage; 118 | } 119 | } else { 120 | unset resp.http.X-VC-Enabled; 121 | unset resp.http.X-VC-Cache; 122 | unset resp.http.X-VC-Debug; 123 | unset resp.http.X-VC-DebugMessage; 124 | unset resp.http.X-VC-Cacheable; 125 | unset resp.http.X-VC-Purge-Key-Auth; 126 | unset resp.http.X-VC-TTL; 127 | } 128 | } 129 | 130 | ### WordPress-specific config ### 131 | sub vcl_recv { 132 | if (req.method !~ "^GET|HEAD|PUT|POST|TRACE|OPTIONS|DELETE$") { 133 | return(pipe); 134 | } 135 | # TURN OFF CACHE when needed (just uncomment this only when needed) 136 | # return(pass); 137 | 138 | ### Do not Cache: special cases 139 | # don't cache ajax requests 140 | if (req.http.X-Requested-With == "XMLHttpRequest") { 141 | set req.http.X-VC-Cacheable = "NO:Requested with: XMLHttpRequest"; 142 | return(pass); 143 | } 144 | 145 | # don't cache logged-in users. you can set users `logged in cookie` name in settings 146 | if (req.http.Cookie ~ "wordpress_logged_in_") { 147 | set req.http.X-VC-Cacheable = "NO:Found logged in cookie"; 148 | return(pass); 149 | } 150 | 151 | ### looks like we might actually cache it! 152 | # fix up the request 153 | set req.url = regsub(req.url, "\?replytocom=.*$", ""); 154 | 155 | # never cache anything except GET/HEAD 156 | if (req.method != "GET" && req.method != "HEAD") { 157 | set req.http.X-VC-Cacheable = "NO:Request method:" + req.method; 158 | return(pass); 159 | } 160 | 161 | # Don't cache feed 162 | if (req.url ~ "/feed") { 163 | return (pass); 164 | } 165 | 166 | if (req.url ~ "/wp-cron.php" || req.url ~ "preview=true") { 167 | return (pass); 168 | } 169 | 170 | # Woocommerce 171 | if (req.url ~ "(cart|my-account|checkout|addons)") { 172 | return (pass); 173 | } 174 | if ( req.url ~ "\?add-to-cart=" ) { 175 | return (pass); 176 | } 177 | 178 | # Remove any Google Analytics based cookies 179 | set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", ""); 180 | set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", ""); 181 | set req.http.Cookie = regsuball(req.http.Cookie, "_gat=[^;]+(; )?", ""); 182 | set req.http.Cookie = regsuball(req.http.Cookie, "_gid=[^;]+(; )?", ""); 183 | set req.http.Cookie = regsuball(req.http.Cookie, "utmctr=[^;]+(; )?", ""); 184 | set req.http.Cookie = regsuball(req.http.Cookie, "utmcmd.=[^;]+(; )?", ""); 185 | set req.http.Cookie = regsuball(req.http.Cookie, "utmccn.=[^;]+(; )?", ""); 186 | 187 | # Remove the Quant Capital cookies (added by some plugin, all __qca) 188 | set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", ""); 189 | 190 | # Remove the wp-settings cookie 191 | set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-\d+=[^;]+(; )?", ""); 192 | 193 | # Remove the wp-settings-time cookie 194 | set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-\d+=[^;]+(; )?", ""); 195 | 196 | # Remove the wp test cookie 197 | set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", ""); 198 | 199 | # Remove has_js and Cloudflare/Google Analytics __* cookies. 200 | set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", ""); 201 | 202 | # Yandex metrica cookies 203 | set req.http.Cookie = regsuball(req.http.Cookie, "_ym[_a-z0-9]+=[^;]+(; )?", ""); 204 | set req.http.Cookie = regsuball(req.http.Cookie, "yabs-sid=[^;]+(; )?", ""); 205 | set req.http.Cookie = regsuball(req.http.Cookie, "yp=[^;]+(; )?", ""); 206 | set req.http.Cookie = regsuball(req.http.Cookie, "metrics_token=[^;]+(; )?", ""); 207 | set req.http.Cookie = regsuball(req.http.Cookie, "yandexuid=[^;]+(; )?", ""); 208 | set req.http.Cookie = regsuball(req.http.Cookie, "sync_cookie_csrf=[^;]+(; )?", ""); 209 | 210 | # Carrotquest cookies 211 | set req.http.Cookie = regsuball(req.http.Cookie, "carrotquest[_a-z]+=[^;]+(; )?", ""); 212 | 213 | # Remove a ";" prefix, if present. 214 | set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", ""); 215 | 216 | # Remove the cloudflare cookie 217 | set req.http.Cookie = regsuball(req.http.Cookie, "__cfduid=[^;]+(; )?", ""); 218 | 219 | # Remove the PHPSESSID in members area cookie 220 | set req.http.Cookie = regsuball(req.http.Cookie, "PHPSESSID=[^;]+(; )?", ""); 221 | 222 | # Remove DoubleClick offensive cookies 223 | set req.http.Cookie = regsuball(req.http.Cookie, "__gads=[^;]+(; )?", ""); 224 | 225 | # Remove the AddThis cookies 226 | set req.http.Cookie = regsuball(req.http.Cookie, "__atuv.=[^;]+(; )?", ""); 227 | 228 | # Are there cookies left with only spaces or that are empty? 229 | if (req.http.Cookie ~ "^\s*$") { 230 | unset req.http.Cookie; 231 | } 232 | 233 | # Normalize the query arguments. 234 | # Note: Placing this above the "do not cache" section breaks some WP theme elements and admin functionality. 235 | set req.url = std.querysort(req.url); 236 | 237 | # Cache all others requests. 238 | return (hash); 239 | } 240 | 241 | sub vcl_hash { 242 | set req.http.hash = req.url; 243 | if (req.http.host) { 244 | set req.http.hash = req.http.hash + "#" + req.http.host; 245 | } else { 246 | set req.http.hash = req.http.hash + "#" + server.ip; 247 | } 248 | } 249 | 250 | sub vcl_backend_response { 251 | # make sure grace is at least N minutes 252 | if (beresp.grace < 2m) { 253 | set beresp.grace = 2m; 254 | } 255 | 256 | # overwrite ttl with X-VC-TTL 257 | if (beresp.http.X-VC-TTL) { 258 | set beresp.ttl = std.duration(beresp.http.X-VC-TTL + "s", 0s); 259 | } 260 | 261 | # catch obvious reasons we can't cache 262 | if (beresp.http.Set-Cookie ~ "wordpress_logged_in_") { 263 | set beresp.ttl = 0s; 264 | } 265 | 266 | # Don't cache object as instructed by header bereq.X-VC-Cacheable 267 | if (bereq.http.X-VC-Cacheable ~ "^NO") { 268 | set beresp.http.X-VC-Cacheable = bereq.http.X-VC-Cacheable; 269 | set beresp.uncacheable = true; 270 | set beresp.ttl = 120s; 271 | 272 | # Varnish determined the object is not cacheable 273 | } else if (beresp.ttl <= 0s) { 274 | if (!beresp.http.X-VC-Cacheable) { 275 | set beresp.http.X-VC-Cacheable = "NO:Not cacheable, ttl: "+ beresp.ttl; 276 | } 277 | set beresp.uncacheable = true; 278 | set beresp.ttl = 120s; 279 | # Cache object 280 | } else if (beresp.http.X-VC-Enabled ~ "true") { 281 | if (!beresp.http.X-VC-Cacheable) { 282 | set beresp.http.X-VC-Cacheable = "YES:Is cacheable, ttl: " + beresp.ttl; 283 | } 284 | 285 | # Do not cache object 286 | } else if (beresp.http.X-VC-Enabled ~ "false") { 287 | if (!beresp.http.X-VC-Cacheable) { 288 | set beresp.http.X-VC-Cacheable = "NO:Disabled"; 289 | } 290 | set beresp.ttl = 0s; 291 | } 292 | 293 | # Avoid caching error responses 294 | if (beresp.status == 404 || beresp.status >= 500) { 295 | set beresp.ttl = 0s; 296 | set beresp.grace = 15s; 297 | } 298 | 299 | # Deliver the content 300 | return(deliver); 301 | } 302 | 303 | sub vcl_synth { 304 | if (resp.status == 750) { 305 | set resp.http.Location = req.http.X-VC-Redirect; 306 | set resp.status = 302; 307 | return(deliver); 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /configs/db.php: -------------------------------------------------------------------------------- 1 | dbh) for established mysql connections 76 | * @var array 77 | */ 78 | var $dbhs; 79 | 80 | /** 81 | * The multi-dimensional array of datasets and servers 82 | * @var array 83 | */ 84 | var $hyper_servers = array(); 85 | 86 | /** 87 | * Optional directory of tables and their datasets 88 | * @var array 89 | */ 90 | var $hyper_tables = array(); 91 | 92 | /** 93 | * Optional directory of callbacks to determine datasets from queries 94 | * @var array 95 | */ 96 | var $hyper_callbacks = array(); 97 | 98 | /** 99 | * Custom callback to save debug info in $this->queries 100 | * @var callable 101 | */ 102 | var $save_query_callback = null; 103 | 104 | /** 105 | * Whether to use persistent connections 106 | * @var bool 107 | */ 108 | var $persistent = false; 109 | 110 | /** 111 | * The maximum number of db links to keep open. The least-recently used 112 | * link will be closed when the number of links exceeds this. 113 | * @var int 114 | */ 115 | var $max_connections = 10; 116 | 117 | /** 118 | * Whether to check with fsockopen prior to connecting to mysql. 119 | * @var bool 120 | */ 121 | var $check_tcp_responsiveness = true; 122 | 123 | /** 124 | * Minimum number of connections to try before bailing 125 | * @var int 126 | */ 127 | var $min_tries = 3; 128 | 129 | /** 130 | * Send Reads To Masters. This disables slave connections while true. 131 | * Otherwise it is an array of written tables. 132 | * @var array 133 | */ 134 | var $srtm = array(); 135 | 136 | /** 137 | * The log of db connections made and the time each one took 138 | * @var array 139 | */ 140 | var $db_connections; 141 | 142 | /** 143 | * The list of unclosed connections sorted by LRU 144 | */ 145 | var $open_connections = array(); 146 | 147 | /** 148 | * The last server used and the database name selected 149 | * @var array 150 | */ 151 | var $last_used_server; 152 | 153 | /** 154 | * Lookup array (dbhname => (server, db name) ) for re-selecting the db 155 | * when a link is re-used. 156 | * @var array 157 | */ 158 | var $used_servers = array(); 159 | 160 | /** 161 | * Whether to save debug_backtrace in save_query_callback. You may wish 162 | * to disable this, e.g. when tracing out-of-memory problems. 163 | */ 164 | var $save_backtrace = true; 165 | 166 | /** 167 | * Maximum lag in seconds. Set null to disable. Requires callbacks. 168 | * @var integer 169 | */ 170 | var $default_lag_threshold = null; 171 | 172 | /** 173 | * Lookup array (dbhname => host:port) 174 | * @var array 175 | */ 176 | var $dbh2host = array(); 177 | 178 | /** 179 | * Keeps track of the dbhname usage and errors. 180 | */ 181 | var $dbhname_heartbeats = array(); 182 | 183 | /** 184 | * Counter for how many queries have failed during the life of the $wpdb object 185 | */ 186 | var $num_failed_queries = 0; 187 | 188 | /** 189 | * Gets ready to make database connections 190 | * @param array db class vars 191 | */ 192 | function __construct( $args = null ) { 193 | if ( is_array($args) ) 194 | foreach ( get_class_vars(__CLASS__) as $var => $value ) 195 | if ( isset($args[$var]) ) 196 | $this->$var = $args[$var]; 197 | 198 | $this->use_mysqli = $this->should_use_mysqli(); 199 | 200 | $this->init_charset(); 201 | } 202 | 203 | /** 204 | * Triggers __construct() for backwards compatibility with PHP4 205 | */ 206 | function hyperdb( $args = null ) { 207 | return $this->__construct($args); 208 | } 209 | 210 | /** 211 | * Sets $this->charset and $this->collate 212 | */ 213 | function init_charset() { 214 | if ( function_exists('is_multisite') && is_multisite() ) { 215 | $this->charset = 'utf8'; 216 | if ( defined( 'DB_COLLATE' ) && DB_COLLATE ) 217 | $this->collate = DB_COLLATE; 218 | else 219 | $this->collate = 'utf8_general_ci'; 220 | } elseif ( defined( 'DB_COLLATE' ) ) { 221 | $this->collate = DB_COLLATE; 222 | } 223 | 224 | if ( defined( 'DB_CHARSET' ) ) 225 | $this->charset = DB_CHARSET; 226 | } 227 | 228 | /** 229 | * Add the connection parameters for a database 230 | */ 231 | function add_database( $db ) { 232 | extract($db, EXTR_SKIP); 233 | isset($dataset) or $dataset = 'global'; 234 | isset($read) or $read = 1; 235 | isset($write) or $write = 1; 236 | unset($db['dataset']); 237 | 238 | if ( $read ) 239 | $this->hyper_servers[ $dataset ][ 'read' ][ $read ][] = $db; 240 | if ( $write ) 241 | $this->hyper_servers[ $dataset ][ 'write' ][ $write ][] = $db; 242 | } 243 | 244 | /** 245 | * Specify the dateset where a table is found 246 | */ 247 | function add_table( $dataset, $table ) { 248 | $this->hyper_tables[ $table ] = $dataset; 249 | } 250 | 251 | /** 252 | * Add a callback to a group of callbacks. 253 | * The default group is 'dataset', used to examine 254 | * queries and determine dataset. 255 | */ 256 | function add_callback( $callback, $group = 'dataset' ) { 257 | $this->hyper_callbacks[ $group ][] = $callback; 258 | } 259 | 260 | /** 261 | * Find the first table name referenced in a query 262 | * @param string query 263 | * @return string table 264 | */ 265 | function get_table_from_query( $q ) { 266 | // Remove characters that can legally trail the table name 267 | $q = rtrim($q, ';/-#'); 268 | // allow (select...) union [...] style queries. Use the first queries table name. 269 | $q = ltrim($q, "\t ("); 270 | // Strip everything between parentheses except nested 271 | // selects and use only 1500 chars of the query 272 | $q = preg_replace( '/\((?!\s*select)[^(]*?\)/is', '()', substr( $q, 0, 1500 ) ); 273 | 274 | // Refer to the previous query 275 | // wpdb doesn't implement last_table, so we run it first. 276 | if ( preg_match('/^\s*SELECT.*?\s+FOUND_ROWS\(\)/is', $q) ) 277 | return $this->last_table; 278 | 279 | if( method_exists( get_parent_class( $this ), 'get_table_from_query' ) ) { 280 | // WPDB has added support for get_table_from_query, which should take precedence 281 | return parent::get_table_from_query( $q ); 282 | } 283 | 284 | // Quickly match most common queries 285 | if ( preg_match('/^\s*(?:' 286 | . 'SELECT.*?\s+FROM' 287 | . '|INSERT(?:\s+IGNORE)?(?:\s+INTO)?' 288 | . '|REPLACE(?:\s+INTO)?' 289 | . '|UPDATE(?:\s+IGNORE)?' 290 | . '|DELETE(?:\s+IGNORE)?(?:\s+FROM)?' 291 | . ')\s+`?([\w-]+)`?/is', $q, $maybe) ) 292 | return $maybe[1]; 293 | 294 | // SHOW TABLE STATUS and SHOW TABLES 295 | if ( preg_match('/^\s*(?:' 296 | . 'SHOW\s+TABLE\s+STATUS.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)' 297 | . '|SHOW\s+(?:FULL\s+)?TABLES.+(?:LIKE\s+|WHERE\s+Name\s*=\s*)' 298 | . ')\W([\w-]+)\W/is', $q, $maybe) ) 299 | return $maybe[1]; 300 | 301 | // Big pattern for the rest of the table-related queries in MySQL 5.0 302 | if ( preg_match('/^\s*(?:' 303 | . '(?:EXPLAIN\s+(?:EXTENDED\s+)?)?SELECT.*?\s+FROM' 304 | . '|INSERT(?:\s+LOW_PRIORITY|\s+DELAYED|\s+HIGH_PRIORITY)?(?:\s+IGNORE)?(?:\s+INTO)?' 305 | . '|REPLACE(?:\s+LOW_PRIORITY|\s+DELAYED)?(?:\s+INTO)?' 306 | . '|UPDATE(?:\s+LOW_PRIORITY)?(?:\s+IGNORE)?' 307 | . '|DELETE(?:\s+LOW_PRIORITY|\s+QUICK|\s+IGNORE)*(?:\s+FROM)?' 308 | . '|DESCRIBE|DESC|EXPLAIN|HANDLER' 309 | . '|(?:LOCK|UNLOCK)\s+TABLE(?:S)?' 310 | . '|(?:RENAME|OPTIMIZE|BACKUP|RESTORE|CHECK|CHECKSUM|ANALYZE|OPTIMIZE|REPAIR).*\s+TABLE' 311 | . '|TRUNCATE(?:\s+TABLE)?' 312 | . '|CREATE(?:\s+TEMPORARY)?\s+TABLE(?:\s+IF\s+NOT\s+EXISTS)?' 313 | . '|ALTER(?:\s+IGNORE)?\s+TABLE' 314 | . '|DROP\s+TABLE(?:\s+IF\s+EXISTS)?' 315 | . '|CREATE(?:\s+\w+)?\s+INDEX.*\s+ON' 316 | . '|DROP\s+INDEX.*\s+ON' 317 | . '|LOAD\s+DATA.*INFILE.*INTO\s+TABLE' 318 | . '|(?:GRANT|REVOKE).*ON\s+TABLE' 319 | . '|SHOW\s+(?:.*FROM|.*TABLE)' 320 | . ')\s+`?([\w-]+)`?/is', $q, $maybe) ) 321 | return $maybe[1]; 322 | } 323 | 324 | /** 325 | * Determine the likelihood that this query could alter anything 326 | * @param string query 327 | * @return bool 328 | */ 329 | function is_write_query( $q ) { 330 | // Quick and dirty: only SELECT statements are considered read-only. 331 | $q = ltrim($q, "\r\n\t ("); 332 | return !preg_match('/^(?:SELECT|SHOW|DESCRIBE|DESC|EXPLAIN)\s/i', $q); 333 | } 334 | 335 | /** 336 | * Set a flag to prevent reading from slaves which might be lagging after a write 337 | */ 338 | function send_reads_to_masters() { 339 | $this->srtm = true; 340 | } 341 | 342 | /** 343 | * Callbacks are executed in the order in which they are registered until one 344 | * of them returns something other than null. 345 | */ 346 | function run_callbacks( $group, $args = null) { 347 | if ( ! isset( $this->hyper_callbacks[ $group ] ) || ! is_array( $this->hyper_callbacks[ $group ] ) ) 348 | return null; 349 | 350 | if ( ! isset( $args ) ) { 351 | $args = array( &$this ); 352 | } elseif ( is_array( $args ) ) { 353 | $args[] = &$this; 354 | } else { 355 | $args = array( $args, &$this ); 356 | } 357 | 358 | foreach ( $this->hyper_callbacks[ $group ] as $func ) { 359 | $result = call_user_func_array($func, $args); 360 | if ( isset($result) ) 361 | return $result; 362 | } 363 | } 364 | 365 | /** 366 | * Figure out which database server should handle the query, and connect to it. 367 | * @param string query 368 | * @return resource mysql database connection 369 | */ 370 | function db_connect( $query = '' ) { 371 | if ( empty( $query ) ) 372 | return false; 373 | 374 | $this->last_table = $this->table = $this->get_table_from_query($query); 375 | 376 | if ( isset($this->hyper_tables[$this->table]) ) { 377 | $dataset = $this->hyper_tables[$this->table]; 378 | $this->callback_result = null; 379 | } elseif ( null !== $this->callback_result = $this->run_callbacks( 'dataset', $query ) ) { 380 | if ( is_array($this->callback_result) ) 381 | extract( $this->callback_result, EXTR_OVERWRITE ); 382 | else 383 | $dataset = $this->callback_result; 384 | } 385 | 386 | if ( ! isset($dataset) ) 387 | $dataset = 'global'; 388 | 389 | if ( ! $dataset ) 390 | return $this->log_and_bail("Unable to determine dataset (for table: $this->table)"); 391 | else 392 | $this->dataset = $dataset; 393 | 394 | $this->run_callbacks( 'dataset_found', $dataset ); 395 | 396 | if ( empty( $this->hyper_servers ) ) { 397 | if ( $this->is_mysql_connection( $this->dbh ) ) 398 | return $this->dbh; 399 | if ( 400 | !defined('DB_HOST') 401 | || !defined('DB_USER') 402 | || !defined('DB_PASSWORD') 403 | || !defined('DB_NAME') ) 404 | return $this->log_and_bail("We were unable to query because there was no database defined"); 405 | $this->dbh = $this->ex_mysql_connect( DB_HOST, DB_USER, DB_PASSWORD, $this->persistent ); 406 | if ( ! $this->is_mysql_connection( $this->dbh ) ) 407 | return $this->log_and_bail("We were unable to connect to the database. (DB_HOST)"); 408 | if ( ! $this->ex_mysql_select_db( DB_NAME, $this->dbh ) ) 409 | return $this->log_and_bail("We were unable to select the database"); 410 | if ( ! empty( $this->charset ) ) { 411 | $collation_query = "SET NAMES '$this->charset'"; 412 | if ( !empty( $this->collate ) ) 413 | $collation_query .= " COLLATE '$this->collate'"; 414 | $this->ex_mysql_query( $collation_query, $this->dbh ); 415 | } 416 | return $this->dbh; 417 | } 418 | 419 | // Determine whether the query must be sent to the master (a writable server) 420 | if ( !empty( $use_master ) || $this->srtm === true || isset($this->srtm[$this->table]) ) { 421 | $use_master = true; 422 | } elseif ( $is_write = $this->is_write_query($query) ) { 423 | $use_master = true; 424 | if ( is_array($this->srtm) ) 425 | $this->srtm[$this->table] = true; 426 | } elseif ( !isset($use_master) && is_array($this->srtm) && !empty($this->srtm) ) { 427 | // Detect queries that have a join in the srtm array. 428 | $use_master = false; 429 | $query_match = substr( $query, 0, 1000 ); 430 | foreach ( $this->srtm as $key => $value ) { 431 | if ( false !== stripos( $query_match, $key ) ) { 432 | $use_master = true; 433 | break; 434 | } 435 | } 436 | } else { 437 | $use_master = false; 438 | } 439 | 440 | if ( $use_master ) { 441 | $this->dbhname = $dbhname = $dataset . '__w'; 442 | $operation = 'write'; 443 | } else { 444 | $this->dbhname = $dbhname = $dataset . '__r'; 445 | $operation = 'read'; 446 | } 447 | 448 | // Try to reuse an existing connection 449 | while ( isset( $this->dbhs[$dbhname] ) && $this->is_mysql_connection( $this->dbhs[$dbhname] ) ) { 450 | // Find the connection for incrementing counters 451 | foreach ( array_keys($this->db_connections) as $i ) 452 | if ( $this->db_connections[$i]['dbhname'] == $dbhname ) 453 | $conn =& $this->db_connections[$i]; 454 | 455 | if ( isset($server['name']) ) { 456 | $name = $server['name']; 457 | // A callback has specified a database name so it's possible the existing connection selected a different one. 458 | if ( $name != $this->used_servers[$dbhname]['name'] ) { 459 | if ( ! $this->ex_mysql_select_db( $name, $this->dbhs[$dbhname] ) ) { 460 | // this can happen when the user varies and lacks permission on the $name database 461 | if ( isset( $conn['disconnect (select failed)'] ) ) 462 | ++$conn['disconnect (select failed)']; 463 | else 464 | $conn['disconnect (select failed)'] = 1; 465 | 466 | $this->disconnect($dbhname); 467 | break; 468 | } 469 | $this->used_servers[$dbhname]['name'] = $name; 470 | } 471 | } else { 472 | $name = $this->used_servers[$dbhname]['name']; 473 | } 474 | 475 | $this->current_host = $this->dbh2host[$dbhname]; 476 | 477 | // Keep this connection at the top of the stack to prevent disconnecting frequently-used connections 478 | if ( $k = array_search($dbhname, $this->open_connections) ) { 479 | unset($this->open_connections[$k]); 480 | $this->open_connections[] = $dbhname; 481 | } 482 | 483 | $this->last_used_server = $this->used_servers[$dbhname]; 484 | $this->last_connection = compact('dbhname', 'name'); 485 | 486 | if ( $this->should_mysql_ping() && ! $this->ex_mysql_ping( $this->dbhs[$dbhname] ) ) { 487 | if ( isset( $conn['disconnect (ping failed)'] ) ) 488 | ++$conn['disconnect (ping failed)']; 489 | else 490 | $conn['disconnect (ping failed)'] = 1; 491 | 492 | $this->disconnect($dbhname); 493 | break; 494 | } 495 | 496 | if ( isset( $conn['queries'] ) ) 497 | ++$conn['queries']; 498 | else 499 | $conn['queries'] = 1; 500 | 501 | return $this->dbhs[$dbhname]; 502 | } 503 | 504 | if ( $use_master && defined( "MASTER_DB_DEAD" ) ) { 505 | return $this->bail("We're updating the database, please try back in 5 minutes. If you are posting to your blog please hit the refresh button on your browser in a few minutes to post the data again. It will be posted as soon as the database is back online again."); 506 | } 507 | 508 | if ( empty($this->hyper_servers[$dataset][$operation]) ) 509 | return $this->log_and_bail("No databases available with $this->table ($dataset)"); 510 | 511 | // Put the groups in order by priority 512 | ksort($this->hyper_servers[$dataset][$operation]); 513 | 514 | // Make a list of at least $this->min_tries connections to try, repeating as necessary. 515 | $servers = array(); 516 | do { 517 | foreach ( $this->hyper_servers[$dataset][$operation] as $group => $items ) { 518 | $keys = array_keys($items); 519 | shuffle($keys); 520 | foreach ( $keys as $key ) 521 | $servers[] = compact('group', 'key'); 522 | } 523 | 524 | if ( !$tries_remaining = count( $servers ) ) 525 | return $this->log_and_bail("No database servers were found to match the query ($this->table, $dataset)"); 526 | 527 | if ( !isset( $unique_servers ) ) 528 | $unique_servers = $tries_remaining; 529 | 530 | } while ( $tries_remaining < $this->min_tries ); 531 | 532 | // Connect to a database server 533 | do { 534 | $unique_lagged_slaves = array(); 535 | $success = false; 536 | 537 | foreach ( $servers as $group_key ) { 538 | --$tries_remaining; 539 | 540 | // If all servers are lagged, we need to start ignoring the lag and retry 541 | if ( count( $unique_lagged_slaves ) == $unique_servers ) 542 | break; 543 | 544 | // $group, $key 545 | extract($group_key, EXTR_OVERWRITE); 546 | 547 | // $host, $user, $password, $name, $read, $write [, $lag_threshold, $timeout ] 548 | extract($this->hyper_servers[$dataset][$operation][$group][$key], EXTR_OVERWRITE); 549 | $port = null; 550 | 551 | // Split host:port into $host and $port 552 | if ( strpos($host, ':') ) 553 | list($host, $port) = explode(':', $host); 554 | 555 | // Overlay $server if it was extracted from a callback 556 | if ( isset($server) && is_array($server) ) 557 | extract($server, EXTR_OVERWRITE); 558 | 559 | // Split again in case $server had host:port 560 | if ( strpos($host, ':') ) 561 | list($host, $port) = explode(':', $host); 562 | 563 | // Make sure there's always a port number 564 | if ( empty($port) ) 565 | $port = 3306; 566 | 567 | // Use a default timeout of 200ms 568 | if ( !isset($timeout) ) 569 | $timeout = 0.2; 570 | 571 | // Get the minimum group here, in case $server rewrites it 572 | if ( !isset( $min_group ) || $min_group > $group ) 573 | $min_group = $group; 574 | 575 | // Can be used by the lag callbacks 576 | $this->lag_cache_key = "$host:$port"; 577 | $this->lag_threshold = isset($lag_threshold) ? $lag_threshold : $this->default_lag_threshold; 578 | 579 | // Check for a lagged slave, if applicable 580 | if ( !$use_master && !$write && !isset( $ignore_slave_lag ) 581 | && isset($this->lag_threshold) && !isset( $server['host'] ) 582 | && ( $lagged_status = $this->get_lag_cache() ) === HYPERDB_LAG_BEHIND 583 | ) { 584 | // If it is the last lagged slave and it is with the best preference we will ignore its lag 585 | if ( !isset( $unique_lagged_slaves["$host:$port"] ) 586 | && $unique_servers == count( $unique_lagged_slaves ) + 1 587 | && $group == $min_group ) 588 | { 589 | $this->lag_threshold = null; 590 | } else { 591 | $unique_lagged_slaves["$host:$port"] = $this->lag; 592 | continue; 593 | } 594 | } 595 | 596 | $this->timer_start(); 597 | 598 | // Connect if necessary or possible 599 | $server_state = null; 600 | if ( $use_master || ! $tries_remaining || 601 | 'up' == $server_state = $this->get_server_state( $host, $port, $timeout ) ) 602 | { 603 | $this->set_connect_timeout( 'pre_connect', $use_master, $tries_remaining ); 604 | $this->dbhs[$dbhname] = $this->ex_mysql_connect( "$host:$port", $user, $password, $this->persistent ); 605 | $this->set_connect_timeout( 'post_connect', $use_master, $tries_remaining ); 606 | } else { 607 | $this->dbhs[$dbhname] = false; 608 | } 609 | 610 | $elapsed = $this->timer_stop(); 611 | 612 | if ( $this->is_mysql_connection( $this->dbhs[$dbhname] ) ) { 613 | /** 614 | * If we care about lag, disconnect lagged slaves and try to find others. 615 | * We don't disconnect if it is the last lagged slave and it is with the best preference. 616 | */ 617 | if ( !$use_master && !$write && !isset( $ignore_slave_lag ) 618 | && isset($this->lag_threshold) && !isset( $server['host'] ) 619 | && $lagged_status !== HYPERDB_LAG_OK 620 | && ( $lagged_status = $this->get_lag() ) === HYPERDB_LAG_BEHIND 621 | && !( 622 | !isset( $unique_lagged_slaves["$host:$port"] ) 623 | && $unique_servers == count( $unique_lagged_slaves ) + 1 624 | && $group == $min_group 625 | ) 626 | ) { 627 | $success = false; 628 | $unique_lagged_slaves["$host:$port"] = $this->lag; 629 | $this->disconnect( $dbhname ); 630 | $this->dbhs[$dbhname] = false; 631 | $msg = "Replication lag of {$this->lag}s on $host:$port ($dbhname)"; 632 | $this->print_error( $msg ); 633 | continue; 634 | } elseif ( $this->ex_mysql_select_db( $name, $this->dbhs[$dbhname] ) ) { 635 | $success = true; 636 | $this->current_host = "$host:$port"; 637 | $this->dbh2host[$dbhname] = "$host:$port"; 638 | $queries = 1; 639 | $lag = isset( $this->lag ) ? $this->lag : 0; 640 | $this->last_connection = compact('dbhname', 'host', 'port', 'user', 'name', 'tcp', 'elapsed', 'success', 'queries', 'lag'); 641 | $this->db_connections[] = $this->last_connection; 642 | $this->open_connections[] = $dbhname; 643 | break; 644 | } 645 | } 646 | 647 | if ( 'down' == $server_state ) 648 | continue; // don't flood the logs if already down 649 | 650 | if ( HYPERDB_CONN_HOST_ERROR == $this->ex_mysql_errno() && 651 | ( 'up' == $server_state || ! $tries_remaining ) ) 652 | { 653 | $this->mark_server_as_down( $host, $port ); 654 | $server_state = 'down'; 655 | } 656 | 657 | $success = false; 658 | $this->last_connection = compact('dbhname', 'host', 'port', 'user', 'name', 'tcp', 'elapsed', 'success'); 659 | $this->db_connections[] = $this->last_connection; 660 | $msg = date( "Y-m-d H:i:s" ) . " Can't select $dbhname - \n"; 661 | $msg .= "'referrer' => '{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}',\n"; 662 | $msg .= "'server' => {$server},\n"; 663 | $msg .= "'host' => {$host},\n"; 664 | $msg .= "'error' => " . $this->ex_mysql_error() . ",\n"; 665 | $msg .= "'errno' => " . $this->ex_mysql_errno() . ",\n"; 666 | $msg .= "'server_state' => $server_state\n"; 667 | $msg .= "'lagged_status' => " . ( isset( $lagged_status ) ? $lagged_status : HYPERDB_LAG_UNKNOWN ); 668 | 669 | $this->print_error( $msg ); 670 | } 671 | 672 | if ( ! $success || ! isset( $this->dbhs[$dbhname] ) || ! $this->is_mysql_connection( $this->dbhs[$dbhname] ) ) { 673 | if ( !isset( $ignore_slave_lag ) && count( $unique_lagged_slaves ) ) { 674 | // Lagged slaves were not used. Ignore the lag for this connection attempt and retry. 675 | $ignore_slave_lag = true; 676 | $tries_remaining = count( $servers ); 677 | continue; 678 | } 679 | 680 | $error_details = array( 681 | 'host' => $host, 682 | 'port' => $port, 683 | 'operation' => $operation, 684 | 'table' => $this->table, 685 | 'dataset' => $dataset, 686 | 'dbhname' => $dbhname 687 | ); 688 | $this->run_callbacks( 'db_connection_error', $error_details ); 689 | 690 | return $this->bail( "Unable to connect to $host:$port to $operation table '$this->table' ($dataset)" ); 691 | } 692 | 693 | break; 694 | } while ( true ); 695 | 696 | if ( !isset( $charset ) ) 697 | $charset = null; 698 | 699 | if ( !isset( $collate ) ) 700 | $collate = null; 701 | 702 | $this->set_charset($this->dbhs[$dbhname], $charset, $collate); 703 | 704 | $this->dbh = $this->dbhs[$dbhname]; // needed by $wpdb->_real_escape() 705 | 706 | $this->last_used_server = compact('host', 'user', 'name', 'read', 'write'); 707 | 708 | $this->used_servers[$dbhname] = $this->last_used_server; 709 | 710 | while ( !$this->persistent && count($this->open_connections) > $this->max_connections ) { 711 | $oldest_connection = array_shift($this->open_connections); 712 | if ( $this->dbhs[$oldest_connection] != $this->dbhs[$dbhname] ) 713 | $this->disconnect($oldest_connection); 714 | } 715 | 716 | return $this->dbhs[$dbhname]; 717 | } 718 | 719 | /** 720 | * Sets the connection's character set. 721 | * @param resource $dbh The resource given by ex_mysql_connect 722 | * @param string $charset The character set (optional) 723 | * @param string $collate The collation (optional) 724 | */ 725 | function set_charset($dbh, $charset = null, $collate = null) { 726 | if ( !isset($charset) ) 727 | $charset = $this->charset; 728 | if ( !isset($collate) ) 729 | $collate = $this->collate; 730 | 731 | if ( ! $this->has_cap( 'collation', $dbh ) || empty( $charset ) ) 732 | return; 733 | 734 | if ( ! in_array( strtolower( $charset ), array( 'utf8', 'utf8mb4', 'latin1' ) ) ) 735 | wp_die( "$charset charset isn't supported in HyperDB for security reasons" ); 736 | 737 | if ( $this->is_mysql_set_charset_callable() && $this->has_cap( 'set_charset', $dbh ) ) { 738 | $this->ex_mysql_set_charset( $charset, $dbh ); 739 | $this->real_escape = true; 740 | } else { 741 | $query = $this->prepare( 'SET NAMES %s', $charset ); 742 | if ( ! empty( $collate ) ) 743 | $query .= $this->prepare( ' COLLATE %s', $collate ); 744 | $this->ex_mysql_query( $query, $dbh ); 745 | } 746 | } 747 | 748 | /* 749 | * Force addslashes() for the escapes. 750 | * 751 | * HyperDB makes connections when a query is made 752 | * which is why we can't use mysql_real_escape_string() for escapes. 753 | * This is also the reason why we don't allow certain charsets. See set_charset(). 754 | */ 755 | function _real_escape( $string ) { 756 | return addslashes( $string ); 757 | } 758 | 759 | /** 760 | * Disconnect and remove connection from open connections list 761 | * @param string $tdbhname 762 | */ 763 | function disconnect($dbhname) { 764 | if ( false !== $k = array_search($dbhname, $this->open_connections) ) 765 | unset($this->open_connections[$k]); 766 | 767 | if ( $this->is_mysql_connection( $this->dbhs[$dbhname] ) ) 768 | $this->ex_mysql_close( $this->dbhs[$dbhname] ); 769 | 770 | unset($this->dbhs[$dbhname]); 771 | } 772 | 773 | /** 774 | * Kill cached query results 775 | */ 776 | function flush() { 777 | $this->last_error = ''; 778 | $this->last_errno = 0; 779 | $this->num_rows = 0; 780 | parent::flush(); 781 | } 782 | 783 | /** 784 | * Basic query. See docs for more details. 785 | * @param string $query 786 | * @return int number of rows 787 | */ 788 | function query( $query ) { 789 | // some queries are made before the plugins have been loaded, and thus cannot be filtered with this method 790 | if ( function_exists('apply_filters') ) 791 | $query = apply_filters('query', $query); 792 | 793 | // initialise return 794 | $return_val = 0; 795 | $this->flush(); 796 | 797 | // Log how the function was called 798 | $this->func_call = "\$db->query(\"$query\")"; 799 | 800 | // Keep track of the last query for debug.. 801 | $this->last_query = $query; 802 | 803 | if ( preg_match( '/^\s*SELECT\s+FOUND_ROWS(\s*)/i', $query ) && $this->is_mysql_result( $this->last_found_rows_result ) ) { 804 | $this->result = $this->last_found_rows_result; 805 | $elapsed = 0; 806 | } else { 807 | $this->dbh = $this->db_connect( $query ); 808 | 809 | if ( ! $this->is_mysql_connection( $this->dbh ) ) { 810 | $this->check_current_query = true; 811 | $this->last_error = 'Database connection failed'; 812 | $this->num_failed_queries++; 813 | return false; 814 | } 815 | 816 | $query_comment = $this->run_callbacks( 'get_query_comment', $query ); 817 | if ( ! empty( $query_comment ) ) 818 | $query = rtrim( $query, ";\t \n\r" ) . ' /* ' . $query_comment . ' */'; 819 | 820 | // If we're writing to the database, make sure the query will write safely. 821 | if ( $this->check_current_query && method_exists( $this, 'check_ascii' ) && ! $this->check_ascii( $query ) ) { 822 | $stripped_query = $this->strip_invalid_text_from_query( $query ); 823 | if ( $stripped_query !== $query ) { 824 | $this->insert_id = 0; 825 | $this->last_error = 'Invalid query'; 826 | $this->num_failed_queries++; 827 | return false; 828 | } 829 | } 830 | 831 | $this->check_current_query = true; 832 | 833 | // Inject setup and teardown statements 834 | $statement_before_query = $this->run_callbacks( 'statement_before_query' ); 835 | $statement_after_query = $this->run_callbacks( 'statement_after_query' ); 836 | $query_for_log = $query; 837 | 838 | $this->timer_start(); 839 | if ( $statement_before_query ) { 840 | $query_for_log = "$statement_before_query; $query_for_log"; 841 | $this->ex_mysql_query( $statement_before_query, $this->dbh ); 842 | } 843 | 844 | $this->result = $this->ex_mysql_query( $query, $this->dbh ); 845 | 846 | if ( $statement_after_query ) { 847 | $query_for_log = "$query_for_log; $statement_after_query"; 848 | $this->ex_mysql_query( $statement_after_query, $this->dbh ); 849 | } 850 | $elapsed = $this->timer_stop(); 851 | ++$this->num_queries; 852 | 853 | if ( preg_match('/^\s*SELECT\s+SQL_CALC_FOUND_ROWS\s/i', $query) ) { 854 | if ( false === strpos($query, "NO_SELECT_FOUND_ROWS") ) { 855 | $this->timer_start(); 856 | $this->last_found_rows_result = $this->ex_mysql_query( "SELECT FOUND_ROWS()", $this->dbh ); 857 | $elapsed += $this->timer_stop(); 858 | ++$this->num_queries; 859 | $query .= "; SELECT FOUND_ROWS()"; 860 | } 861 | } else { 862 | $this->last_found_rows_result = null; 863 | } 864 | 865 | $this->dbhname_heartbeats[$this->dbhname]['last_used'] = microtime( true ); 866 | 867 | if ( $this->save_queries ) { 868 | if ( is_callable($this->save_query_callback) ) { 869 | $saved_query = call_user_func_array( $this->save_query_callback, array( $query_for_log, $elapsed, $this->save_backtrace ? debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ) : null, &$this ) ); 870 | if ( $saved_query !== null ) { 871 | $this->queries[] = $saved_query; 872 | } 873 | } else { 874 | $this->queries[] = array( $query_for_log, $elapsed, $this->get_caller() ); 875 | } 876 | } 877 | } 878 | 879 | // If there is an error then take note of it 880 | if ( $this->last_error = $this->ex_mysql_error( $this->dbh ) ) { 881 | $this->last_errno = $this->ex_mysql_errno( $this->dbh ); 882 | $this->dbhname_heartbeats[$this->dbhname]['last_errno'] = $this->last_errno; 883 | $this->print_error($this->last_error); 884 | $this->num_failed_queries++; 885 | return false; 886 | } 887 | 888 | if ( preg_match('/^\s*(insert|delete|update|replace|alter)\s/i',$query) ) { 889 | $this->rows_affected = $this->ex_mysql_affected_rows( $this->dbh ); 890 | 891 | // Take note of the insert_id 892 | if ( preg_match('/^\s*(insert|replace)\s/i',$query) ) { 893 | $this->insert_id = $this->ex_mysql_insert_id( $this->dbh ); 894 | } 895 | // Return number of rows affected 896 | $return_val = $this->rows_affected; 897 | } else if ( is_bool( $this->result ) ) { 898 | $return_val = $this->result; 899 | $this->result = null; 900 | } else { 901 | $i = 0; 902 | $this->col_info = array(); 903 | while ( $i < $this->ex_mysql_num_fields( $this->result ) ) { 904 | $this->col_info[$i] = $this->ex_mysql_fetch_field( $this->result ); 905 | $i++; 906 | } 907 | $num_rows = 0; 908 | $this->last_result = array(); 909 | while ( $row = $this->ex_mysql_fetch_object( $this->result ) ) { 910 | $this->last_result[$num_rows] = $row; 911 | $num_rows++; 912 | } 913 | 914 | $this->ex_mysql_free_result( $this->result ); 915 | $this->result = null; 916 | 917 | // Log number of rows the query returned 918 | $this->num_rows = $num_rows; 919 | 920 | // Return number of rows selected 921 | $return_val = $this->num_rows; 922 | } 923 | 924 | return $return_val; 925 | } 926 | 927 | /** 928 | * Whether or not MySQL database is at least the required minimum version. 929 | * The additional argument allows the caller to check a specific database. 930 | * 931 | * @since 2.5.0 932 | * @uses $wp_version 933 | * 934 | * @return WP_Error 935 | */ 936 | function check_database_version( $dbh_or_table = false ) { 937 | global $wp_version; 938 | // Make sure the server has MySQL 4.1.2 939 | $mysql_version = preg_replace( '|[^0-9\.]|', '', $this->db_version( $dbh_or_table ) ); 940 | if ( version_compare($mysql_version, '4.1.2', '<') ) 941 | return new WP_Error( 'database_version', sprintf(__('ERROR: WordPress %s requires MySQL 4.1.2 or higher'), $wp_version) ); 942 | } 943 | 944 | /** 945 | * This function is called when WordPress is generating the table schema to determine wether or not the current database 946 | * supports or needs the collation statements. 947 | * The additional argument allows the caller to check a specific database. 948 | * @return bool 949 | */ 950 | function supports_collation( $dbh_or_table = false ) { 951 | return $this->has_cap( 'collation', $dbh_or_table ); 952 | } 953 | 954 | /** 955 | * Generic function to determine if a database supports a particular feature 956 | * The additional argument allows the caller to check a specific database. 957 | * @param string $db_cap the feature 958 | * @param false|string|resource $dbh_or_table the databaese (the current database, the database housing the specified table, or the database of the mysql resource) 959 | * @return bool 960 | */ 961 | function has_cap( $db_cap, $dbh_or_table = false ) { 962 | $version = $this->db_version( $dbh_or_table ); 963 | 964 | switch ( strtolower( $db_cap ) ) : 965 | case 'collation' : 966 | case 'group_concat' : 967 | case 'subqueries' : 968 | return version_compare($version, '4.1', '>='); 969 | case 'set_charset' : 970 | return version_compare($version, '5.0.7', '>='); 971 | endswitch; 972 | 973 | return false; 974 | } 975 | 976 | /** 977 | * The database version number 978 | * @param false|string|resource $dbh_or_table the databaese (the current database, the database housing the specified table, or the database of the mysql resource) 979 | * @return false|string false on failure, version number on success 980 | */ 981 | function db_version( $dbh_or_table = false ) { 982 | if ( !$dbh_or_table && $this->dbh ) 983 | $dbh =& $this->dbh; 984 | elseif ( $this->is_mysql_connection( $dbh_or_table ) ) 985 | $dbh =& $dbh_or_table; 986 | else 987 | $dbh = $this->db_connect( "SELECT FROM $dbh_or_table $this->users" ); 988 | 989 | if ( $dbh ) 990 | return preg_replace( '/[^0-9.].*/', '', $this->ex_mysql_get_server_info( $dbh ) ); 991 | return false; 992 | } 993 | 994 | /** 995 | * Get the name of the function that called wpdb. 996 | * @return string the name of the calling function 997 | */ 998 | function get_caller() { 999 | // requires PHP 4.3+ 1000 | if ( !is_callable('debug_backtrace') ) 1001 | return ''; 1002 | 1003 | $hyper_callbacks = array(); 1004 | foreach ( $this->hyper_callbacks as $group_name => $group_callbacks ) 1005 | $hyper_callbacks = array_merge( $hyper_callbacks, $group_callbacks ); 1006 | 1007 | $bt = debug_backtrace( false ); 1008 | $caller = ''; 1009 | 1010 | foreach ( (array) $bt as $trace ) { 1011 | if ( isset($trace['class']) && is_a( $this, $trace['class'] ) ) 1012 | continue; 1013 | elseif ( !isset($trace['function']) ) 1014 | continue; 1015 | elseif ( strtolower($trace['function']) == 'call_user_func_array' ) 1016 | continue; 1017 | elseif ( strtolower($trace['function']) == 'apply_filters' ) 1018 | continue; 1019 | elseif ( strtolower($trace['function']) == 'do_action' ) 1020 | continue; 1021 | 1022 | if ( in_array( strtolower($trace['function']), $hyper_callbacks ) ) 1023 | continue; 1024 | 1025 | if ( isset($trace['class']) ) 1026 | $caller = $trace['class'] . '::' . $trace['function']; 1027 | else 1028 | $caller = $trace['function']; 1029 | break; 1030 | } 1031 | return $caller; 1032 | } 1033 | 1034 | function log_and_bail( $msg ) { 1035 | $logged = $this->run_callbacks( 'log_and_bail', $msg ); 1036 | 1037 | if ( ! $logged ) 1038 | error_log( "WordPress database error $msg for query {$this->last_query} made by " . $this->get_caller() ); 1039 | 1040 | return $this->bail( $msg ); 1041 | } 1042 | 1043 | /** 1044 | * Check the responsiveness of a tcp/ip daemon 1045 | * @return (string) 'up' when $host:$post responds within $float_timeout seconds, 1046 | * otherwise a string with details about the failure. 1047 | */ 1048 | function check_tcp_responsiveness( $host, $port, $float_timeout ) { 1049 | if ( function_exists( 'apcu_store' ) ) { 1050 | $use_apc = true; 1051 | $apcu_key = "tcp_responsive_{$host}{$port}"; 1052 | $apcu_ttl = 10; 1053 | } else { 1054 | $use_apc = false; 1055 | } 1056 | 1057 | if ( $use_apc ) { 1058 | $server_state = apcu_fetch( $apcu_key ); 1059 | if ( $server_state ) 1060 | return $server_state; 1061 | } 1062 | 1063 | $socket = @ fsockopen( $host, $port, $errno, $errstr, $float_timeout ); 1064 | if ( $socket === false ) { 1065 | $server_state = "down [ > $float_timeout ] ($errno) '$errstr'"; 1066 | if ( $use_apc ) 1067 | apcu_store( $apcu_key, $server_state, $apcu_ttl ); 1068 | 1069 | return $server_state; 1070 | } 1071 | 1072 | fclose( $socket ); 1073 | 1074 | if ( $use_apc ) 1075 | apcu_store( $apcu_key, 'up', $apcu_ttl ); 1076 | 1077 | return 'up'; 1078 | } 1079 | 1080 | function get_server_state( $host, $port, $timeout ) { 1081 | // We still do the check_tcp_responsiveness() until we have 1082 | // mysql connect function with less than 1 second timeout 1083 | if ( $this->check_tcp_responsiveness ) { 1084 | $server_state = $this->check_tcp_responsiveness( $host, $port, $timeout ); 1085 | if ( 'up' !== $server_state ) 1086 | return $server_state; 1087 | } 1088 | 1089 | if ( ! function_exists( 'apcu_store' ) ) 1090 | return 'up'; 1091 | 1092 | $server_state = apcu_fetch( "server_state_$host$port" ); 1093 | if ( ! $server_state ) 1094 | return 'up'; 1095 | 1096 | return $server_state; 1097 | } 1098 | 1099 | function mark_server_as_down( $host, $port, $apcu_ttl = 10 ) { 1100 | if ( ! function_exists( 'apcu_store' ) ) 1101 | return; 1102 | 1103 | apcu_add( "server_state_$host$port", 'down', $apcu_ttl ); 1104 | } 1105 | 1106 | function set_connect_timeout( $tag, $use_master, $tries_remaining ) { 1107 | static $default_connect_timeout; 1108 | 1109 | if ( ! isset ( $default_connect_timeout ) ) 1110 | $default_connect_timeout = $this->ex_mysql_connect_timeout(); 1111 | 1112 | switch ( $tag ) { 1113 | case 'pre_connect': 1114 | if ( ! $use_master && $tries_remaining ) 1115 | $this->ex_mysql_connect_timeout( 1 ); 1116 | break; 1117 | case 'post_connect': 1118 | default: 1119 | if ( ! $use_master && $tries_remaining ) 1120 | $this->ex_mysql_connect_timeout( $default_connect_timeout ); 1121 | break; 1122 | } 1123 | } 1124 | 1125 | function get_lag_cache() { 1126 | $this->lag = $this->run_callbacks( 'get_lag_cache' ); 1127 | 1128 | return $this->check_lag(); 1129 | } 1130 | 1131 | function get_lag() { 1132 | $this->lag = $this->run_callbacks( 'get_lag' ); 1133 | 1134 | return $this->check_lag(); 1135 | } 1136 | 1137 | function check_lag() { 1138 | if ( $this->lag === false ) 1139 | return HYPERDB_LAG_UNKNOWN; 1140 | 1141 | if ( $this->lag > $this->lag_threshold ) 1142 | return HYPERDB_LAG_BEHIND; 1143 | 1144 | return HYPERDB_LAG_OK; 1145 | } 1146 | 1147 | function should_use_mysqli() { 1148 | if ( ! function_exists( 'mysqli_connect' ) ) 1149 | return false; 1150 | 1151 | if ( defined( 'WP_USE_EXT_MYSQL' ) && WP_USE_EXT_MYSQL ) 1152 | return false; 1153 | 1154 | return true; 1155 | } 1156 | 1157 | function should_mysql_ping() { 1158 | // Shouldn't happen 1159 | if ( ! isset( $this->dbhname_heartbeats[$this->dbhname] ) ) 1160 | return true; 1161 | 1162 | // MySQL server has gone away 1163 | if ( isset( $this->dbhname_heartbeats[$this->dbhname]['last_errno'] ) && 1164 | HYPERDB_SERVER_GONE_ERROR == $this->dbhname_heartbeats[$this->dbhname]['last_errno'] ) 1165 | { 1166 | unset( $this->dbhname_heartbeats[$this->dbhname]['last_errno'] ); 1167 | return true; 1168 | } 1169 | 1170 | // More than 0.1 seconds of inactivity on that dbhname 1171 | if ( microtime( true ) - $this->dbhname_heartbeats[$this->dbhname]['last_used'] > 0.1 ) 1172 | return true; 1173 | 1174 | return false; 1175 | } 1176 | 1177 | function is_mysql_connection( $dbh ) { 1178 | if ( ! $this->use_mysqli ) 1179 | return is_resource( $dbh ); 1180 | 1181 | return $dbh instanceof mysqli; 1182 | } 1183 | 1184 | function is_mysql_result( $result ) { 1185 | if ( ! $this->use_mysqli ) 1186 | return is_resource( $result ); 1187 | 1188 | return $result instanceof mysqli_result; 1189 | } 1190 | 1191 | function is_mysql_set_charset_callable() { 1192 | if ( ! $this->use_mysqli ) 1193 | return function_exists( 'mysql_set_charset' ); 1194 | 1195 | return function_exists( 'mysqli_set_charset' ); 1196 | } 1197 | 1198 | // MySQL execution functions. 1199 | // They perform the appropriate calls based on whether we use MySQLi. 1200 | 1201 | function ex_mysql_query( $query, $dbh ) { 1202 | if ( ! $this->use_mysqli ) 1203 | return mysql_query( $query, $dbh ); 1204 | 1205 | return mysqli_query( $dbh, $query ); 1206 | } 1207 | 1208 | function ex_mysql_unbuffered_query( $query, $dbh ) { 1209 | if ( ! $this->use_mysqli ) 1210 | return mysql_unbuffered_query( $query, $dbh ); 1211 | 1212 | return mysqli_query( $dbh, $query, MYSQLI_USE_RESULT ); 1213 | } 1214 | 1215 | function ex_mysql_connect( $db_host, $db_user, $db_password, $persistent ) { 1216 | if ( ! $this->use_mysqli ) { 1217 | $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; 1218 | return @$connect_function( $db_host, $db_user, $db_password, true ); 1219 | } 1220 | 1221 | $dbh = mysqli_init(); 1222 | 1223 | // mysqli_real_connect doesn't support the host param including a port or socket 1224 | // like mysql_connect does. This duplicates how mysql_connect detects a port and/or socket file. 1225 | $port = null; 1226 | $socket = null; 1227 | $port_or_socket = strstr( $db_host, ':' ); 1228 | if ( ! empty( $port_or_socket ) ) { 1229 | $db_host = substr( $db_host, 0, strpos( $db_host, ':' ) ); 1230 | $port_or_socket = substr( $port_or_socket, 1 ); 1231 | if ( 0 !== strpos( $port_or_socket, '/' ) ) { 1232 | $port = intval( $port_or_socket ); 1233 | $maybe_socket = strstr( $port_or_socket, ':' ); 1234 | if ( ! empty( $maybe_socket ) ) { 1235 | $socket = substr( $maybe_socket, 1 ); 1236 | } 1237 | } else { 1238 | $socket = $port_or_socket; 1239 | } 1240 | } 1241 | 1242 | if ( $persistent ) 1243 | $db_host = "p:{$db_host}"; 1244 | 1245 | $retval = mysqli_real_connect( $dbh, $db_host, $db_user, $db_password, null, $port, $socket ); 1246 | 1247 | if ( ! $retval || $dbh->connect_errno ) 1248 | return false; 1249 | 1250 | return $dbh; 1251 | } 1252 | 1253 | function ex_mysql_select_db( $db_name, $dbh ) { 1254 | if ( ! $this->use_mysqli ) 1255 | return @mysql_select_db( $db_name, $dbh ); 1256 | 1257 | return @mysqli_select_db( $dbh, $db_name ); 1258 | } 1259 | 1260 | function ex_mysql_close( $dbh ) { 1261 | if ( ! $this->use_mysqli ) 1262 | return mysql_close( $dbh ); 1263 | 1264 | return mysqli_close( $dbh ); 1265 | } 1266 | 1267 | function ex_mysql_set_charset( $charset, $dbh ) { 1268 | if ( ! $this->use_mysqli ) 1269 | return mysql_set_charset( $charset, $dbh ); 1270 | 1271 | return mysqli_set_charset( $dbh, $charset ); 1272 | } 1273 | 1274 | function ex_mysql_errno( $dbh = null ) { 1275 | if ( ! $this->use_mysqli ) 1276 | return is_resource( $dbh ) ? mysql_errno( $dbh ) : mysql_errno(); 1277 | 1278 | if ( is_null( $dbh ) ) 1279 | return mysqli_connect_errno(); 1280 | 1281 | return mysqli_errno( $dbh ); 1282 | } 1283 | 1284 | function ex_mysql_error( $dbh = null ) { 1285 | if ( ! $this->use_mysqli ) 1286 | return is_resource( $dbh ) ? mysql_error( $dbh ) : mysql_error(); 1287 | 1288 | if ( is_null( $dbh ) ) 1289 | return mysqli_connect_error(); 1290 | 1291 | if ( ! $this->is_mysql_connection( $dbh ) ) 1292 | return false; 1293 | 1294 | return mysqli_error( $dbh ); 1295 | } 1296 | 1297 | function ex_mysql_ping( $dbh ) { 1298 | if ( ! $this->use_mysqli ) 1299 | return @mysql_ping( $dbh ); 1300 | 1301 | return @mysqli_ping( $dbh ); 1302 | } 1303 | 1304 | function ex_mysql_affected_rows( $dbh ) { 1305 | if ( ! $this->use_mysqli ) 1306 | return mysql_affected_rows( $dbh ); 1307 | 1308 | return mysqli_affected_rows( $dbh ); 1309 | } 1310 | 1311 | function ex_mysql_insert_id( $dbh ) { 1312 | if ( ! $this->use_mysqli ) 1313 | return mysql_insert_id( $dbh ); 1314 | 1315 | return mysqli_insert_id( $dbh ); 1316 | } 1317 | 1318 | function ex_mysql_num_fields( $result ) { 1319 | if ( ! $this->use_mysqli ) 1320 | return @mysql_num_fields( $result ); 1321 | 1322 | return @mysqli_num_fields( $result ); 1323 | } 1324 | 1325 | function ex_mysql_fetch_field( $result ) { 1326 | if ( ! $this->use_mysqli ) 1327 | return @mysql_fetch_field( $result ); 1328 | 1329 | return @mysqli_fetch_field( $result ); 1330 | } 1331 | 1332 | function ex_mysql_fetch_assoc( $result ) { 1333 | if ( ! $this->use_mysqli ) 1334 | return mysql_fetch_assoc( $result ); 1335 | 1336 | if ( ! $this->is_mysql_result( $result ) ) 1337 | return false; 1338 | 1339 | $object = mysqli_fetch_assoc( $result ); 1340 | 1341 | return ! is_null( $object ) ? $object : false; 1342 | } 1343 | 1344 | function ex_mysql_fetch_object( $result ) { 1345 | if ( ! $this->use_mysqli ) 1346 | return @mysql_fetch_object( $result ); 1347 | 1348 | if ( ! $this->is_mysql_result( $result ) ) 1349 | return false; 1350 | 1351 | $object = @mysqli_fetch_object( $result ); 1352 | 1353 | return ! is_null( $object ) ? $object : false; 1354 | } 1355 | 1356 | function ex_mysql_fetch_row( $result ) { 1357 | if ( ! $this->use_mysqli ) 1358 | return mysql_fetch_row( $result ); 1359 | 1360 | if ( ! $this->is_mysql_result( $result ) ) 1361 | return false; 1362 | 1363 | $row = mysqli_fetch_row( $result ); 1364 | 1365 | return ! is_null( $row ) ? $row : false; 1366 | 1367 | } 1368 | 1369 | function ex_mysql_num_rows( $result ) { 1370 | if ( ! $this->use_mysqli ) 1371 | return mysql_num_rows( $result ); 1372 | 1373 | return mysqli_num_rows( $result ); 1374 | } 1375 | 1376 | function ex_mysql_free_result( $result ) { 1377 | if ( ! $this->use_mysqli ) 1378 | return @mysql_free_result( $result ); 1379 | 1380 | return @mysqli_free_result( $result ); 1381 | } 1382 | 1383 | function ex_mysql_get_server_info( $dbh ) { 1384 | if ( ! $this->use_mysqli ) 1385 | return mysql_get_server_info( $dbh ); 1386 | 1387 | return mysqli_get_server_info( $dbh ); 1388 | } 1389 | 1390 | function ex_mysql_connect_timeout( $timeout = null ) { 1391 | if ( is_null( $timeout ) ) { 1392 | if ( ! $this->use_mysqli ) 1393 | return ini_get( 'mysql.connect_timeout' ); 1394 | 1395 | return ini_get( 'default_socket_timeout' ); 1396 | } 1397 | 1398 | if ( ! $this->use_mysqli ) 1399 | return ini_set( 'mysql.connect_timeout', $timeout ); 1400 | 1401 | return ini_set( 'default_socket_timeout', $timeout ); 1402 | } 1403 | // Helper functions for configuration 1404 | 1405 | } // class hyperdb 1406 | 1407 | $wpdb = new hyperdb(); 1408 | 1409 | require( DB_CONFIG_FILE ); 1410 | --------------------------------------------------------------------------------