/includes/Status.php

https://github.com/daevid/MWFork · PHP · 358 lines · 188 code · 26 blank · 144 comment · 25 complexity · 083f396e6d23e9e95458bb5360394a6d MD5 · raw file

  1. <?php
  2. /**
  3. * Generic operation result class
  4. * Has warning/error list, boolean status and arbitrary value
  5. *
  6. * "Good" means the operation was completed with no warnings or errors.
  7. *
  8. * "OK" means the operation was partially or wholly completed.
  9. *
  10. * An operation which is not OK should have errors so that the user can be
  11. * informed as to what went wrong. Calling the fatal() function sets an error
  12. * message and simultaneously switches off the OK flag.
  13. */
  14. class Status {
  15. var $ok = true;
  16. var $value;
  17. /** Counters for batch operations */
  18. public $successCount = 0, $failCount = 0;
  19. /** Array to indicate which items of the batch operations were successful */
  20. public $success = array();
  21. /*semi-private*/ var $errors = array();
  22. /*semi-private*/ var $cleanCallback = false;
  23. /**
  24. * Factory function for fatal errors
  25. *
  26. * @param $message String: message name
  27. */
  28. static function newFatal( $message /*, parameters...*/ ) {
  29. $params = func_get_args();
  30. $result = new self;
  31. call_user_func_array( array( &$result, 'error' ), $params );
  32. $result->ok = false;
  33. return $result;
  34. }
  35. /**
  36. * Factory function for good results
  37. *
  38. * @param $value Mixed
  39. */
  40. static function newGood( $value = null ) {
  41. $result = new self;
  42. $result->value = $value;
  43. return $result;
  44. }
  45. /**
  46. * Change operation result
  47. *
  48. * @param $ok Boolean: whether to operation completed
  49. * @param $value Mixed
  50. */
  51. function setResult( $ok, $value = null ) {
  52. $this->ok = $ok;
  53. $this->value = $value;
  54. }
  55. /**
  56. * Returns whether the operation completed and didn't have any error or
  57. * warnings
  58. *
  59. * @return Boolean
  60. */
  61. function isGood() {
  62. return $this->ok && !$this->errors;
  63. }
  64. /**
  65. * Returns whether the operation completed
  66. *
  67. * @return Boolean
  68. */
  69. function isOK() {
  70. return $this->ok;
  71. }
  72. /**
  73. * Add a new warning
  74. *
  75. * @param $message String: message name
  76. */
  77. function warning( $message /*, parameters... */ ) {
  78. $params = array_slice( func_get_args(), 1 );
  79. $this->errors[] = array(
  80. 'type' => 'warning',
  81. 'message' => $message,
  82. 'params' => $params );
  83. }
  84. /**
  85. * Add an error, do not set fatal flag
  86. * This can be used for non-fatal errors
  87. *
  88. * @param $message String: message name
  89. */
  90. function error( $message /*, parameters... */ ) {
  91. $params = array_slice( func_get_args(), 1 );
  92. $this->errors[] = array(
  93. 'type' => 'error',
  94. 'message' => $message,
  95. 'params' => $params );
  96. }
  97. /**
  98. * Add an error and set OK to false, indicating that the operation
  99. * as a whole was fatal
  100. *
  101. * @param $message String: message name
  102. */
  103. function fatal( $message /*, parameters... */ ) {
  104. $params = array_slice( func_get_args(), 1 );
  105. $this->errors[] = array(
  106. 'type' => 'error',
  107. 'message' => $message,
  108. 'params' => $params );
  109. $this->ok = false;
  110. }
  111. /**
  112. * Sanitize the callback parameter on wakeup, to avoid arbitrary execution.
  113. */
  114. function __wakeup() {
  115. $this->cleanCallback = false;
  116. }
  117. /**
  118. * @param $params array
  119. * @return array
  120. */
  121. protected function cleanParams( $params ) {
  122. if ( !$this->cleanCallback ) {
  123. return $params;
  124. }
  125. $cleanParams = array();
  126. foreach ( $params as $i => $param ) {
  127. $cleanParams[$i] = call_user_func( $this->cleanCallback, $param );
  128. }
  129. return $cleanParams;
  130. }
  131. /**
  132. * @param $item
  133. * @return string
  134. */
  135. protected function getItemXML( $item ) {
  136. $params = $this->cleanParams( $item['params'] );
  137. $xml = "<{$item['type']}>\n" .
  138. Xml::element( 'message', null, $item['message'] ) . "\n" .
  139. Xml::element( 'text', null, wfMsg( $item['message'], $params ) ) ."\n";
  140. foreach ( $params as $param ) {
  141. $xml .= Xml::element( 'param', null, $param );
  142. }
  143. $xml .= "</{$item['type']}>\n";
  144. return $xml;
  145. }
  146. /**
  147. * Get the error list as XML
  148. * @return string
  149. */
  150. function getXML() {
  151. $xml = "<errors>\n";
  152. foreach ( $this->errors as $error ) {
  153. $xml .= $this->getItemXML( $error );
  154. }
  155. $xml .= "</errors>\n";
  156. return $xml;
  157. }
  158. /**
  159. * Get the error list as a wikitext formatted list
  160. *
  161. * @param $shortContext String: a short enclosing context message name, to
  162. * be used when there is a single error
  163. * @param $longContext String: a long enclosing context message name, for a list
  164. * @return String
  165. */
  166. function getWikiText( $shortContext = false, $longContext = false ) {
  167. if ( count( $this->errors ) == 0 ) {
  168. if ( $this->ok ) {
  169. $this->fatal( 'internalerror_info',
  170. __METHOD__." called for a good result, this is incorrect\n" );
  171. } else {
  172. $this->fatal( 'internalerror_info',
  173. __METHOD__.": Invalid result object: no error text but not OK\n" );
  174. }
  175. }
  176. if ( count( $this->errors ) == 1 ) {
  177. $s = $this->getWikiTextForError( $this->errors[0], $this->errors[0] );
  178. if ( $shortContext ) {
  179. $s = wfMsgNoTrans( $shortContext, $s );
  180. } elseif ( $longContext ) {
  181. $s = wfMsgNoTrans( $longContext, "* $s\n" );
  182. }
  183. } else {
  184. $s = '* '. implode("\n* ",
  185. $this->getWikiTextArray( $this->errors ) ) . "\n";
  186. if ( $longContext ) {
  187. $s = wfMsgNoTrans( $longContext, $s );
  188. } elseif ( $shortContext ) {
  189. $s = wfMsgNoTrans( $shortContext, "\n$s\n" );
  190. }
  191. }
  192. return $s;
  193. }
  194. /**
  195. * Return the wiki text for a single error.
  196. * @param $error Mixed With an array & two values keyed by
  197. * 'message' and 'params', use those keys-value pairs.
  198. * Otherwise, if its an array, just use the first value as the
  199. * message and the remaining items as the params.
  200. *
  201. * @return String
  202. */
  203. protected function getWikiTextForError( $error ) {
  204. if ( is_array( $error ) ) {
  205. if ( isset( $error['message'] ) && isset( $error['params'] ) ) {
  206. return wfMsgNoTrans( $error['message'],
  207. array_map( 'wfEscapeWikiText', $this->cleanParams( $error['params'] ) ) );
  208. } else {
  209. $message = array_shift($error);
  210. return wfMsgNoTrans( $message,
  211. array_map( 'wfEscapeWikiText', $this->cleanParams( $error ) ) );
  212. }
  213. } else {
  214. return wfMsgNoTrans( $error );
  215. }
  216. }
  217. /**
  218. * Return an array with the wikitext for each item in the array.
  219. * @param $errors Array
  220. * @return Array
  221. */
  222. function getWikiTextArray( $errors ) {
  223. return array_map( array( $this, 'getWikiTextForError' ), $errors );
  224. }
  225. /**
  226. * Merge another status object into this one
  227. *
  228. * @param $other Status Other Status object
  229. * @param $overwriteValue Boolean: whether to override the "value" member
  230. */
  231. function merge( $other, $overwriteValue = false ) {
  232. $this->errors = array_merge( $this->errors, $other->errors );
  233. $this->ok = $this->ok && $other->ok;
  234. if ( $overwriteValue ) {
  235. $this->value = $other->value;
  236. }
  237. $this->successCount += $other->successCount;
  238. $this->failCount += $other->failCount;
  239. }
  240. /**
  241. * Get the list of errors (but not warnings)
  242. *
  243. * @return Array
  244. */
  245. function getErrorsArray() {
  246. return $this->getStatusArray( "error" );
  247. }
  248. /**
  249. * Get the list of warnings (but not errors)
  250. *
  251. * @return Array
  252. */
  253. function getWarningsArray() {
  254. return $this->getStatusArray( "warning" );
  255. }
  256. /**
  257. * Returns a list of status messages of the given type
  258. * @param $type String
  259. *
  260. * @return Array
  261. */
  262. protected function getStatusArray( $type ) {
  263. $result = array();
  264. foreach ( $this->errors as $error ) {
  265. if ( $error['type'] === $type ) {
  266. if( $error['params'] ) {
  267. $result[] = array_merge( array( $error['message'] ), $error['params'] );
  268. } else {
  269. $result[] = array( $error['message'] );
  270. }
  271. }
  272. }
  273. return $result;
  274. }
  275. /**
  276. * Returns a list of status messages of the given type, with message and
  277. * params left untouched, like a sane version of getStatusArray
  278. *
  279. * @param $type String
  280. *
  281. * @return Array
  282. */
  283. public function getErrorsByType( $type ) {
  284. $result = array();
  285. foreach ( $this->errors as $error ) {
  286. if ( $error['type'] === $type ) {
  287. $result[] = $error;
  288. }
  289. }
  290. return $result;
  291. }
  292. /**
  293. * Returns true if the specified message is present as a warning or error
  294. *
  295. * @param $msg String: message name
  296. * @return Boolean
  297. */
  298. function hasMessage( $msg ) {
  299. foreach ( $this->errors as $error ) {
  300. if ( $error['message'] === $msg ) {
  301. return true;
  302. }
  303. }
  304. return false;
  305. }
  306. /**
  307. * If the specified source message exists, replace it with the specified
  308. * destination message, but keep the same parameters as in the original error.
  309. *
  310. * Return true if the replacement was done, false otherwise.
  311. *
  312. * @return bool
  313. */
  314. function replaceMessage( $source, $dest ) {
  315. $replaced = false;
  316. foreach ( $this->errors as $index => $error ) {
  317. if ( $error['message'] === $source ) {
  318. $this->errors[$index]['message'] = $dest;
  319. $replaced = true;
  320. }
  321. }
  322. return $replaced;
  323. }
  324. /**
  325. * Backward compatibility function for WikiError -> Status migration
  326. *
  327. * @return String
  328. */
  329. public function getMessage() {
  330. return $this->getWikiText();
  331. }
  332. }