Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • None
  • system
    • base
    • caching
    • console
    • db
      • ar
      • schema
    • validators
    • web
      • actions
      • auth
      • helpers
      • widgets
        • captcha
        • pagers
  • zii
    • widgets
      • grid

Classes

  • ActionFormModel
  • ArrayUtil
  • ArrayValidator
  • AssociatedMediaBehavior
  • AuxLib
  • Changelog
  • DetailView
  • EncryptUtilTmp
  • EventsWidgetFieldFormatter
  • FailedLogins
  • FieldFormatter
  • FieldFormatterBase
  • FieldInputRenderer
  • FileFieldBehavior
  • FiltersForm
  • FilterUtil
  • FineDiff
  • FineDiffCopyOp
  • FineDiffDeleteOp
  • FineDiffInsertOp
  • FineDiffOp
  • FineDiffOps
  • FineDiffReplaceOp
  • GlobalCSSFormModel
  • GlobalImportFormModel
  • GoogleAuthenticator
  • JSONFieldsBehavior
  • JSONResponse
  • MediaFieldFormatter
  • MediaSelector
  • MobileActiveRecordFieldFormatter
  • MobileActivityFeed
  • MobileChartDashboard
  • MobileFieldFormatter
  • MobileFieldInputRenderer
  • ModuleModelNameValidator
  • MultiChildNode
  • MultiTypeAutocomplete
  • PasswordUtil
  • ProductFeature
  • ProfileWidgetLayout
  • QueryParamGenerator
  • RecordLimitBehavior
  • RecordView
  • RecordViewWidgetLayout
  • RelationshipsGridModel
  • RelationshipsJoin
  • RepairUserDataCommand
  • RequestUtil
  • RequiredIfNotSetValidator
  • ResponseUtil
  • RunMigrationScriptCommand
  • ServiceWebFormDesigner
  • Settings
  • StringUtil
  • TestEmailAction
  • TestEmailActionForm
  • ThemeGenerator
  • TimerUtil
  • TopicsFieldFormatter
  • TopicsWidgetLayout
  • TransactionalViewFieldFormatter
  • UrlUtil
  • ValidLinkValidator
  • WebFormDesigner
  • WebLeadFormDesigner
  • X2ActiveRecordBehavior
  • X2ActiveRecordFieldFormatter
  • X2ButtonColumn
  • X2ConditionList
  • X2ConsoleCommand
  • X2ControllerBehavior
  • X2DataColumn
  • X2DuplicateBehavior
  • X2Flashes
  • X2GridViewFieldFormatter
  • X2IPAddress
  • X2LeadsDataColumn
  • X2MergeableBehavior
  • X2MessageSource
  • X2MobileControllerBehavior
  • X2MobileProfileControllerBehavior
  • X2MobileQuotesControllerBehavior
  • X2MobileSiteControllerBehavior
  • X2MobileTopicsControllerBehavior
  • X2ModelConversionBehavior
  • X2ModelConversionWidget
  • X2ModelForeignKeyValidator
  • X2ModelUniqueIndexValidator
  • X2NonWebUser
  • X2StaticDropdown
  • X2StaticField
  • X2StaticFieldsBehavior
  • X2UrlManager
  • X2Validator
  • X2WidgetBehavior

Interfaces

  • AdminOwnedCredentials

Exceptions

  • CampaignMailingException
  • CodeExchangeException
  • GetCredentialsException
  • NoRefreshTokenException
  • NoUserIdException
  • StringUtilException

