111 |
116 |
112 return true; |
117 return true; |
113 } |
118 } |
114 |
119 |
115 /** |
120 /** |
116 * Retrieve a network object by its domain and path. |
121 * Retrieve the closest matching network for a domain and path. |
117 * |
122 * |
118 * @since 3.9.0 |
123 * @since 3.9.0 |
|
124 * |
|
125 * @internal In 4.4.0, converted to a wrapper for WP_Network::get_by_path() |
119 * |
126 * |
120 * @param string $domain Domain to check. |
127 * @param string $domain Domain to check. |
121 * @param string $path Path to check. |
128 * @param string $path Path to check. |
122 * @param int|null $segments Path segments to use. Defaults to null, or the full path. |
129 * @param int|null $segments Path segments to use. Defaults to null, or the full path. |
123 * @return object|bool Network object if successful. False when no network is found. |
130 * @return WP_Network|false Network object if successful. False when no network is found. |
124 */ |
131 */ |
125 function get_network_by_path( $domain, $path, $segments = null ) { |
132 function get_network_by_path( $domain, $path, $segments = null ) { |
126 global $wpdb; |
133 return WP_Network::get_by_path( $domain, $path, $segments ); |
127 |
134 } |
128 $domains = array( $domain ); |
135 |
129 $pieces = explode( '.', $domain ); |
136 /** |
130 |
137 * Retrieves the closest matching site object by its domain and path. |
131 /* |
138 * |
132 * It's possible one domain to search is 'com', but it might as well |
139 * This will not necessarily return an exact match for a domain and path. Instead, it |
133 * be 'localhost' or some other locally mapped domain. |
140 * breaks the domain and path into pieces that are then used to match the closest |
134 */ |
141 * possibility from a query. |
135 while ( array_shift( $pieces ) ) { |
142 * |
136 if ( $pieces ) { |
143 * The intent of this method is to match a site object during bootstrap for a |
137 $domains[] = implode( '.', $pieces ); |
144 * requested site address |
138 } |
|
139 } |
|
140 |
|
141 /* |
|
142 * If we've gotten to this function during normal execution, there is |
|
143 * more than one network installed. At this point, who knows how many |
|
144 * we have. Attempt to optimize for the situation where networks are |
|
145 * only domains, thus meaning paths never need to be considered. |
|
146 * |
|
147 * This is a very basic optimization; anything further could have drawbacks |
|
148 * depending on the setup, so this is best done per-install. |
|
149 */ |
|
150 $using_paths = true; |
|
151 if ( wp_using_ext_object_cache() ) { |
|
152 $using_paths = wp_cache_get( 'networks_have_paths', 'site-options' ); |
|
153 if ( false === $using_paths ) { |
|
154 $using_paths = (bool) $wpdb->get_var( "SELECT id FROM $wpdb->site WHERE path <> '/' LIMIT 1" ); |
|
155 wp_cache_add( 'networks_have_paths', (int) $using_paths, 'site-options' ); |
|
156 } |
|
157 } |
|
158 |
|
159 $paths = array(); |
|
160 if ( $using_paths ) { |
|
161 $path_segments = array_filter( explode( '/', trim( $path, "/" ) ) ); |
|
162 |
|
163 /** |
|
164 * Filter the number of path segments to consider when searching for a site. |
|
165 * |
|
166 * @since 3.9.0 |
|
167 * |
|
168 * @param int|null $segments The number of path segments to consider. WordPress by default looks at |
|
169 * one path segment. The function default of null only makes sense when you |
|
170 * know the requested path should match a network. |
|
171 * @param string $domain The requested domain. |
|
172 * @param string $path The requested path, in full. |
|
173 */ |
|
174 $segments = apply_filters( 'network_by_path_segments_count', $segments, $domain, $path ); |
|
175 |
|
176 if ( null !== $segments && count($path_segments ) > $segments ) { |
|
177 $path_segments = array_slice( $path_segments, 0, $segments ); |
|
178 } |
|
179 |
|
180 while ( count( $path_segments ) ) { |
|
181 $paths[] = '/' . implode( '/', $path_segments ) . '/'; |
|
182 array_pop( $path_segments ); |
|
183 } |
|
184 |
|
185 $paths[] = '/'; |
|
186 } |
|
187 |
|
188 /** |
|
189 * Determine a network by its domain and path. |
|
190 * |
|
191 * This allows one to short-circuit the default logic, perhaps by |
|
192 * replacing it with a routine that is more optimal for your setup. |
|
193 * |
|
194 * Return null to avoid the short-circuit. Return false if no network |
|
195 * can be found at the requested domain and path. Otherwise, return |
|
196 * an object from wp_get_network(). |
|
197 * |
|
198 * @since 3.9.0 |
|
199 * |
|
200 * @param null|bool|object $network Network value to return by path. |
|
201 * @param string $domain The requested domain. |
|
202 * @param string $path The requested path, in full. |
|
203 * @param int|null $segments The suggested number of paths to consult. |
|
204 * Default null, meaning the entire path was to be consulted. |
|
205 * @param array $paths The paths to search for, based on $path and $segments. |
|
206 */ |
|
207 $pre = apply_filters( 'pre_get_network_by_path', null, $domain, $path, $segments, $paths ); |
|
208 if ( null !== $pre ) { |
|
209 return $pre; |
|
210 } |
|
211 |
|
212 // @todo Consider additional optimization routes, perhaps as an opt-in for plugins. |
|
213 // We already have paths covered. What about how far domains should be drilled down (including www)? |
|
214 |
|
215 $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'"; |
|
216 |
|
217 if ( ! $using_paths ) { |
|
218 $network = $wpdb->get_row( "SELECT id, domain, path FROM $wpdb->site |
|
219 WHERE domain IN ($search_domains) ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1" ); |
|
220 if ( $network ) { |
|
221 return wp_get_network( $network ); |
|
222 } |
|
223 return false; |
|
224 |
|
225 } else { |
|
226 $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'"; |
|
227 $networks = $wpdb->get_results( "SELECT id, domain, path FROM $wpdb->site |
|
228 WHERE domain IN ($search_domains) AND path IN ($search_paths) |
|
229 ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC" ); |
|
230 } |
|
231 |
|
232 /* |
|
233 * Domains are sorted by length of domain, then by length of path. |
|
234 * The domain must match for the path to be considered. Otherwise, |
|
235 * a network with the path of / will suffice. |
|
236 */ |
|
237 $found = false; |
|
238 foreach ( $networks as $network ) { |
|
239 if ( $network->domain === $domain || "www.$network->domain" === $domain ) { |
|
240 if ( in_array( $network->path, $paths, true ) ) { |
|
241 $found = true; |
|
242 break; |
|
243 } |
|
244 } |
|
245 if ( $network->path === '/' ) { |
|
246 $found = true; |
|
247 break; |
|
248 } |
|
249 } |
|
250 |
|
251 if ( $found ) { |
|
252 return wp_get_network( $network ); |
|
253 } |
|
254 |
|
255 return false; |
|
256 } |
|
257 |
|
258 /** |
|
259 * Retrieve an object containing information about the requested network. |
|
260 * |
145 * |
261 * @since 3.9.0 |
146 * @since 3.9.0 |
262 * |
147 * @since 4.7.0 Updated to always return a `WP_Site` object. |
263 * @param object|int $network The network's database row or ID. |
148 * |
264 * @return object|bool Object containing network information if found, false if not. |
149 * @global wpdb $wpdb WordPress database abstraction object. |
265 */ |
|
266 function wp_get_network( $network ) { |
|
267 global $wpdb; |
|
268 |
|
269 if ( ! is_object( $network ) ) { |
|
270 $network = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->site WHERE id = %d", $network ) ); |
|
271 if ( ! $network ) { |
|
272 return false; |
|
273 } |
|
274 } |
|
275 |
|
276 return $network; |
|
277 } |
|
278 |
|
279 /** |
|
280 * Retrieve a site object by its domain and path. |
|
281 * |
|
282 * @since 3.9.0 |
|
283 * |
150 * |
284 * @param string $domain Domain to check. |
151 * @param string $domain Domain to check. |
285 * @param string $path Path to check. |
152 * @param string $path Path to check. |
286 * @param int|null $segments Path segments to use. Defaults to null, or the full path. |
153 * @param int|null $segments Path segments to use. Defaults to null, or the full path. |
287 * @return object|bool Site object if successful. False when no site is found. |
154 * @return WP_Site|false Site object if successful. False when no site is found. |
288 */ |
155 */ |
289 function get_site_by_path( $domain, $path, $segments = null ) { |
156 function get_site_by_path( $domain, $path, $segments = null ) { |
290 global $wpdb; |
|
291 |
|
292 $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) ); |
157 $path_segments = array_filter( explode( '/', trim( $path, '/' ) ) ); |
293 |
158 |
294 /** |
159 /** |
295 * Filter the number of path segments to consider when searching for a site. |
160 * Filters the number of path segments to consider when searching for a site. |
296 * |
161 * |
297 * @since 3.9.0 |
162 * @since 3.9.0 |
298 * |
163 * |
299 * @param int|null $segments The number of path segments to consider. WordPress by default looks at |
164 * @param int|null $segments The number of path segments to consider. WordPress by default looks at |
300 * one path segment following the network path. The function default of |
165 * one path segment following the network path. The function default of |
327 * can be found at the requested domain and path. Otherwise, return |
192 * can be found at the requested domain and path. Otherwise, return |
328 * a site object. |
193 * a site object. |
329 * |
194 * |
330 * @since 3.9.0 |
195 * @since 3.9.0 |
331 * |
196 * |
332 * @param null|bool|object $site Site value to return by path. |
197 * @param null|bool|WP_Site $site Site value to return by path. |
333 * @param string $domain The requested domain. |
198 * @param string $domain The requested domain. |
334 * @param string $path The requested path, in full. |
199 * @param string $path The requested path, in full. |
335 * @param int|null $segments The suggested number of paths to consult. |
200 * @param int|null $segments The suggested number of paths to consult. |
336 * Default null, meaning the entire path was to be consulted. |
201 * Default null, meaning the entire path was to be consulted. |
337 * @param array $paths The paths to search for, based on $path and $segments. |
202 * @param array $paths The paths to search for, based on $path and $segments. |
338 */ |
203 */ |
339 $pre = apply_filters( 'pre_get_site_by_path', null, $domain, $path, $segments, $paths ); |
204 $pre = apply_filters( 'pre_get_site_by_path', null, $domain, $path, $segments, $paths ); |
340 if ( null !== $pre ) { |
205 if ( null !== $pre ) { |
|
206 if ( false !== $pre && ! $pre instanceof WP_Site ) { |
|
207 $pre = new WP_Site( $pre ); |
|
208 } |
341 return $pre; |
209 return $pre; |
342 } |
210 } |
343 |
211 |
344 /* |
212 /* |
345 * @todo |
213 * @todo |
346 * get_blog_details(), caching, etc. Consider alternative optimization routes, |
214 * caching, etc. Consider alternative optimization routes, |
347 * perhaps as an opt-in for plugins, rather than using the pre_* filter. |
215 * perhaps as an opt-in for plugins, rather than using the pre_* filter. |
348 * For example: The segments filter can expand or ignore paths. |
216 * For example: The segments filter can expand or ignore paths. |
349 * If persistent caching is enabled, we could query the DB for a path <> '/' |
217 * If persistent caching is enabled, we could query the DB for a path <> '/' |
350 * then cache whether we can just always ignore paths. |
218 * then cache whether we can just always ignore paths. |
351 */ |
219 */ |
353 // Either www or non-www is supported, not both. If a www domain is requested, |
221 // Either www or non-www is supported, not both. If a www domain is requested, |
354 // query for both to provide the proper redirect. |
222 // query for both to provide the proper redirect. |
355 $domains = array( $domain ); |
223 $domains = array( $domain ); |
356 if ( 'www.' === substr( $domain, 0, 4 ) ) { |
224 if ( 'www.' === substr( $domain, 0, 4 ) ) { |
357 $domains[] = substr( $domain, 4 ); |
225 $domains[] = substr( $domain, 4 ); |
358 $search_domains = "'" . implode( "', '", $wpdb->_escape( $domains ) ) . "'"; |
226 } |
|
227 |
|
228 $args = array( |
|
229 'domain__in' => $domains, |
|
230 'path__in' => $paths, |
|
231 'number' => 1, |
|
232 ); |
|
233 |
|
234 if ( count( $domains ) > 1 ) { |
|
235 $args['orderby']['domain_length'] = 'DESC'; |
359 } |
236 } |
360 |
237 |
361 if ( count( $paths ) > 1 ) { |
238 if ( count( $paths ) > 1 ) { |
362 $search_paths = "'" . implode( "', '", $wpdb->_escape( $paths ) ) . "'"; |
239 $args['orderby']['path_length'] = 'DESC'; |
363 } |
240 } |
364 |
241 |
365 if ( count( $domains ) > 1 && count( $paths ) > 1 ) { |
242 $result = get_sites( $args ); |
366 $site = $wpdb->get_row( "SELECT * FROM $wpdb->blogs WHERE domain IN ($search_domains) AND path IN ($search_paths) ORDER BY CHAR_LENGTH(domain) DESC, CHAR_LENGTH(path) DESC LIMIT 1" ); |
243 $site = array_shift( $result ); |
367 } elseif ( count( $domains ) > 1 ) { |
244 |
368 $sql = $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE path = %s", $paths[0] ); |
245 if ( $site ) { |
369 $sql .= " AND domain IN ($search_domains) ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1"; |
246 return $site; |
370 $site = $wpdb->get_row( $sql ); |
247 } |
371 } elseif ( count( $paths ) > 1 ) { |
248 |
372 $sql = $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s", $domains[0] ); |
249 return false; |
373 $sql .= " AND path IN ($search_paths) ORDER BY CHAR_LENGTH(path) DESC LIMIT 1"; |
250 } |
374 $site = $wpdb->get_row( $sql ); |
251 |
|
252 /** |
|
253 * Identifies the network and site of a requested domain and path and populates the |
|
254 * corresponding network and site global objects as part of the multisite bootstrap process. |
|
255 * |
|
256 * Prior to 4.6.0, this was a procedural block in `ms-settings.php`. It was wrapped into |
|
257 * a function to facilitate unit tests. It should not be used outside of core. |
|
258 * |
|
259 * Usually, it's easier to query the site first, which then declares its network. |
|
260 * In limited situations, we either can or must find the network first. |
|
261 * |
|
262 * If a network and site are found, a `true` response will be returned so that the |
|
263 * request can continue. |
|
264 * |
|
265 * If neither a network or site is found, `false` or a URL string will be returned |
|
266 * so that either an error can be shown or a redirect can occur. |
|
267 * |
|
268 * @since 4.6.0 |
|
269 * @access private |
|
270 * |
|
271 * @global WP_Network $current_site The current network. |
|
272 * @global WP_Site $current_blog The current site. |
|
273 * |
|
274 * @param string $domain The requested domain. |
|
275 * @param string $path The requested path. |
|
276 * @param bool $subdomain Optional. Whether a subdomain (true) or subdirectory (false) configuration. |
|
277 * Default false. |
|
278 * @return bool|string True if bootstrap successfully populated `$current_blog` and `$current_site`. |
|
279 * False if bootstrap could not be properly completed. |
|
280 * Redirect URL if parts exist, but the request as a whole can not be fulfilled. |
|
281 */ |
|
282 function ms_load_current_site_and_network( $domain, $path, $subdomain = false ) { |
|
283 global $current_site, $current_blog; |
|
284 |
|
285 // If the network is defined in wp-config.php, we can simply use that. |
|
286 if ( defined( 'DOMAIN_CURRENT_SITE' ) && defined( 'PATH_CURRENT_SITE' ) ) { |
|
287 $current_site = new stdClass; |
|
288 $current_site->id = defined( 'SITE_ID_CURRENT_SITE' ) ? SITE_ID_CURRENT_SITE : 1; |
|
289 $current_site->domain = DOMAIN_CURRENT_SITE; |
|
290 $current_site->path = PATH_CURRENT_SITE; |
|
291 if ( defined( 'BLOG_ID_CURRENT_SITE' ) ) { |
|
292 $current_site->blog_id = BLOG_ID_CURRENT_SITE; |
|
293 } elseif ( defined( 'BLOGID_CURRENT_SITE' ) ) { // deprecated. |
|
294 $current_site->blog_id = BLOGID_CURRENT_SITE; |
|
295 } |
|
296 |
|
297 if ( 0 === strcasecmp( $current_site->domain, $domain ) && 0 === strcasecmp( $current_site->path, $path ) ) { |
|
298 $current_blog = get_site_by_path( $domain, $path ); |
|
299 } elseif ( '/' !== $current_site->path && 0 === strcasecmp( $current_site->domain, $domain ) && 0 === stripos( $path, $current_site->path ) ) { |
|
300 // If the current network has a path and also matches the domain and path of the request, |
|
301 // we need to look for a site using the first path segment following the network's path. |
|
302 $current_blog = get_site_by_path( $domain, $path, 1 + count( explode( '/', trim( $current_site->path, '/' ) ) ) ); |
|
303 } else { |
|
304 // Otherwise, use the first path segment (as usual). |
|
305 $current_blog = get_site_by_path( $domain, $path, 1 ); |
|
306 } |
|
307 |
|
308 } elseif ( ! $subdomain ) { |
|
309 /* |
|
310 * A "subdomain" installation can be re-interpreted to mean "can support any domain". |
|
311 * If we're not dealing with one of these installations, then the important part is determining |
|
312 * the network first, because we need the network's path to identify any sites. |
|
313 */ |
|
314 if ( ! $current_site = wp_cache_get( 'current_network', 'site-options' ) ) { |
|
315 // Are there even two networks installed? |
|
316 $networks = get_networks( array( 'number' => 2 ) ); |
|
317 if ( count( $networks ) === 1 ) { |
|
318 $current_site = array_shift( $networks ); |
|
319 wp_cache_add( 'current_network', $current_site, 'site-options' ); |
|
320 } elseif ( empty( $networks ) ) { |
|
321 // A network not found hook should fire here. |
|
322 return false; |
|
323 } |
|
324 } |
|
325 |
|
326 if ( empty( $current_site ) ) { |
|
327 $current_site = WP_Network::get_by_path( $domain, $path, 1 ); |
|
328 } |
|
329 |
|
330 if ( empty( $current_site ) ) { |
|
331 /** |
|
332 * Fires when a network cannot be found based on the requested domain and path. |
|
333 * |
|
334 * At the time of this action, the only recourse is to redirect somewhere |
|
335 * and exit. If you want to declare a particular network, do so earlier. |
|
336 * |
|
337 * @since 4.4.0 |
|
338 * |
|
339 * @param string $domain The domain used to search for a network. |
|
340 * @param string $path The path used to search for a path. |
|
341 */ |
|
342 do_action( 'ms_network_not_found', $domain, $path ); |
|
343 |
|
344 return false; |
|
345 } elseif ( $path === $current_site->path ) { |
|
346 $current_blog = get_site_by_path( $domain, $path ); |
|
347 } else { |
|
348 // Search the network path + one more path segment (on top of the network path). |
|
349 $current_blog = get_site_by_path( $domain, $path, substr_count( $current_site->path, '/' ) ); |
|
350 } |
375 } else { |
351 } else { |
376 $site = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s AND path = %s", $domains[0], $paths[0] ) ); |
352 // Find the site by the domain and at most the first path segment. |
377 } |
353 $current_blog = get_site_by_path( $domain, $path, 1 ); |
378 |
354 if ( $current_blog ) { |
379 if ( $site ) { |
355 $current_site = WP_Network::get_instance( $current_blog->site_id ? $current_blog->site_id : 1 ); |
380 // @todo get_blog_details() |
356 } else { |
381 return $site; |
357 // If you don't have a site with the same domain/path as a network, you're pretty screwed, but: |
382 } |
358 $current_site = WP_Network::get_by_path( $domain, $path, 1 ); |
383 |
359 } |
384 return false; |
360 } |
|
361 |
|
362 // The network declared by the site trumps any constants. |
|
363 if ( $current_blog && $current_blog->site_id != $current_site->id ) { |
|
364 $current_site = WP_Network::get_instance( $current_blog->site_id ); |
|
365 } |
|
366 |
|
367 // No network has been found, bail. |
|
368 if ( empty( $current_site ) ) { |
|
369 /** This action is documented in wp-includes/ms-settings.php */ |
|
370 do_action( 'ms_network_not_found', $domain, $path ); |
|
371 |
|
372 return false; |
|
373 } |
|
374 |
|
375 // During activation of a new subdomain, the requested site does not yet exist. |
|
376 if ( empty( $current_blog ) && wp_installing() ) { |
|
377 $current_blog = new stdClass; |
|
378 $current_blog->blog_id = $blog_id = 1; |
|
379 $current_blog->public = 1; |
|
380 } |
|
381 |
|
382 // No site has been found, bail. |
|
383 if ( empty( $current_blog ) ) { |
|
384 // We're going to redirect to the network URL, with some possible modifications. |
|
385 $scheme = is_ssl() ? 'https' : 'http'; |
|
386 $destination = "$scheme://{$current_site->domain}{$current_site->path}"; |
|
387 |
|
388 /** |
|
389 * Fires when a network can be determined but a site cannot. |
|
390 * |
|
391 * At the time of this action, the only recourse is to redirect somewhere |
|
392 * and exit. If you want to declare a particular site, do so earlier. |
|
393 * |
|
394 * @since 3.9.0 |
|
395 * |
|
396 * @param object $current_site The network that had been determined. |
|
397 * @param string $domain The domain used to search for a site. |
|
398 * @param string $path The path used to search for a site. |
|
399 */ |
|
400 do_action( 'ms_site_not_found', $current_site, $domain, $path ); |
|
401 |
|
402 if ( $subdomain && ! defined( 'NOBLOGREDIRECT' ) ) { |
|
403 // For a "subdomain" installation, redirect to the signup form specifically. |
|
404 $destination .= 'wp-signup.php?new=' . str_replace( '.' . $current_site->domain, '', $domain ); |
|
405 } elseif ( $subdomain ) { |
|
406 // For a "subdomain" installation, the NOBLOGREDIRECT constant |
|
407 // can be used to avoid a redirect to the signup form. |
|
408 // Using the ms_site_not_found action is preferred to the constant. |
|
409 if ( '%siteurl%' !== NOBLOGREDIRECT ) { |
|
410 $destination = NOBLOGREDIRECT; |
|
411 } |
|
412 } elseif ( 0 === strcasecmp( $current_site->domain, $domain ) ) { |
|
413 /* |
|
414 * If the domain we were searching for matches the network's domain, |
|
415 * it's no use redirecting back to ourselves -- it'll cause a loop. |
|
416 * As we couldn't find a site, we're simply not installed. |
|
417 */ |
|
418 return false; |
|
419 } |
|
420 |
|
421 return $destination; |
|
422 } |
|
423 |
|
424 // Figure out the current network's main site. |
|
425 if ( empty( $current_site->blog_id ) ) { |
|
426 $current_site->blog_id = get_main_site_id( $current_site->id ); |
|
427 } |
|
428 |
|
429 return true; |
385 } |
430 } |
386 |
431 |
387 /** |
432 /** |
388 * Displays a failure message. |
433 * Displays a failure message. |
389 * |
434 * |
390 * Used when a blog's tables do not exist. Checks for a missing $wpdb->site table as well. |
435 * Used when a blog's tables do not exist. Checks for a missing $wpdb->site table as well. |
391 * |
436 * |
392 * @access private |
437 * @access private |
393 * @since 3.0.0 |
438 * @since 3.0.0 |
394 */ |
439 * @since 4.4.0 The `$domain` and `$path` parameters were added. |
395 function ms_not_installed() { |
440 * |
396 global $wpdb, $domain, $path; |
441 * @global wpdb $wpdb WordPress database abstraction object. |
|
442 * |
|
443 * @param string $domain The requested domain for the error to reference. |
|
444 * @param string $path The requested path for the error to reference. |
|
445 */ |
|
446 function ms_not_installed( $domain, $path ) { |
|
447 global $wpdb; |
397 |
448 |
398 if ( ! is_admin() ) { |
449 if ( ! is_admin() ) { |
399 dead_db(); |
450 dead_db(); |
400 } |
451 } |
401 |
452 |