Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
CargoQueryPage
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 10
1482
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
156
 isExpensive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isSyndicated
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRecacheDB
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
110
 linkParameters
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 getOrderFields
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sortDescending
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 formatResult
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 outputResults
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3use Wikimedia\Rdbms\IDatabase;
4use Wikimedia\Rdbms\IResultWrapper;
5
6class CargoQueryPage extends QueryPage {
7    /** @var CargoSQLQuery */
8    private $sqlQuery;
9    /** @var array */
10    private $displayParams;
11    /** @var string */
12    private $format;
13
14    public function __construct( $name = 'CargoQuery' ) {
15        parent::__construct( $name );
16
17        $req = $this->getRequest();
18        $tablesStr = trim( $req->getVal( 'tables' ) );
19        $fieldsStr = trim( $req->getVal( 'fields' ) );
20        $whereStr = trim( $req->getVal( 'where' ) );
21        $joinOnStr = trim( $req->getVal( 'join_on' ) );
22        $groupByStr = trim( $req->getVal( 'group_by' ) );
23        if ( substr( $groupByStr, -1, 1 ) == ',' ) {
24            $groupByStr = substr( $groupByStr, 0, -1 ); // Remove last comma for group by
25        }
26        $havingStr = trim( $req->getVal( 'having' ) );
27
28        $orderByStr = "";
29        $orderByValues = $req->getArray( 'order_by' );
30        $orderByOptions = $req->getArray( 'order_by_options' );
31        if ( is_array( $orderByValues ) ) {
32            foreach ( $orderByValues as $i => $curOrderBy ) {
33                if ( $curOrderBy == '' ) {
34                    continue;
35                }
36                $orderByStr .= $curOrderBy;
37                if ( $orderByOptions != null ) {
38                    $orderByStr .= ' ' . $orderByOptions[$i];
39                }
40                $orderByStr .= ',';
41            }
42        }
43        if ( substr( $orderByStr, -1, 1 ) == ',' ) {
44            $orderByStr = substr( $orderByStr, 0, -1 ); // Remove last comma for order by
45        }
46        $limitStr = trim( $req->getVal( 'limit' ) );
47        $offsetStr = trim( $req->getVal( 'offset' ) );
48
49        $this->sqlQuery = CargoSQLQuery::newFromValues( $tablesStr, $fieldsStr, $whereStr, $joinOnStr,
50                $groupByStr, $havingStr, $orderByStr, $limitStr, $offsetStr );
51
52        $formatStr = trim( $req->getVal( 'format' ) );
53        $this->format = $formatStr;
54
55        // This is needed for both the results display and the
56        // navigation links.
57        $this->displayParams = [];
58        $queryStringValues = $this->getRequest()->getValues();
59        foreach ( $queryStringValues as $key => $value ) {
60            // For some reason, getValues() turns all spaces
61            // into underlines.
62            $paramName = str_replace( '_', ' ', $key );
63            if ( in_array( $paramName,
64                [ 'title', 'tables', 'fields', 'join on', 'order by', 'group by', 'having', 'format',
65                'offset' ] ) ) {
66                // Not display params.
67            } elseif ( in_array( $paramName, [ 'intro', 'outro', 'default' ] ) ) {
68                // These already get parsed/escaped during regular display, so no need to
69                // further escape them here (and they can't be arrays).
70                $this->displayParams[$paramName] = $value;
71            } else {
72                if ( is_array( $value ) ) {
73                    $this->displayParams[$paramName] = array_map( 'htmlspecialchars', $value );
74                } else {
75                    $this->displayParams[$paramName] = htmlspecialchars( $value );
76                }
77            }
78        }
79
80        // 'dynamic table' makes its own use of 'order by'.
81        if ( $this->format == 'dynamic table' ) {
82            $this->displayParams['order by'] = $orderByStr;
83        }
84    }
85
86    public function isExpensive() {
87        return false;
88    }
89
90    public function isSyndicated() {
91        return false;
92    }
93
94    // @todo - declare a getPageHeader() function, to show some
95    // information about the query?
96
97    /** @inheritDoc */
98    public function getRecacheDB() {
99        return CargoUtils::getDB();
100    }
101
102    public function getQueryInfo() {
103        $selectOptions = [];
104        if ( $this->sqlQuery->mGroupByStr != '' ) {
105            $selectOptions['GROUP BY'] = $this->sqlQuery->mGroupByStr;
106        }
107        if ( $this->sqlQuery->mHavingStr != '' ) {
108            $selectOptions['HAVING'] = $this->sqlQuery->mHavingStr;
109        }
110
111        // "order by" is handled elsewhere, in getOrderFields().
112
113        // Field aliases need to have quotes placed around them
114        // before running the query.
115        $cdb = CargoUtils::getDB();
116        $aliasedFieldNames = [];
117        foreach ( $this->sqlQuery->mAliasedFieldNames as $alias => $fieldName ) {
118            // If it's really a field name, add quotes around it.
119            if ( strpos( $fieldName, '(' ) === false && strpos( $fieldName, '.' ) === false &&
120                !$cdb->isQuotedIdentifier( $fieldName ) && !CargoUtils::isSQLStringLiteral( $fieldName ) ) {
121                $fieldName = $cdb->addIdentifierQuotes( $fieldName );
122            }
123            $aliasedFieldNames[$alias] = $fieldName;
124        }
125
126        $queryInfo = [
127            'tables' => $this->sqlQuery->mAliasedTableNames,
128            'fields' => $aliasedFieldNames,
129            'options' => $selectOptions
130        ];
131        if ( $this->sqlQuery->mWhereStr != '' ) {
132            $queryInfo['conds'] = $this->sqlQuery->mWhereStr;
133        }
134        if ( $this->sqlQuery->mJoinConds ) {
135            $queryInfo['join_conds'] = $this->sqlQuery->mJoinConds;
136        }
137        return $queryInfo;
138    }
139
140    /**
141     * Returns an associative array that will be encoded and added to the
142     * paging links
143     * @return array
144     */
145    public function linkParameters() {
146        $possibleParams = [
147            'tables', 'fields', 'where', 'join_on', 'order_by', 'group_by', 'having', 'format'
148        ];
149        $linkParams = [];
150        $req = $this->getRequest();
151        foreach ( $possibleParams as $possibleParam ) {
152            if ( $req->getCheck( $possibleParam ) ) {
153                $linkParams[$possibleParam] = $req->getVal( $possibleParam );
154            } elseif ( $req->getArray( $possibleParam ) ) {
155                $linkParams[$possibleParam] = $req->getArray( $possibleParam );
156            }
157        }
158
159        foreach ( $this->displayParams as $key => $value ) {
160            $linkParams[$key] = $value;
161        }
162
163        return $linkParams;
164    }
165
166    public function getOrderFields() {
167        return $this->sqlQuery->mOrderBy;
168    }
169
170    public function sortDescending() {
171        return false;
172    }
173
174    public function formatResult( $skin, $result ) {
175        // This function needs to be declared, but it is not called.
176    }
177
178    /**
179     * Format and output report results using the given information plus
180     * OutputPage
181     *
182     * @param OutputPage $out OutputPage to print to
183     * @param Skin $skin User skin to use
184     * @param IDatabase $dbr Database (read) connection to use
185     * @param IResultWrapper $res Result pointer
186     * @param int $num Number of available result rows
187     * @param int $offset Paging offset
188     */
189    public function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
190        $valuesTable = [];
191        for ( $i = 0; $i < $num && $row = $res->fetchObject(); $i++ ) {
192            $valuesTable[] = get_object_vars( $row );
193        }
194        $queryDisplayer = CargoQueryDisplayer::newFromSQLQuery( $this->sqlQuery );
195        $queryDisplayer->mFieldDescriptions = $this->sqlQuery->mFieldDescriptions;
196        $queryDisplayer->mFormat = $this->format;
197        $formatter = $queryDisplayer->getFormatter( $out );
198
199        if ( $formatter->isDeferred() ) {
200            $text = $formatter->queryAndDisplay( [ $this->sqlQuery ], $this->displayParams );
201            $out->addHTML( $text );
202            return;
203        }
204
205        $this->displayParams['offset'] = $offset;
206        $queryDisplayer->mDisplayParams = $this->displayParams;
207        $html = $queryDisplayer->displayQueryResults( $formatter, $valuesTable );
208        if ( $this->format === 'template' ) {
209            $out->addWikiTextAsContent( $html );
210        } else {
211            $out->addHTML( $html );
212        }
213    }
214}