| 1 |
<?php |
| 2 |
/** |
| 3 |
* Deep_Link — build the dashboard hash a notice or recommendation CTA |
| 4 |
* points at (issue #49). |
| 5 |
* |
| 6 |
* The contract, shared with the React shell (src/components/deepLink.ts): |
| 7 |
* |
| 8 |
* #cache panel |
| 9 |
* #health/pagespeed panel + tab |
| 10 |
* #cache?focus=cache:cache_expiry panel + scroll/blink control |
| 11 |
* #cache?focus=cache:cache_expiry&suggest=24 …and offer that value |
| 12 |
* |
| 13 |
* It exists so no caller hand-assembles the string. A CTA that links to a |
| 14 |
* panel and leaves the user hunting for the setting it just described is |
| 15 |
* the exact failure this replaces, and a typo'd hash fails silently — |
| 16 |
* the panel opens, nothing focuses, and nobody notices for a release. |
| 17 |
* |
| 18 |
* Pure: no WordPress calls, so the format is unit-testable on its own. |
| 19 |
* |
| 20 |
* @package XSpeed |
| 21 |
*/ |
| 22 |
|
| 23 |
declare(strict_types=1); |
| 24 |
|
| 25 |
namespace XSpeed; |
| 26 |
|
| 27 |
defined( 'ABSPATH' ) || exit; |
| 28 |
|
| 29 |
final class Deep_Link { |
| 30 |
|
| 31 |
/** |
| 32 |
* Build a dashboard hash. |
| 33 |
* |
| 34 |
* @param string $module Module slug — which panel opens. |
| 35 |
* @param string|null $field Field key to focus, within $focus_module. |
| 36 |
* @param mixed $suggest Recommended value, or null for none. |
| 37 |
* @param string $subtab Tab within a host page. |
| 38 |
* @param string|null $focus_module Module owning $field, when it isn't |
| 39 |
* $module (a host page can render |
| 40 |
* fields from several modules). |
| 41 |
*/ |
| 42 |
public static function hash( string $module, ?string $field = null, $suggest = null, string $subtab = '', ?string $focus_module = null ): string { |
| 43 |
$module = trim( $module ); |
| 44 |
if ( '' === $module ) { |
| 45 |
return '#'; |
| 46 |
} |
| 47 |
|
| 48 |
$hash = '#' . $module; |
| 49 |
if ( '' !== $subtab ) { |
| 50 |
$hash .= '/' . $subtab; |
| 51 |
} |
| 52 |
|
| 53 |
$params = array(); |
| 54 |
if ( null !== $field && '' !== $field ) { |
| 55 |
$params[] = 'focus=' . rawurlencode( ( $focus_module ?? $module ) . ':' . $field ); |
| 56 |
} |
| 57 |
if ( null !== $suggest && '' !== $suggest ) { |
| 58 |
$params[] = 'suggest=' . rawurlencode( self::stringify( $suggest ) ); |
| 59 |
} |
| 60 |
if ( ! empty( $params ) ) { |
| 61 |
$hash .= '?' . implode( '&', $params ); |
| 62 |
} |
| 63 |
|
| 64 |
return $hash; |
| 65 |
} |
| 66 |
|
| 67 |
/** |
| 68 |
* The action array a notice or recommendation carries to the UI. |
| 69 |
* |
| 70 |
* Structured rather than a pre-built hash so the React side owns the |
| 71 |
* final string — one format, one builder per language, and no chance |
| 72 |
* of a half-encoded hash arriving from PHP. |
| 73 |
* |
| 74 |
* @param mixed $suggest Recommended value, or null. |
| 75 |
* @return array<string,string> |
| 76 |
*/ |
| 77 |
public static function action( string $label, string $module, ?string $field = null, $suggest = null, string $subtab = '', ?string $focus_module = null ): array { |
| 78 |
$action = array( |
| 79 |
'label' => $label, |
| 80 |
'module' => $module, |
| 81 |
); |
| 82 |
if ( '' !== $subtab ) { |
| 83 |
$action['subtab'] = $subtab; |
| 84 |
} |
| 85 |
if ( null !== $field && '' !== $field ) { |
| 86 |
$action['focus'] = ( $focus_module ?? $module ) . ':' . $field; |
| 87 |
} |
| 88 |
if ( null !== $suggest && '' !== $suggest ) { |
| 89 |
$action['suggest'] = self::stringify( $suggest ); |
| 90 |
} |
| 91 |
return $action; |
| 92 |
} |
| 93 |
|
| 94 |
/** |
| 95 |
* Render a value the way the link carries it. |
| 96 |
* |
| 97 |
* Booleans become 1/0 rather than PHP's "" for false — an empty string |
| 98 |
* would be dropped as "no suggestion", turning "we recommend turning |
| 99 |
* this off" into no recommendation at all. |
| 100 |
* |
| 101 |
* @param mixed $value Any scalar or list. |
| 102 |
*/ |
| 103 |
private static function stringify( $value ): string { |
| 104 |
if ( is_bool( $value ) ) { |
| 105 |
return $value ? '1' : '0'; |
| 106 |
} |
| 107 |
if ( is_array( $value ) ) { |
| 108 |
return implode( ',', array_map( 'strval', $value ) ); |
| 109 |
} |
| 110 |
return (string) $value; |
| 111 |
} |
| 112 |
} |
| 113 |
|