Make LocalisationCache a service
[lhc/web/wiklou.git] / tests / phpunit / includes / TestLocalisationCache.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 /**
6 * A test-only LocalisationCache that caches all data in memory for test speed.
7 */
8 class TestLocalisationCache extends LocalisationCache {
9
10 /**
11 * A cache of the parsed data for tests. Services are reset between every test, which forces
12 * localization to be recached between every test, which is unreasonably slow. As an
13 * optimization, we cache our data in a static member for tests.
14 *
15 * @var array
16 */
17 private static $testingCache = [];
18
19 private $selfAccess;
20
21 public function __construct() {
22 parent::__construct( ...func_get_args() );
23 $this->selfAccess = TestingAccessWrapper::newFromObject( $this );
24 }
25
26 /**
27 * Recurse through the given array and replace every object by a scalar value that can be
28 * serialized as JSON to use as a hash key.
29 *
30 * @param array $arr
31 * @return array
32 */
33 private static function hashiblifyArray( array $arr ) : array {
34 foreach ( $arr as $key => $val ) {
35 if ( is_array( $val ) ) {
36 $arr[$key] = self::hashiblifyArray( $val );
37 } elseif ( is_object( $val ) ) {
38 // spl_object_hash() may return duplicate values if an object is destroyed and a new
39 // one gets its hash and happens to be registered in the same hook in the same
40 // location. This seems unlikely, but let's be safe and maintain a reference so it
41 // can't happen. (In practice, there are probably no objects in the hooks at all.)
42 static $objects = [];
43 if ( !in_array( $val, $objects, true ) ) {
44 $objects[] = $val;
45 }
46 $arr[$key] = spl_object_hash( $val );
47 }
48 }
49 return $arr;
50 }
51
52 public function recache( $code ) {
53 // Test run performance is killed if we have to regenerate l10n for every test
54 $cacheKey = sha1( json_encode( [
55 $code,
56 $this->selfAccess->options->get( 'ExtensionMessagesFiles' ),
57 $this->selfAccess->options->get( 'MessagesDirs' ),
58 // json_encode doesn't handle objects well
59 self::hashiblifyArray( Hooks::getHandlers( 'LocalisationCacheRecacheFallback' ) ),
60 self::hashiblifyArray( Hooks::getHandlers( 'LocalisationCacheRecache' ) ),
61 ] ) );
62 if ( isset( self::$testingCache[$cacheKey] ) ) {
63 $this->data[$code] = self::$testingCache[$cacheKey];
64 foreach ( self::$testingCache[$cacheKey] as $key => $item ) {
65 $loadedItems = $this->selfAccess->loadedItems;
66 $loadedItems[$code][$key] = true;
67 $this->selfAccess->loadedItems = $loadedItems;
68 }
69 return;
70 }
71
72 parent::recache( $code );
73
74 if ( count( self::$testingCache ) > 4 ) {
75 // Don't store more than a few $data's, they can add up to a lot of memory if
76 // they're kept around for the whole test duration
77 array_pop( self::$testingCache );
78 }
79 // Put the new one in front
80 self::$testingCache = array_merge( [ $cacheKey => $this->data[$code] ], self::$testingCache );
81 }
82 }