Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 42 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
StoriesCache | |
0.00% |
0 / 42 |
|
0.00% |
0 / 9 |
110 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getRelatedStories | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
getStory | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getStories | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
invalidateForArticle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
invalidateStory | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
loadAndRenderStory | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
makeRelatedStoriesKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
makeStoryKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Wikistories; |
4 | |
5 | use MediaWiki\Page\WikiPageFactory; |
6 | use Wikimedia\ObjectCache\WANObjectCache; |
7 | |
8 | class 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 | } |