kenken999/php
0
1<?php2class RunnerContextItem3{4/**5 * define('CONTEXT_GLOBAL', 0); // global context6 * define('CONTEXT_PAGE', 1); // page where pageObject is available7 * define('CONTEXT_BUTTON', 2); // button or other AJAX event8 * define('CONTEXT_LOOKUP', 3); // dependent lookup9 * define('CONTEXT_ROW', 4); // processing grid row on multiple-records page (list)10 * define('CONTEXT_COMMAND', 5); // DsCommand context11 * define('CONTEXT_SEARCH', 6); // Search object context12 * define('CONTEXT_MASTER', 7); // Search object context13 */14 public $type; 15 16 public $pageObj;17 public $data;18 public $oldData;19 public $newData;20 public $detailsKeys;21 public $dc;22 public $searchClause;23 public $masterData;24 25 function __construct( $type, $params )26 {27 RunnerApply($this, $params);28 $this->type = $type;29 }30 31 public function getType()32 {33 return $this->type;34 }35 36 /**37 * @return Array38 */39 public function getValues()40 {41 if( $this->data )42 return $this->data; 43 44 if( $this->dc ) {45 return $this->dc->values;46 }47 48 if( $this->pageObj )49 return $this->pageObj->getCurrentRecord();50 51 return array();52 }53 54 /**55 * @param String field56 * @return Mixed57 */58 public function getFieldValue( $field )59 {60 $data = $this->getValues();61 return getArrayElementNC( $data, $field );62 }63 64 public function getSearchValue( $field ) {65 return $this->searchClause->_getFieldValue( $field, null, false, true );66 }67 68 public function getAllSearchValue() {69 return $this->searchClause->getAllFieldsSearchValue();70 }71 72 /**73 * @return Array74 */75 public function getOldValues()76 {77 if( $this->oldData )78 return $this->oldData;79 80 if( $this->pageObj )81 return $this->pageObj->getOldRecordData();82 83 return array();84 }85 86 public function getKeyValue( $field ) {87 if( $this->dc ) {88 return $this->dc->keys[ $field ];89 }90 return null;91 }92 93 public function getOrderValue( $key ) {94 if( !$this->dc ) {95 return null;96 }97 $idx = 0;98 if( substr( $key, 0, 5) == "field" ) {99 $param = "field";100 $idx = (int)substr( $key, 5);101 } else if( substr( $key, 0, 3 ) == "dir" ) {102 $param = "dir";103 $idx = (int)substr( $key, 3);104 }105 if( !$idx || $idx < 1 ) {106 return null;107 }108 if( count( $this->dc->order ) <= $idx ) {109 return null;110 }111 $order = $this->dc->order[ $idx - 1 ];112 if( $param == "field") {113 return $order["column"];114 }115 if( $param == "dir") {116 return $order["dir"];117 }118 return null;119 }120 121 122 public function getFilterValue( $field ) {123 if( $this->dc ) {124 return $this->dc->findFieldFilterValue( $field );125 }126 }127 128 public function getLimitValue( $key ) {129 if( !$this->dc ) {130 return "";131 }132 if( $key == "record_from" ) {133 return $this->dc->startRecord + 1;134 }135 if( $key == "record_to" ) {136 return $this->dc->startRecord + $this->dc->reccount;137 }138 if( $key == "record_count" ) {139 return $this->dc->reccount;140 }141 if( $key == "record_offset" ) {142 return $this->dc->startRecord;143 }144 }145 146 147 /**148 * @param String field149 * @return Mixed150 */151 public function getOldFieldValue( $field )152 {153 $oldData = $this->getOldValues();154 return getArrayElementNC( $oldData, $field );155 }156 157 /**158 * @param String field159 * @return Mixed160 */161 public function getNewFieldValue( $field )162 {163 if ( $this->newData )164 return getArrayElementNC( $this->newData, $field );165 if( $this->dc ) {166 return getArrayElementNC( $this->dc->values, $field );167 }168 169 return $this->getFieldValue( $field );170 }171 172 173 174 /**175 * @return Array176 */177 public function getMasterValues()178 {179 if( $this->masterData )180 return $this->masterData;181 182 if( $this->pageObj )183 return $this->pageObj->getMasterRecord();184 185 return array();186 }187 188 /**189 * @param String field190 * @return Mixed191 */192 public function getMasterFieldValue( $field )193 {194 $masterData = $this->getMasterValues();195 if( !$masterData ) {196 return "";197 }198 return getArrayElementNC( $masterData, $field );199 200 }201 202 /**203 * @param String key204 * @return String205 */206 public function getUserValue( $key )207 {208 return getArrayElementNC( Security::currentUserData(), $key );209 }210 211 /**212 * @param String key213 * @return Mixed214 */215 public function getSessionValue( $key )216 {217 return getSessionElementNC( $key );218 }219 220 public function getDetailsKeyValue( $key ) {221 return $this->detailsKeys[ $key ];222 }223 224 225 /**226 * Returns true if context must serve this scope, and search must stop here. 227 * For example, when page context is found, it must serve the 'master' and 'details' scopes even if no master-detail is in effect.228 * @param String - scope like 'keys', 'master', 'details'229 * @return Boolean230 */231 public function hasScope( $scope ) {232 if( $scope == "master" )233 return $this->masterData || $this->type == CONTEXT_PAGE;234 235 if( $scope == "session" )236 return true;237 238 if( $scope == "user" )239 return true;240 241 if( $scope == "old" )242 return $this->oldData || $this->type == CONTEXT_PAGE;243 244 if( $scope == "keys" )245 return $this->type == CONTEXT_COMMAND;246 247 if( $scope == "order" )248 return $this->type == CONTEXT_COMMAND;249 250 251 if( $scope == "new" )252 return $this->newData || $this->type == CONTEXT_PAGE || $this->type == CONTEXT_COMMAND;253 254 if ( $scope == "global" )255 return true;256 257 if( $scope == "details" )258 return $this->type == CONTEXT_PAGE || $this->type == CONTEXT_MASTER;259 260 if ( $scope == "values" )261 return !!$this->data || $this->type == CONTEXT_PAGE || $this->type == CONTEXT_COMMAND; 262 263 if( $scope == "search" )264 return $this->type == CONTEXT_SEARCH;265 266 if( $scope == "filter" )267 return $this->type == CONTEXT_COMMAND;268 269 if( $scope == "request" )270 return true;271 272 if( $scope == "limit" )273 return $this->type == CONTEXT_COMMAND;274 275 if( $scope == "all_field_search" )276 return $this->type == CONTEXT_SEARCH;277 }278 279 /**280 * @param String key281 * @return Mixed282 */283 public function getContextValue( $scope, $key )284 {285 286 if( $scope == "master" )287 return $this->getMasterFieldValue( $key );288 289 if( $scope == "session" )290 return $this->getSessionValue( $key );291 292 if( $scope == "user" )293 return $this->getUserValue( $key );294 295 if( $scope == "old" )296 return $this->getOldFieldValue( $key );297 298 if( $scope == "keys" )299 return $this->getKeyValue( $key );300 301 if( $scope == "order" )302 return $this->getOrderValue( $key );303 304 if( $scope == "new" )305 return $this->getNewFieldValue( $key );306 307 if ( $scope == "global" && $key == "language" )308 return mlang_getcurrentlang();309 310 if( $scope == "details" ) {311 if( $this->type == CONTEXT_PAGE )312 return $this->pageObj->getDetailsKeyValue( $key );313 if( $this->type == CONTEXT_MASTER )314 return $this->getDetailsKeyValue( $key );315 }316 317 if( $scope == "values" )318 return $this->getFieldValue( $key );319 320 if( $scope == "search" )321 return $this->getSearchValue( $key );322 323 if( $scope == "filter" )324 return $this->getFilterValue( $key );325 326 if( $scope == "request" )327 return postvalue( $key );328 329 if( $scope == "limit" )330 return $this->getLimitValue( $key );331 332 if( $scope == "all_field_search" )333 return $this->getAllSearchValue();334 335 return false;336 }337}338 339/**340 * Singletone. All public functions are static341 */342class RunnerContext343{344 protected $stack = array();345 346 public function __construct( )347 {348 $context = new RunnerContextItem( CONTEXT_GLOBAL, array() );349 $this->stack[ count($this->stack) ] = $context;350 }351 352 public static function push( $context )353 {354 global $contextStack;355 $contextStack->stack[ count($contextStack->stack) ] = $context;356 }357 358 public static function current( )359 {360 global $contextStack;361 return $contextStack->stack[ count($contextStack->stack) - 1 ];362 }363 364 public static function pop( )365 {366 global $contextStack;367 368 // this sometimes happens during the error reporting369 if( !$contextStack->stack )370 return null;371 372 $context = $contextStack->stack[ count($contextStack->stack) - 1 ];373 unset( $contextStack->stack[ count($contextStack->stack) - 1 ] );374 375 return $context;376 }377 378 // Utility functions379 /**380 * Shortcut for adding page-based context381 */382 public static function pushPageContext( $pageObj ) {383 RunnerContext::push( new RunnerContextItem( CONTEXT_PAGE, array( "pageObj" => $pageObj ) ) );384 }385 /**386 * Shortcut for adding record-based context387 */388 public static function pushRecordContext( $record, $pageObj ) {389 RunnerContext::push( new RunnerContextItem( CONTEXT_ROW, array( "pageObj" => $pageObj, "data" => $record ) ) ); //?390 }391 392 public static function pushDataCommandContext( $dc ) {393 RunnerContext::push( new RunnerContextItem( CONTEXT_COMMAND, array( "dc" => $dc ) ) ); 394 }395 396 public static function pushSearchContext( $searchClause ) {397 RunnerContext::push( new RunnerContextItem( CONTEXT_SEARCH, array( "searchClause" => $searchClause ) ) );398 }399 400 public static function pushMasterContext( $detailsKeys ) {401 RunnerContext::push( new RunnerContextItem( 402 CONTEXT_MASTER, 403 array( 404 "detailsKeys" => $detailsKeys, 405 ) ) ); 406 }407 408 409 public static function getMasterValues() {410 $ctx = RunnerContext::current();411 return $ctx->getMasterValues();412 }413 414 public static function getValues() {415 $ctx = RunnerContext::current();416 return $ctx->getValues();417 }418 419 /**420 * @return String421 */422 public static function PrepareRest( $str, $urlenc = true ) {423 $context = RunnerContext::current();424 $tokens = DB::scanTokenString($str);425 426 $replacements = array();427 // build array of replacements in this format:428 // "offset" => position in the string where replacement should be done429 // "len" => length of original substring to cut out430 // "insert" => string to insert in place of cut out431 432 foreach ($tokens["matches"] as $i => $match) {433 $offset = $tokens["offsets"][$i];434 $token = $tokens["tokens"][$i];435 436 $repl = array(437 "offset" => $offset,438 "len" => strlen($match)439 );440 $val = "";441 if (is_numeric($token) && count( $args ) > $token) {442 $val = $args[(int)$token];443 } else {444 $val = RunnerContext::getValue($token);445 }446 if( $urlenc )447 $val = rawurlencode($val);448 $repl["insert"] = $val;449 450 $replacements[] = $repl;451 }452 // do replacements453 return RunnerContext::doReplacements( $str, $replacements );454 }455 456 /**457 * locate all <? - ?> snippets in a string458 * @param String str459 * @return Array of array(460 * "offset" => integer - offset of the opening bracket in the original string461 * "len" => integer - length includes opening and closing brackets: <? - ?>462 * )463 */464 protected static function getOptionalBlocks( $str ) {465 if( !is_string( $str ) )466 return array(); 467 468 $snippetStack = array();469 $snippets = array();470 $pos = strpos( $str, '<?' );471 if( $pos === false )472 return array();473 474 while( true ) {475 $snippetStack[] = $pos;476 $newPos = strpos( $str, '<?', $pos + 1 );477 478 /* locate all ?> before the next <? or end of string*/479 $tailLen = ( $newPos !== false ? $newPos : strlen( $str ) ) - $pos;480 $tail = substr( $str, $pos, $tailLen );481 $endPos = 0;482 while( ($endPos = strpos( $tail, '?>', $endPos+1 ) ) !== false && count( $snippetStack ) ) {483 $stackIdx = count( $snippetStack ) - 1;484 $snippets[] = array( "offset" => $snippetStack[ $stackIdx ], "len" => $endPos + $pos + 2 - $snippetStack[ $stackIdx ] );485 $snippetStack = array_slice( $snippetStack, 0, $stackIdx );486 }487 if( $newPos === false ) 488 break;489 $pos = $newPos;490 }491 return $snippets;492 }493 494 /**495 * do actual replacements496 * @param String $str - source string497 * @param Array $replacements - Array of Array(498 * "offset" => Integer, position in the source tring499 * "len" => Integer, length of the source string portion to be replaced500 * "insert" => String, value to insert instead of replaced501 * )502 */503 public static function doReplacements( $str, $replacements ) {504 if( !is_string( $str ) )505 return $str;506 507 $snippets = RunnerContext::getOptionalBlocks( $str );508 /* snippets with 0 non-empty requirements must be deleted from the string */509 /* mark empty snippets */510 for( $i=0; $i < count( $snippets ); ++$i ) {511 $s = &$snippets[$i];512 $s["empty"] = true;513 foreach ($replacements as $r) {514 /* replacement inside snippet */515 if( $r["offset"] > $s["offset"] && $r["offset"] < $s["offset"] + $s["len"] ) {516 if( $r["insert"] != "" ) {517 $s["empty"] = false;518 break;519 }520 }521 }522 } 523 524 /* do replacements */525 $offsetShift = 0;526 foreach ($replacements as $r) {527 $str = substr_replace($str, $r["insert"], $r["offset"] + $offsetShift, $r["len"]);528 $offsetDelta = strlen($r["insert"]) - $r["len"];529 // update all $snippets530 RunnerContext::updateOptionalBlockOffset( $snippets, $r["offset"], $offsetDelta );531 $offsetShift += $offsetDelta;532 }533 534 /* process optional blocks - delete or remove brackets */535 for( $i=0; $i < count( $snippets ); ++$i ) {536 $s = &$snippets[$i];537 if( $s["empty"]) {538 $str = substr_replace($str, "", $s["offset"], $s["len"] );539 $offsetDelta = -$s["len"];540 } else {541 $str = substr_replace($str, 542 substr( $str, $s["offset"] + 2, $s["len"] - 4 ), 543 $s["offset"], 544 $s["len"] 545 );546 $offsetDelta = -4;547 }548 RunnerContext::updateOptionalBlockOffset( $snippets, $s["offset"], $offsetDelta );549 }550 return $str;551 }552 553 protected static function updateOptionalBlockOffset( &$snippets, $offset, $delta ) {554 for( $i=0; $i < count( $snippets ); ++$i ) {555 $s = &$snippets[$i];556 if( $s["offset"] > $offset ) {557 $s["offset"] += $delta;558 } else {559 if( $s["offset"] + $s["len"] > $offset ) {560 $s["len"] += $delta;561 }562 }563 }564 }565 566 567 public static function getValue( $key ) {568 $prefix = "";569 $dotPos = strpos( $key, ".");570 if( $dotPos !== FALSE )571 {572 $scope = strtolower( substr( $key, 0, $dotPos ) );573 $key = substr( $key, $dotPos + 1 );574 } else {575 if( $key === "language" ) {576 $scope = "global";577 } else if( $key == "all_field_search") {578 $scope = $key;579 } else {580 $scope = "values";581 }582 583 }584 return RunnerContext::_getValue( $scope, $key );585 }586 587 588 /**589 * Search got requested value in the stack of contexts590 * @param String - scope like 'keys', 'master', 'details'591 * @param String - key. 'details.teamId' is translated to context=details, key=teamId592 * @return String or false if value is not found593 */594 protected static function _getValue( $scope, $key ) 595 {596 global $contextStack;597 $idx = count( $contextStack->stack );598 while( $idx > 0 ) {599 $ctx = $contextStack->stack[ --$idx ];600 if( $ctx->hasScope( $scope ) ) {601 return $ctx->getContextValue( $scope, $key );602 }603 }604 return false;605 }606 607}608 609/**610 * Push context in constructor and pop in destructor611 */612class TempContext613{614 function __construct( $context ) {615 RunnerContext::push( $context );616 }617 618 function __destruct() {619 RunnerContext::pop();620 }621}622 623?>