Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
51.72% covered (warning)
51.72%
15 / 29
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiUsageException
53.57% covered (warning)
53.57%
15 / 28
28.57% covered (danger)
28.57%
2 / 7
26.41
0.00% covered (danger)
0.00%
0 / 1
 __construct
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
 newWithMessage
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getApiMessage
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 getModulePath
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getStatusValue
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMessageObject
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Api;
8
9use InvalidArgumentException;
10use MediaWiki\Exception\ILocalizedException;
11use MediaWiki\Exception\MWException;
12use MediaWiki\Status\Status;
13use StatusValue;
14use Stringable;
15use Throwable;
16use Wikimedia\Message\MessageSpecifier;
17
18/**
19 * Exception used to abort API execution with an error
20 *
21 * If possible, use ApiBase::dieWithError() instead of throwing this directly.
22 *
23 * @newable
24 * @ingroup API
25 */
26class ApiUsageException extends MWException implements Stringable, ILocalizedException {
27
28    /** @var string|null */
29    protected $modulePath;
30    /** @var StatusValue */
31    protected $status;
32
33    /**
34     * @stable to call
35     * @param ApiBase|null $module API module responsible for the error, if known
36     * @param StatusValue $status Status holding errors
37     * @param int $httpCode HTTP error code to use
38     * @param Throwable|null $previous Previous exception
39     */
40    public function __construct(
41        ?ApiBase $module, StatusValue $status, $httpCode = 0, ?Throwable $previous = null
42    ) {
43        if ( $status->isOK() ) {
44            throw new InvalidArgumentException( __METHOD__ . ' requires a fatal Status' );
45        }
46
47        $this->modulePath = $module ? $module->getModulePath() : null;
48        $this->status = $status;
49
50        // Bug T46111: Messages in the log files should be in English and not
51        // customized by the local wiki.
52        $enMsg = clone $this->getApiMessage();
53        $enMsg->inLanguage( 'en' )->useDatabase( false );
54        parent::__construct( ApiErrorFormatter::stripMarkup( $enMsg->text() ), $httpCode, $previous );
55    }
56
57    /**
58     * @param ApiBase|null $module API module responsible for the error, if known
59     * @param string|array|MessageSpecifier $msg See ApiMessage::create()
60     * @param string|null $code See ApiMessage::create()
61     * @param array|null $data See ApiMessage::create()
62     * @param int $httpCode HTTP error code to use
63     * @param Throwable|null $previous Previous exception
64     */
65    public static function newWithMessage(
66        ?ApiBase $module, $msg, $code = null, $data = null, $httpCode = 0, ?Throwable $previous = null
67    ): static {
68        return new static(
69            $module,
70            StatusValue::newFatal( ApiMessage::create( $msg, $code, $data ) ),
71            $httpCode,
72            $previous
73        );
74    }
75
76    /**
77     * @return ApiMessage
78     */
79    private function getApiMessage() {
80        // Return the first error message, if any; or the first warning message, if any; or a generic message
81        foreach ( $this->status->getMessages( 'error' ) as $msg ) {
82            // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
83            return ApiMessage::create( $msg );
84        }
85        foreach ( $this->status->getMessages( 'warning' ) as $msg ) {
86            // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
87            return ApiMessage::create( $msg );
88        }
89        return new ApiMessage( 'apierror-unknownerror-nocode', 'unknownerror' );
90    }
91
92    /**
93     * Fetch the responsible module name
94     * @return string|null
95     */
96    public function getModulePath() {
97        return $this->modulePath;
98    }
99
100    /**
101     * Fetch the error status
102     * @return StatusValue
103     */
104    public function getStatusValue() {
105        return $this->status;
106    }
107
108    /**
109     * @inheritDoc
110     */
111    public function getMessageObject() {
112        return Status::wrap( $this->status )->getMessage();
113    }
114
115    /**
116     * @return string
117     */
118    public function __toString() {
119        $enMsg = clone $this->getApiMessage();
120        $enMsg->inLanguage( 'en' )->useDatabase( false );
121        $text = ApiErrorFormatter::stripMarkup( $enMsg->text() );
122
123        return get_class( $this ) . "{$enMsg->getApiCode()}{$text} "
124            . "in {$this->getFile()}:{$this->getLine()}\n"
125            . "Stack trace:\n{$this->getTraceAsString()}"
126            . ( $this->getPrevious() ? "\n\nNext {$this->getPrevious()}" : "" );
127    }
128
129}
130
131/** @deprecated class alias since 1.43 */
132class_alias( ApiUsageException::class, 'ApiUsageException' );