diff --git a/src/wp-includes/class-wp-theme-json-resolver.php b/src/wp-includes/class-wp-theme-json-resolver.php index 85124cf0e0cb6..02d1a84d96550 100644 --- a/src/wp-includes/class-wp-theme-json-resolver.php +++ b/src/wp-includes/class-wp-theme-json-resolver.php @@ -106,11 +106,12 @@ protected static function read_json_file( $file_path ) { if ( array_key_exists( $file_path, static::$theme_json_file_cache ) ) { return static::$theme_json_file_cache[ $file_path ]; } - - $decoded_file = wp_json_file_decode( $file_path, array( 'associative' => true ) ); - if ( is_array( $decoded_file ) ) { - static::$theme_json_file_cache[ $file_path ] = $decoded_file; - return static::$theme_json_file_cache[ $file_path ]; + if ( is_readable( $file_path ) ) { + $decoded_file = wp_json_file_decode( $file_path, array( 'associative' => true ) ); + if ( is_array( $decoded_file ) ) { + static::$theme_json_file_cache[ $file_path ] = $decoded_file; + return static::$theme_json_file_cache[ $file_path ]; + } } } diff --git a/tests/phpunit/tests/theme/wpThemeJsonResolver/readJsonFile.php b/tests/phpunit/tests/theme/wpThemeJsonResolver/readJsonFile.php new file mode 100644 index 0000000000000..1fb4a5efa4038 --- /dev/null +++ b/tests/phpunit/tests/theme/wpThemeJsonResolver/readJsonFile.php @@ -0,0 +1,103 @@ +setAccessible( true ); + } + static::$property_theme_json_file_cache_orig_value = static::$property_theme_json_file_cache->getValue(); + } + + public static function tear_down_after_class() { + static::$property_theme_json_file_cache->setValue( null, static::$property_theme_json_file_cache_orig_value ); + parent::tear_down_after_class(); + } + + public function tear_down() { + // Reset data between tests. + static::$property_theme_json_file_cache->setValue( null, array() ); + parent::tear_down(); + } + + /** + * @ticket 64620 + */ + public function test_read_json_file() { + $read_json_file = new ReflectionMethod( WP_Theme_JSON_Resolver::class, 'read_json_file' ); + if ( PHP_VERSION_ID < 80100 ) { + $read_json_file->setAccessible( true ); + } + + // Test reading a valid JSON file. + $valid_file = DIR_TESTDATA . '/themedir1/block-theme/theme.json'; + $result = $read_json_file->invoke( null, $valid_file ); + $this->assertIsArray( $result ); + $this->assertArrayHasKey( 'version', $result ); + $this->assertSame( 3, $result['version'] ); + + // Test that the result is cached. + $cache = static::$property_theme_json_file_cache->getValue(); + $this->assertArrayHasKey( $valid_file, $cache ); + $this->assertSame( $result, $cache[ $valid_file ] ); + + // Test cache hit: modify cache and verify read_json_file returns cached value. + $cache[ $valid_file ] = array( + 'version' => 3, + 'cached' => true, + ); + static::$property_theme_json_file_cache->setValue( null, $cache ); + $result = $read_json_file->invoke( null, $valid_file ); + $this->assertSame( 3, $result['version'] ); + $this->assertTrue( $result['cached'] ); + + // Test non-existent file. + $non_existent_file = DIR_TESTDATA . '/non-existent.json'; + $result = $read_json_file->invoke( null, $non_existent_file ); + $this->assertSame( array(), $result ); + + // Test unreadable file. + if ( function_exists( 'posix_getpwuid' ) && 'root' !== posix_getpwuid( posix_geteuid() )['name'] ) { + $unreadable_file = DIR_TESTDATA . '/unreadable.json'; + touch( $unreadable_file ); + chmod( $unreadable_file, 0000 ); + $result = @$read_json_file->invoke( null, $unreadable_file ); + $this->assertSame( array(), $result ); + unlink( $unreadable_file ); + } + + // Test invalid JSON. + $invalid_json_file = tempnam( sys_get_temp_dir(), 'invalid-json' ); + file_put_contents( $invalid_json_file, '{ invalid json }' ); + $result = @$read_json_file->invoke( null, $invalid_json_file ); + $this->assertSame( array(), $result ); + unlink( $invalid_json_file ); + } +}