Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
StoriesCache
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 9
110
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getRelatedStories
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getStory
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getStories
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 invalidateForArticle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 invalidateStory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 loadAndRenderStory
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 makeRelatedStoriesKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeStoryKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\Wikistories;
4
5use MediaWiki\Page\WikiPageFactory;
6use WANObjectCache;
7
8class StoriesCache {
9
10    /**
11     * This needs to be incremented every time we change
12     * the structure of the cached stories so they can
13     * be invalidated and re-created with the most recent
14     * structure.
15     */
16    private const CACHE_VERSION = 14;
17
18    /**
19     * This defines how long stories will stay in the cache if they not edited.
20     * For testing use WANObjectCache::TTL_UNCACHEABLE
21     */
22    private const CACHE_TTL = WANObjectCache::TTL_WEEK;
23
24    /** @var WANObjectCache */
25    private $wanObjectCache;
26
27    /** @var PageLinksSearch */
28    private $pageLinksSearch;
29
30    /** @var WikiPageFactory */
31    private $wikiPageFactory;
32
33    /** @var StoryRenderer */
34    private $storyRenderer;
35
36    /**
37     * @param WANObjectCache $wanObjectCache
38     * @param PageLinksSearch $pageLinksSearch
39     * @param WikiPageFactory $wikiPageFactory
40     * @param StoryRenderer $storyRenderer
41     */
42    public function __construct(
43        WANObjectCache $wanObjectCache,
44        PageLinksSearch $pageLinksSearch,
45        WikiPageFactory $wikiPageFactory,
46        StoryRenderer $storyRenderer
47    ) {
48        $this->wanObjectCache = $wanObjectCache;
49        $this->pageLinksSearch = $pageLinksSearch;
50        $this->wikiPageFactory = $wikiPageFactory;
51        $this->storyRenderer = $storyRenderer;
52    }
53
54    /**
55     * Retrieve the stories related to the give title
56     *
57     * @param string $titleDbKey
58     * @param int $pageId
59     * @return array Stories linked to the given title
60     */
61    public function getRelatedStories( string $titleDbKey, int $pageId ): array {
62        $ids = $this->wanObjectCache->getWithSetCallback(
63            $this->makeRelatedStoriesKey( $pageId ),
64            self::CACHE_TTL,
65            function () use ( $titleDbKey ) {
66                return $this->pageLinksSearch->getPageLinks( $titleDbKey, 10 );
67            }
68        );
69        return array_values( $this->getStories( $ids ) );
70    }
71
72    /**
73     * @param int $id
74     * @return mixed
75     */
76    public function getStory( int $id ) {
77        return $this->wanObjectCache->getWithSetCallback(
78            $this->makeStoryKey( $id ),
79            self::CACHE_TTL,
80            function () use ( $id ) {
81                return $this->loadAndRenderStory( $id );
82            }
83        );
84    }
85
86    /**
87     * @param array $ids
88     * @return mixed[]
89     */
90    private function getStories( array $ids ) {
91        $keys = $this->wanObjectCache->makeMultiKeys(
92            $ids,
93            function ( $id ) {
94                return $this->makeStoryKey( $id );
95            }
96        );
97        $stories = $this->wanObjectCache->getMultiWithSetCallback(
98            $keys,
99            self::CACHE_TTL,
100            function ( $id ) {
101                return $this->loadAndRenderStory( $id );
102            }
103        );
104        return array_filter( $stories );
105    }
106
107    /**
108     * Clear the cached stories for the given article.
109     *
110     * @param int $articleId
111     */
112    public function invalidateForArticle( int $articleId ) {
113        $this->wanObjectCache->delete( $this->makeRelatedStoriesKey( $articleId ) );
114    }
115
116    /**
117     * @param int $storyId
118     */
119    public function invalidateStory( int $storyId ) {
120        $this->wanObjectCache->delete( $this->makeStoryKey( $storyId ) );
121    }
122
123    /**
124     * @param int $storyId
125     * @return array|null
126     */
127    private function loadAndRenderStory( int $storyId ) {
128        $page = $this->wikiPageFactory->newFromID( $storyId );
129        $storyContent = $page->getContent();
130        return $storyContent instanceof StoryContent ?
131            $this->storyRenderer->getStoryData( $storyContent, $page->getTitle() ) :
132            null;
133    }
134
135    /**
136     * @param int $articleId
137     * @return string Cache key for the related stories for an article
138     */
139    private function makeRelatedStoriesKey( int $articleId ): string {
140        return $this->wanObjectCache->makeKey( 'wikistories', self::CACHE_VERSION, 'related', $articleId );
141    }
142
143    /**
144     * @param int $storyId
145     * @return string
146     */
147    private function makeStoryKey( int $storyId ): string {
148        return $this->wanObjectCache->makeKey( 'wikistories', self::CACHE_VERSION, 'story', $storyId );
149    }
150}