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