Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.43% covered (success)
91.43%
32 / 35
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SimpleParsoidOutputStash
91.43% covered (success)
91.43%
32 / 35
66.67% covered (warning)
66.67%
4 / 6
12.09
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 makeCacheKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 set
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 get
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 newSelserContextFromJson
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
4.10
 selserContextToJsonArray
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace MediaWiki\Edit;
4
5use BagOStuff;
6use MediaWiki\Content\IContentHandlerFactory;
7use MediaWiki\Parser\Parsoid\PageBundleJsonTrait;
8
9/**
10 * @internal
11 * @since 1.39
12 */
13class SimpleParsoidOutputStash implements ParsoidOutputStash {
14    use PageBundleJsonTrait;
15
16    /** @var BagOStuff */
17    private $bagOfStuff;
18
19    /** @var int */
20    private $duration;
21
22    /** @var IContentHandlerFactory */
23    private $contentHandlerFactory;
24
25    /**
26     * @param IContentHandlerFactory $contentHandlerFactory
27     * @param BagOStuff $bagOfStuff storage backend
28     * @param int $duration cache duration in seconds
29     */
30    public function __construct( IContentHandlerFactory $contentHandlerFactory, BagOStuff $bagOfStuff, int $duration ) {
31        $this->bagOfStuff = $bagOfStuff;
32        $this->duration = $duration;
33        $this->contentHandlerFactory = $contentHandlerFactory;
34    }
35
36    private function makeCacheKey( ParsoidRenderID $renderId ): string {
37        return $this->bagOfStuff->makeKey( 'ParsoidOutputStash', $renderId->getKey() );
38    }
39
40    /**
41     * Before we stash, we serialize & encode into JSON the relevant
42     * parts of the data we need to construct a page bundle in the future.
43     *
44     * @param ParsoidRenderID $renderId Combination of revision ID and revision's time ID
45     * @param SelserContext $selserContext
46     *
47     * @return bool
48     */
49    public function set( ParsoidRenderID $renderId, SelserContext $selserContext ): bool {
50        $jsonic = $this->selserContextToJsonArray( $selserContext );
51
52        $key = $this->makeCacheKey( $renderId );
53        return $this->bagOfStuff->set( $key, $jsonic, $this->duration );
54    }
55
56    /**
57     * This will decode the JSON data and create a page bundle from it
58     * if we have something in the stash that matches a given rendering or
59     * will just return an empty array if no entry in the stash.
60     *
61     * @param ParsoidRenderID $renderId
62     *
63     * @return SelserContext|null
64     */
65    public function get( ParsoidRenderID $renderId ): ?SelserContext {
66        $key = $this->makeCacheKey( $renderId );
67        $jsonic = $this->bagOfStuff->get( $key ) ?? [];
68
69        if ( !is_array( $jsonic ) ) {
70            // Defend against old stashed data.
71            // Only needed for a couple of days after this code has been deployed.
72            return null;
73        }
74
75        $selserContext = $this->newSelserContextFromJson( $jsonic );
76        return $selserContext ?: null;
77    }
78
79    private function newSelserContextFromJson( array $json ): ?SelserContext {
80        if ( !isset( $json['pb'] ) ) {
81            return null;
82        }
83
84        $pb = $this->newPageBundleFromJson( $json['pb'] );
85
86        if ( !$pb ) {
87            return null;
88        }
89
90        $revId = (int)$json['revId'];
91
92        if ( isset( $json['content'] ) ) {
93            $contentHandler = $this->contentHandlerFactory->getContentHandler( $json['content']['model'] );
94            $content = $contentHandler->unserializeContent( $json['content']['data'] );
95        } else {
96            $content = null;
97        }
98
99        return new SelserContext( $pb, $revId, $content );
100    }
101
102    private function selserContextToJsonArray( SelserContext $selserContext ): array {
103        $json = [
104            'revId' => $selserContext->getRevisionID(),
105        ];
106
107        $json['pb'] = $this->jsonSerializePageBundle( $selserContext->getPageBundle() );
108
109        $content = $selserContext->getContent();
110        if ( $content ) {
111            $json['content'] = [
112                'model' => $content->getModel(),
113                'data' => $content->serialize()
114            ];
115        }
116
117        return $json;
118    }
119
120}