Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
40.74% |
33 / 81 |
|
66.67% |
2 / 3 |
CRAP | |
0.00% |
0 / 1 |
ApiEntryPoint | |
40.74% |
33 / 81 |
|
66.67% |
2 / 3 |
48.17 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
getContext | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
35.14% |
26 / 74 |
|
0.00% |
0 / 1 |
44.02 |
1 | <?php |
2 | /** |
3 | * Entry point implementation for all %Action API queries, handled by ApiMain |
4 | * and ApiBase subclasses. |
5 | * |
6 | * @see /api.php The corresponding HTTP entry point. |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
21 | * http://www.gnu.org/copyleft/gpl.html |
22 | * |
23 | * @file |
24 | * @ingroup entrypoint |
25 | * @ingroup API |
26 | */ |
27 | |
28 | namespace MediaWiki\Api; |
29 | |
30 | use ApiMain; |
31 | use LogicException; |
32 | use MediaWiki\Context\RequestContext; |
33 | use MediaWiki\EntryPointEnvironment; |
34 | use MediaWiki\HookContainer\HookRunner; |
35 | use MediaWiki\Logger\LegacyLogger; |
36 | use MediaWiki\MainConfigNames; |
37 | use MediaWiki\MediaWikiEntryPoint; |
38 | use MediaWiki\MediaWikiServices; |
39 | use MediaWiki\Title\Title; |
40 | use Throwable; |
41 | |
42 | /** |
43 | * Implementation of the API entry point, for web browser navigations, usually via an |
44 | * Action or SpecialPage subclass. |
45 | * |
46 | * This is used by bots to fetch content and information about the wiki, |
47 | * its pages, and its users. See <https://www.mediawiki.org/wiki/API> for more |
48 | * information. |
49 | * |
50 | * @see /api.php The corresponding HTTP entry point. |
51 | * @internal |
52 | */ |
53 | class ApiEntryPoint extends MediaWikiEntryPoint { |
54 | |
55 | public function __construct( |
56 | RequestContext $context, |
57 | EntryPointEnvironment $environment, |
58 | MediaWikiServices $services |
59 | ) { |
60 | parent::__construct( |
61 | $context, |
62 | $environment, |
63 | $services |
64 | ); |
65 | } |
66 | |
67 | /** |
68 | * Overwritten to narrow the return type to RequestContext |
69 | * @return RequestContext |
70 | */ |
71 | protected function getContext(): RequestContext { |
72 | /** @var RequestContext $context */ |
73 | $context = parent::getContext(); |
74 | |
75 | // @phan-suppress-next-line PhanTypeMismatchReturnSuperType see $context in the constructor |
76 | return $context; |
77 | } |
78 | |
79 | /** |
80 | * Executes a request to the action API. |
81 | * |
82 | * It begins by constructing a new ApiMain using the parameter passed to it |
83 | * as an argument in the URL ('?action='). It then invokes "execute()" on the |
84 | * ApiMain object instance, which produces output in the format specified in |
85 | * the URL. |
86 | */ |
87 | protected function execute() { |
88 | global $wgTitle; |
89 | |
90 | $context = $this->getContext(); |
91 | $request = $this->getRequest(); |
92 | $apiRequestLog = $this->getConfig( MainConfigNames::APIRequestLog ); |
93 | |
94 | $starttime = microtime( true ); |
95 | |
96 | $services = $this->getServiceContainer(); |
97 | |
98 | // PATH_INFO can be used for stupid things. We don't support it for api.php at |
99 | // all, so error out if it's present. (T128209) |
100 | $pathInfo = $this->environment->getServerInfo( 'PATH_INFO', '' ); |
101 | if ( $pathInfo != '' ) { |
102 | $correctUrl = wfAppendQuery( |
103 | wfScript( 'api' ), |
104 | $request->getQueryValuesOnly() |
105 | ); |
106 | $correctUrl = (string)$services->getUrlUtils()->expand( |
107 | $correctUrl, |
108 | PROTO_CANONICAL |
109 | ); |
110 | $this->header( |
111 | "Location: $correctUrl", |
112 | true, |
113 | 301 |
114 | ); |
115 | $this->print( |
116 | 'This endpoint does not support "path info", i.e. extra text ' . |
117 | 'between "api.php" and the "?". Remove any such text and try again.' |
118 | ); |
119 | $this->exit( 1 ); |
120 | } |
121 | |
122 | // Set a dummy $wgTitle, because $wgTitle == null breaks various things |
123 | // In a perfect world this wouldn't be necessary |
124 | $wgTitle = Title::makeTitle( |
125 | NS_SPECIAL, |
126 | 'Badtitle/dummy title for API calls set in api.php' |
127 | ); |
128 | |
129 | // RequestContext will read from $wgTitle, but it will also whine about it. |
130 | // In a perfect world this wouldn't be necessary either. |
131 | $context->setTitle( $wgTitle ); |
132 | |
133 | try { |
134 | // Construct an ApiMain with the arguments passed via the URL. What we get back |
135 | // is some form of an ApiMain, possibly even one that produces an error message, |
136 | // but we don't care here, as that is handled by the constructor. |
137 | $processor = new ApiMain( |
138 | $context, |
139 | true, |
140 | false |
141 | ); |
142 | |
143 | // Last chance hook before executing the API |
144 | ( new HookRunner( $services->getHookContainer() ) )->onApiBeforeMain( $processor ); |
145 | if ( !$processor instanceof ApiMain ) { |
146 | throw new LogicException( |
147 | 'ApiBeforeMain hook set $processor to a non-ApiMain class' |
148 | ); |
149 | } |
150 | } catch ( Throwable $e ) { |
151 | // Crap. Try to report the exception in API format to be friendly to clients. |
152 | ApiMain::handleApiBeforeMainException( $e ); |
153 | $processor = false; |
154 | } |
155 | |
156 | // Process data & print results |
157 | if ( $processor ) { |
158 | $processor->execute(); |
159 | } |
160 | |
161 | // Log what the user did, for book-keeping purposes. |
162 | $endtime = microtime( true ); |
163 | |
164 | // Log the request |
165 | if ( $apiRequestLog ) { |
166 | $items = [ |
167 | wfTimestamp( TS_MW ), |
168 | $endtime - $starttime, |
169 | $request->getIP(), |
170 | $request->getHeader( 'User-agent' ) |
171 | ]; |
172 | $items[] = $request->wasPosted() ? 'POST' : 'GET'; |
173 | if ( $processor ) { |
174 | try { |
175 | $manager = $processor->getModuleManager(); |
176 | $module = $manager->getModule( |
177 | $request->getRawVal( 'action' ), |
178 | 'action' |
179 | ); |
180 | } catch ( Throwable $ex ) { |
181 | $module = null; |
182 | } |
183 | if ( !$module || $module->mustBePosted() ) { |
184 | $items[] = "action=" . $request->getRawVal( 'action' ); |
185 | } else { |
186 | $items[] = wfArrayToCgi( $request->getValues() ); |
187 | } |
188 | } else { |
189 | $items[] = "failed in ApiBeforeMain"; |
190 | } |
191 | LegacyLogger::emit( |
192 | implode( |
193 | ',', |
194 | $items |
195 | ) . "\n", |
196 | $apiRequestLog |
197 | ); |
198 | wfDebug( "Logged API request to $apiRequestLog" ); |
199 | } |
200 | } |
201 | } |