Functions

  • checkCurrency
  • checkDNS
  • checkServerVar
  • checkTimezone
  • decodeQuotes
  • echoIcons
  • encodeQuotes
  • exceptionForError
  • getField
  • getLanguageName
  • getModuleTitle
  • handleReqError
  • handleReqException
  • installer_t
  • installer_tr
  • isAllowedDir
  • mediaMigrationRrmdir
  • migrateMediaDir
  • printGraph
  • printR
  • renderFields
  • reqShutdown
  • RIP
  • translateOptions
  • tryGetRemote
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /*****************************************************************************************
  3:  * X2Engine Open Source Edition is a customer relationship management program developed by
  4:  * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
  5:  * 
  6:  * This program is free software; you can redistribute it and/or modify it under
  7:  * the terms of the GNU Affero General Public License version 3 as published by the
  8:  * Free Software Foundation with the addition of the following permission added
  9:  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 10:  * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
 11:  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 12:  * 
 13:  * This program is distributed in the hope that it will be useful, but WITHOUT
 14:  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 15:  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 16:  * details.
 17:  * 
 18:  * You should have received a copy of the GNU Affero General Public License along with
 19:  * this program; if not, see http://www.gnu.org/licenses or write to the Free
 20:  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 21:  * 02110-1301 USA.
 22:  * 
 23:  * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
 24:  * California 95067, USA. or at email address contact@x2engine.com.
 25:  * 
 26:  * The interactive user interfaces in modified source and object code versions
 27:  * of this program must display Appropriate Legal Notices, as required under
 28:  * Section 5 of the GNU Affero General Public License version 3.
 29:  * 
 30:  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 31:  * these Appropriate Legal Notices must retain the display of the "Powered by
 32:  * X2Engine" logo. If the display of the logo is not reasonably feasible for
 33:  * technical reasons, the Appropriate Legal Notices must display the words
 34:  * "Powered by X2Engine".
 35:  *****************************************************************************************/
 36: 
 37: /**
 38:  * Standalone environmentally-agnostic content/message feedback utility.
 39:  *
 40:  * In the scope of a web request, it will respond via JSON (i.e. for use in an
 41:  * API or AJAX response action). When run in a command line interface, it will
 42:  * echo messages without exiting.
 43:  *
 44:  * Setting elements of an object of this class (using the {@link ArrayAccess}
 45:  * implementation) will control the properties of the JSON that is returned when
 46:  * using it in a web request.
 47:  *
 48:  * @author Demitri Morgan <demitri@x2engine.com>
 49:  */
 50: class ResponseUtil implements ArrayAccess {
 51: 
 52:     /**
 53:      * Exit on non-fatal PHP errors.
 54:      *
 55:      * If set to true, the error handler {@link respondWithError} will force a
 56:      * premature response for any PHP error, even if it's not of type E_ERROR.
 57:      * 
 58:      * @var bool 
 59:      */
 60:     public static $exitNonFatal = false;
 61: 
 62:     /**
 63:      * The default HTTP status code to use when handling an internal error.
 64:      *
 65:      * This can be set to 200 when dealing with client-side code that cannot
 66:      * retrieve response data if the response code is not 200. This would
 67:      * thus allow user-friendly error reporting.
 68:      *
 69:      * @var integer
 70:      */
 71:     public static $errorCode = 500;
 72: 
 73:     /**
 74:      * Whether to include or ignore any unintentional output
 75:      *
 76:      * If false, any extra output generated within the scope of the response (i.e.
 77:      * error messages) will be excluded from the response altogether.
 78:      * @var boolean
 79:      */
 80:     public static $includeExtraneousOutput = false;
 81: 
 82:    /**
 83:      * Shutdown method.
 84:      *
 85:      * Can be set to, for instance, "Yii::app()->end();" for a graceful Yii
 86:      * shutdown that saves/rotates logs and performs other useful/appropriate
 87:      * operations before terminating the PHP thread.
 88:      *
 89:      * @var string|array|closure
 90:      */
 91:     public static $shutdown = 'die();';
 92: 
 93:     /**
 94:      * Produce extended error traces in responses triggered by error handlers.
 95:      * @var type
 96:      */
 97:     public static $longErrorTrace = false;
 98: 
 99:     /**
100:      * Override body.
101:      *
102:      * If left unset, the content type header will be set to JSON, and the
103:      * response body will be {@link _properties}, encoded in JSON. Otherwise,
104:      * any content type can be used, andthis property will be returned instead.
105:      * @var string
106:      */
107:     public $body;
108: 
109:     /**
110:      * HTTP header fields.
111:      *
112:      * The default of the "Content-type" field is JSON for ease of use, since
113:      * it's expected that this class will be used mostly to compose responses
114:      * in JSON format.
115:      * 
116:      * @var array
117:      */
118:     public $httpHeader = array(
119:         'Content-Type' => 'application/json'
120:     );
121: 
122:     /**
123:      * Specifies, if true, that a response is already in progress.
124:      *
125:      * This is used to avoid double-responding when using
126:      * {@link respondFatalErrorMessage} as a shutdown function for handling
127:      * fatal errors.
128:      * 
129:      * @var bool
130:      */
131:     private static $_responding = false;
132: 
133:     /**
134:      * Response singleton (there can only be one response at a time)
135:      * @var ResponseUtil
136:      */
137:     private static $_response = null;
138: 
139:     private static $_statusMessages = array(
140:         100 => 'Continue',
141:         101 => 'Switching Protocols',
142:         200 => 'OK',
143:         201 => 'Created',
144:         202 => 'Accepted',
145:         203 => 'Non-Authoritative Information',
146:         204 => 'No content',
147:         205 => 'Reset Content',
148:         206 => 'Partial Content',
149:         300 => 'Multiple Choices',
150:         301 => 'Moved Permanently',
151:         302 => 'Found',
152:         303 => 'See Other',
153:         304 => 'Not Modified',
154:         305 => 'Use Proxy',
155:         307 => 'Temporary Redirect',
156:         308 => 'Permanent Redirect',
157:         400 => 'Bad Request',
158:         401 => 'Unauthorized',
159:         402 => 'Payment Required',
160:         403 => 'Forbidden',
161:         404 => 'Not Found',
162:         405 => 'Method Not Allowed',
163:         406 => 'Not Acceptable',
164:         407 => 'Proxy Authentication Required',
165:         408 => 'Request Timeout',
166:         409 => 'Conflict',
167:         410 => 'Gone',
168:         411 => 'Length Required',
169:         412 => 'Precondition Failed',
170:         413 => 'Request Entity Too Large',
171:         414 => 'Request-URI Too Long',
172:         415 => 'Unsupported Media Type', // Incorrect content type in request
173:         416 => 'Requested Range Not Satisfiable',
174:         417 => 'Expectation Failed',
175:         418 => 'I\'m a teapot',
176:         422 => 'Unprocessable Entity', // Validation errors
177:         423 => 'Locked',
178:         424 => 'Failed Dependency',
179:         425 => 'Unordered Collection',
180:         426 => 'Upgrade Required',
181:         429 => 'Too Many Requests',
182:         431 => 'Request Header Fields Too Large',
183:         444 => 'No Response',
184:         494 => 'Request Header Too Large',
185:         495 => 'Cert Error',
186:         497 => 'HTTP to HTTPS',
187:         499 => 'Client Closed Request',
188:         500 => 'Internal Server Error',
189:         501 => 'Not Implemented',
190:         503 => 'Service Unavailable',
191:         504 => 'Gateway Timeout',
192:         505 => 'HTTP Version Not Supported',
193:         506 => 'Variant Also Negotiates',
194:         507 => 'Insufficient Storage',
195:         508 => 'Loop Detected',
196:         509 => 'Bandwidth Limit Exceeded',
197:         510 => 'Not Extended',
198:         511 => 'Network Authentication Required'
199:     );
200:     
201:     /**
202:      * Properties of the response.
203:      *
204:      * In the case of responding to a web request, the response will be this
205:      * array encoded in JSON. When setting "indexes" of this class using the
206:      * array access method, this is the array where the values are stored.
207:      * @var array
208:      */
209:     private $_properties = array();
210:     
211:     /**
212:      * HTTP status code when applicable.
213:      *
214:      * The default is 200, meaning no error.
215:      *
216:      * @var type
217:      */
218:     private $_status = 200;
219: 
220:     /**
221:      * Performs the shutdown code.
222:      */
223:     public static function end() {
224:         switch(gettype(self::$shutdown)) {
225:             case 'string':
226:                 // Interpret as a snippet of PHP code
227:                 eval(self::$shutdown);
228:                 break;
229:             case 'closure':
230:             case 'object':
231:                 // Interpret as a function
232:                 $shutdown = self::$shutdown;
233:                 $shutdown();
234:                 break;
235:             default:
236:                 die();
237:         }
238:     }
239: 
240:     /**
241:      * Returns the current response singleton object.
242:      */
243:     public static function getObject() {
244:         if(self::$_response instanceof ResponseUtil) {
245:             return self::$_response;
246:         } else {
247:             return false;
248:         }
249:     }
250: 
251:     /**
252:      * Returns the static array of status messages, i.e. for reference
253:      * @return array
254:      */
255:     public static function getStatusMessages() {
256:         return self::$_statusMessages;
257:     }
258: 
259:     /**
260:      * Returns true or false based on whether or not the current thread of PHP
261:      * is being run from the command line.
262:      * @return bool
263:      */
264:     public static function isCli(){
265:         return (empty($_SERVER['SERVER_NAME']) || php_sapi_name()==='cli');
266:     }
267:     /**
268:      * Universal, web-agnostic response function.
269:      *
270:      * Responds with a JSON and closes the connection if used in a web request;
271:      * merely echoes the response message (but optionally exits) otherwise.
272:      *
273:      * @param type $message The message to respond with.
274:      * @param bool $error Indicates that an error has occurred
275:      * @param bool $fatal Shut down PHP thread after printing the message
276:      * @param string $shutdown Optional shutdown expression to be evaluated; it must halt the current PHP process.
277:      */
278:     public static function respond($message, $error = false, $fatal = false){
279:         if(self::isCli()){ // Command line interface message
280:             self::$_responding = true;
281:             echo trim($message)."\n";
282:             if($error && $fatal)
283:                 self::end();
284:         } else { // One-off JSON response to HTTP client
285:             if(!isset(self::$_response)) {
286:                 self::$_response = new ResponseUtil(array());
287:             }
288:             // Default error code if there's an error; default/previously-set
289:             // response code otherwise.
290:             self::$_response->sendHttp($error?self::$errorCode:null,$message,$error);
291:         }
292:     }
293: 
294:     /**
295:      * Error handler method that uses the web-agnostic response method.
296:      *
297:      * This is disabled by default; fatal errors should ordinarily be caught by
298:      * {@link respondFatalErrorMessage()}. This can be enabled for debugging
299:      * purposes via setting {@link exitNonFatal} to true.
300:      *
301:      * @param type $no
302:      * @param type $st
303:      * @param type $fi
304:      * @param type $ln
305:      */
306:     public static function respondWithError($no, $st, $fi = Null, $ln = Null){
307:         if(self::$exitNonFatal){
308:             $message = "Error [$no]: $st $fi L$ln";
309:             if(self::$longErrorTrace){
310:                 ob_start();
311:                 debug_print_backtrace();
312:                 $message .= "; Trace:\n".ob_get_contents();
313:                 ob_end_clean();
314:             }
315:             self::respond($message, true);
316:         }
317:     }
318: 
319:     /**
320:      * Shutdown function for handling fatal errors not caught by
321:      * {@link respondWithError()}.
322:      */
323:     public static function respondFatalErrorMessage(){
324:         $error = error_get_last();
325:         if($error != null && !self::$_responding){
326:             $errno = $error["type"];
327:             $errfile = $error["file"];
328:             $errline = $error["line"];
329:             $errstr = $error["message"];
330:             self::respond("PHP ".($errno == E_PARSE ? 'parse' : 'fatal')." error [$errno]: $errstr in $errfile L$errline",true);
331:         }
332:     }
333: 
334:     /**
335:      * @param Exception $e The uncaught exception
336:      */
337:     public static function respondWithException($e){
338:         $message = 'Exception: "'.$e->getMessage().'" in '.$e->getFile().' L'.$e->getLine()."\n";
339:         if(self::$longErrorTrace){
340:             $message .= "; Trace:\n";
341:             foreach($e->getTrace() as $stackLevel){
342:                 if(!empty($stackLevel['file']) && !empty($stackLevel['line'])){
343:                     $message .= $stackLevel['file'].' L'.$stackLevel['line'].' ';
344:                 }
345:                 if(!empty($stackLevel['class'])){
346:                     $message .= $stackLevel['class'];
347:                     $message .= '->';
348:                 }
349:                 if(!empty($stackLevel['function'])){
350:                     $message .= $stackLevel['function'];
351:                     $message .= "();";
352:                 }
353:                 $message .= "\n";
354:             }
355:         }
356:         self::respond($message, true);
357:     }
358: 
359:     /**
360:      * Obtain an appropriate message for a given HTTP status code.
361:      *
362:      * @param integer $status
363:      * @return string
364:      */
365:     public static function statusMessage($code){
366:         $codes = self::$_statusMessages;
367:         return isset($codes[$code]) ? $codes[$code] : '';
368:     }
369: 
370:     //////////////////////
371:     // Instance Methods //
372:     //////////////////////
373: 
374:     /**
375:      * Constructor.
376:      * If one tries to instantiate two {@link ResponseUtil} objects, an
377:      * exception will be thrown. The idea is that there should only ever be one
378:      * response happening at a time.
379:      *
380:      * @param array $properties Initial response properties
381:      */
382:     public function __construct(){
383:         if(self::$_response instanceof ResponseUtil){
384:             throw new Exception('A response has already been declared.');
385:         }
386:         self::$_response = $this;
387:         if(!self::isCli()){
388:             // Collect any extraneous output so that it doesn't get sent before
389:             // the intended HTTP header gets sent:
390:             ob_start();
391:         }
392:     }
393: 
394: 
395:     /////////////////////////////
396:     // Array Interface Methods //
397:     /////////////////////////////
398: 
399:     /**
400:      * Array interface method from {@link ArrayAccess}
401:      * @param type $offset
402:      * @return type
403:      */
404:     public function offsetExists($offset){
405:         return array_key_exists($offset, $this->_properties);
406:     }
407: 
408:     /**
409:      * Array interface method from {@link ArrayAccess}
410:      * @param type $offset
411:      * @return type
412:      */
413:     public function offsetGet($offset){
414:         return $this->_properties[$offset];
415:     }
416: 
417:     /**
418:      * Array interface method from {@link ArrayAccess}
419:      * @param type $offset
420:      * @param type $value
421:      */
422:     public function offsetSet($offset, $value){
423:         $this->_properties[$offset] = $value;
424:     }
425: 
426:     /**
427:      * Array interface method from {@link ArrayAccess}
428:      * @param type $offset
429:      */
430:     public function offsetUnset($offset){
431:         unset($this->_properties[$offset]);
432:     }
433:     
434:     /**
435:      * Sends a HTTP response back to the client.
436:      *
437:      * @param integer $status The status code to use
438:      * @param type $message
439:      * @param type $error
440:      * @throws Exception
441:      */
442:     public function sendHttp($status=null, $message = '', $error = null){
443:         self::$_responding = true;
444:         // Close the output buffer; it's now safe to do so, since the header
445:         // will soon be sent.
446:         $output = ob_get_clean();
447:         ob_end_clean();
448:         $extraOutput = self::$includeExtraneousOutput && !empty($output);
449:         $status = $status === null ? ((bool)$error ? self::$errorCode : 200) : $status;
450: 
451:         // Set the response content
452:         if($status !== null && !array_key_exists((integer) $status,self::$_statusMessages)){
453:             // Invalid call to this method. Fail noisily.
454:             $this->_status = self::$errorCode;
455:             $body = '{"error":true,"message":"Internal server error: invalid or '
456:                     . 'non-numeric HTTP response status code specifed.","status":500}';
457:         } else if(!extension_loaded('json') || isset($this->body)) {
458:             // We might be doing something other than responding in JSON
459:             if(!isset($this->body)){
460:                 if(strpos($this->httpHeader['Content-Type'],'application/json')===0){
461:                     // JSON-format responding in use but not available
462:                     $this->_status = self::$errorCode;
463:                     $body = '{"error":true,"message":"The JSON PHP extension is required,'
464:                             . ' but this server lacks it.","status":'.$this->_status.'}';
465:                 } else {
466:                     // Simply echo the message if JSON isn't available.
467:                     $this->_status = $status;
468:                     $body = ($extraOutput?($output.' '):'').$message;
469:                 }
470:             } else {
471:                 // The "body" property is in use, which overrides the standard
472:                 // way of responding with JSON-encoded properties
473:                 $this->_status = $status;
474:                 $body = ($extraOutput?($output.' '):'').$this->body;
475:             }
476:         } else {
477:             if($status != null) {
478:                 // Override status. Loose comparison is in use because zero is
479:                 // an invalid HTTP response code and expected only of certain
480:                 // cURL libraries when the connection could not be established.
481:                 $this->_status = $status;
482:             }
483:             $response = $this->_properties;
484:             
485:             // Set universal response properties:
486:             if(empty($message) && !empty($response['message']))
487:                 $message = $response['message'];
488:             $response['message'] = $message.($extraOutput
489:                     ? " Note, extraneous output was generated in the scope of this response: $output"
490:                     : '');
491:             $response['error'] = $error === null
492:                     ? $this->_status >= 400
493:                     : (bool) $error;
494:             // Include the status code in the envelope for clients that can't
495:             // read HTTP headers:
496:             $response['status'] = $this->_status;
497:             // Compose the body of the response as a JSON-encoded object:
498:             $body = json_encode($response);
499:         }
500: 
501:         // Send the response
502:         $this->sendHttpHeader();
503:         echo $body;
504: 
505:         // Shut down
506:         self::$_response = null;
507:         self::end();
508:     }
509: 
510:     /**
511:      * Sends HTTP headers. This method should be called before any content is sent.
512:      * 
513:      * @param bool $replace The argument sent to header as the replacement flag.
514:      */
515:     protected function sendHttpHeader($replace = true){
516:         header(sprintf("HTTP/1.1 %d %s", $this->_status, self::statusMessage($this->_status)), $replace, $this->_status);
517:         foreach($this->httpHeader as $field => $value){
518:             header("$field: $value", $replace, $this->_status);
519:         }
520:     }
521: 
522:     /**
523:      * Sets {@link _properties}
524:      * @param array $properties
525:      */
526:     public function setProperties(array $properties) {
527:         $this->_properties = $properties;
528:     }
529: }
530: 
X2CRM Documentation API documentation generated by ApiGen 2.8.0