vqmod.php (27522B)
1 <?php 2 /** 3 * VQMod 4 * @description Main Object used 5 */ 6 abstract class VQMod { 7 public static $_vqversion = '2.6.4'; // Current version number 8 9 private static $_modFileList = array(); // Array of xml files 10 private static $_mods = array(); // Array of modifications to apply 11 private static $_filesModded = array(); // Array of already modified files 12 private static $_doNotMod = array(); // Array of files not to apply modifications to 13 private static $_cwd = ''; // Current working directory path 14 private static $_folderChecks = false; // Flag for already checked log/cache folders exist 15 private static $_cachePathFull = ''; // Full cache folder path 16 private static $_lastModifiedTime = 0; // Integer representing the last time anything was modified 17 private static $_devMode = false; // Flag for developer mode - disables caching while true 18 19 public static $logFolder = 'vqmod/logs/'; // Path log folders are stored in 20 public static $vqCachePath = 'vqmod/vqcache/'; // Relative path to cache file directory 21 public static $modCache = 'vqmod/mods.cache'; // Relative path to serialized mods array cache file 22 public static $checkedCache = 'vqmod/checked.cache'; // Relative path to already checked files array cache file 23 public static $protectedFilelist = 'vqmod/vqprotect.txt'; // Relative path to protected files array cache file 24 public static $pathReplaces = 'vqmod/pathReplaces.php'; // Relative path to dynamic path replacement file 25 public static $logging = true; // Flag to enabled/disable logging 26 public static $log; // Log object reference 27 public static $fileModding = false; // Reference to the current file being modified by vQmod for logging 28 public static $directorySeparator = ''; // System directory separator (/ or \ depending on OS) 29 public static $replaces = array(); // Array of regex replaces to perform on file paths 30 public static $windows = false; // Flag determining if windows or *nix based 31 32 /** 33 * VQMod::bootup() 34 * 35 * @param bool $path File path to use 36 * @param bool $logging Enable/disabled logging 37 * @return null 38 * @description Startup of VQMod 39 */ 40 public static function bootup($path = false, $logging = true) { 41 if(!class_exists('DOMDocument')) { 42 die('VQMod::bootup - ERROR - YOU NEED THE PHP "DOMDocument" EXTENSION INSTALLED TO USE VQMod'); 43 } 44 45 if(strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { 46 self::$windows = true; 47 } 48 49 self::$directorySeparator = defined('DIRECTORY_SEPARATOR') ? DIRECTORY_SEPARATOR : '/'; 50 51 if(!$path){ 52 $path = dirname(dirname(__FILE__)); 53 } 54 self::_setCwd($path); 55 56 self::$logging = (bool) $logging; 57 self::$log = new VQModLog(); 58 59 $replacesPath = self::path(self::$pathReplaces); 60 $replaces = array(); 61 if($replacesPath) { 62 include_once($replacesPath); 63 self::$_lastModifiedTime = filemtime($replacesPath); 64 } 65 66 self::$replaces = !is_array($replaces) ? array() : $replaces; 67 self::_getMods(); 68 self::_loadProtected(); 69 self::_loadChecked(); 70 } 71 72 /** 73 * VQMod::modCheck() 74 * 75 * @param string $sourceFile path for file to be modified 76 * @param string $modificationFile path for mods to be applied to file 77 * @return string 78 * @description Checks if a file has modifications and applies them, returning cache files or the file name 79 */ 80 public static function modCheck($sourceFile, $modificationFile = false) { 81 82 if(!self::$_folderChecks) { 83 84 if(self::$logging) { 85 // Create log folder if it doesn't exist 86 $log_folder = self::path(self::$logFolder, true); 87 self::dirCheck($log_folder); 88 } 89 90 // Create cache folder if it doesn't exist 91 $cache_folder = self::path(self::$vqCachePath, true); 92 self::dirCheck($cache_folder); 93 94 // Store cache folder path to save on repeat checks for path validity 95 self::$_cachePathFull = self::path(self::$vqCachePath); 96 97 self::$_folderChecks = true; 98 } 99 100 if(!preg_match('%^([a-z]:)?[\\\\/]%i', $sourceFile)) { 101 $sourcePath = self::path($sourceFile); 102 } else { 103 $sourcePath = self::_realpath($sourceFile); 104 } 105 106 if($modificationFile !== false) { 107 if(!preg_match('%^([a-z]:)?[\\\\/]%i', $modificationFile)) { 108 $modificationsPath = self::path($modificationFile); 109 } else { 110 $modificationsPath = self::_realpath($modificationFile); 111 } 112 } else { 113 $modificationsPath = $sourcePath; 114 } 115 116 if(!$sourcePath || is_dir($sourcePath) || in_array($sourcePath, self::$_doNotMod)) { 117 return $sourceFile; 118 } 119 120 $stripped_filename = preg_replace('~^' . preg_quote(self::getCwd(), '~') . '~i', '', $sourcePath); 121 $cacheFile = self::_cacheName($stripped_filename); 122 $file_last_modified = filemtime($sourcePath); 123 124 if(file_exists($cacheFile) && filemtime($cacheFile) >= self::$_lastModifiedTime && filemtime($cacheFile) >= $file_last_modified) { 125 return $cacheFile; 126 } 127 128 if(isset(self::$_filesModded[$sourcePath])) { 129 return self::$_filesModded[$sourcePath]['cached'] ? $cacheFile : $sourceFile; 130 } 131 132 $changed = false; 133 $fileHash = sha1_file($sourcePath); 134 $fileData = file_get_contents($sourcePath); 135 136 foreach(self::$_mods as $modObject) { 137 foreach($modObject->mods as $path => $mods) { 138 if(self::_checkMatch($path, $modificationsPath)) { 139 $modObject->applyMod($mods, $fileData); 140 } 141 } 142 } 143 144 if (sha1($fileData) != $fileHash) { 145 $writePath = $cacheFile; 146 if(!file_exists($writePath) || is_writable($writePath)) { 147 file_put_contents($writePath, $fileData, LOCK_EX); 148 $changed = true; 149 } 150 } else { 151 file_put_contents(self::path(self::$checkedCache, true), $stripped_filename . PHP_EOL, FILE_APPEND | LOCK_EX); 152 self::$_doNotMod[] = $sourcePath; 153 } 154 155 self::$_filesModded[$sourcePath] = array('cached' => $changed); 156 return $changed ? $writePath : $sourcePath; 157 } 158 159 /** 160 * VQMod::path() 161 * 162 * @param string $path File path 163 * @param bool $skip_real If true path is full not relative 164 * @return bool, string 165 * @description Returns the full true path of a file if it exists, otherwise false 166 */ 167 public static function path($path, $skip_real = false) { 168 $tmp = self::$_cwd . $path; 169 $realpath = $skip_real ? $tmp : self::_realpath($tmp); 170 if(!$realpath) { 171 return false; 172 } 173 return $realpath; 174 } 175 176 /** 177 * VQMod::getCwd() 178 * 179 * @return string 180 * @description Returns current working directory 181 */ 182 public static function getCwd() { 183 return self::$_cwd; 184 } 185 186 /** 187 * VQMod::dirCheck() 188 * 189 * @param string $path 190 * @return null 191 * @description Creates $path folder if it doesn't exist 192 */ 193 public static function dirCheck($path) { 194 if(!is_dir($path)) { 195 if(!mkdir($path)) { 196 die('VQMod::dirCheck - CANNOT CREATE "' . $path . '" DIRECTORY'); 197 } 198 } 199 } 200 201 /** 202 * VQMod::handleXMLError() 203 * 204 * @description Error handler for bad XML files 205 */ 206 public static function handleXMLError($errno, $errstr, $errfile, $errline) { 207 if ($errno == E_WARNING && (substr_count($errstr, 'DOMDocument::load()') > 0)) { 208 throw new DOMException(str_replace('DOMDocument::load()', '', $errstr)); 209 } else { 210 return false; 211 } 212 } 213 214 /** 215 * VQMod::_getMods() 216 * 217 * @return null 218 * @description Gets list of XML files in vqmod xml folder for processing 219 */ 220 private static function _getMods() { 221 222 self::$_modFileList = glob(self::path('vqmod/xml/', true) . '*.xml'); 223 224 foreach(self::$_modFileList as $file) { 225 if(file_exists($file)) { 226 $lastMod = filemtime($file); 227 if($lastMod > self::$_lastModifiedTime){ 228 self::$_lastModifiedTime = $lastMod; 229 } 230 } 231 } 232 233 $xml_folder_time = filemtime(self::path('vqmod/xml')); 234 if($xml_folder_time > self::$_lastModifiedTime){ 235 self::$_lastModifiedTime = $xml_folder_time; 236 } 237 238 $modCache = self::path(self::$modCache); 239 if(self::$_devMode || !file_exists($modCache)) { 240 self::$_lastModifiedTime = time(); 241 } elseif(file_exists($modCache) && filemtime($modCache) >= self::$_lastModifiedTime) { 242 $mods = file_get_contents($modCache); 243 if(!empty($mods)) 244 self::$_mods = unserialize($mods); 245 if(self::$_mods !== false) { 246 return; 247 } 248 } 249 250 // Clear checked cache if rebuilding 251 file_put_contents(self::path(self::$checkedCache, true), '', LOCK_EX); 252 253 if(self::$_modFileList) { 254 self::_parseMods(); 255 } else { 256 self::$log->write('VQMod::_getMods - NO XML FILES READABLE IN XML FOLDER'); 257 } 258 } 259 260 /** 261 * VQMod::_parseMods() 262 * 263 * @return null 264 * @description Loops through xml files and attempts to load them as VQModObject's 265 */ 266 private static function _parseMods() { 267 268 set_error_handler(array('VQMod', 'handleXMLError')); 269 270 $dom = new DOMDocument('1.0', 'UTF-8'); 271 foreach(self::$_modFileList as $modFileKey => $modFile) { 272 if(file_exists($modFile)) { 273 try { 274 $dom->load($modFile); 275 $mod = $dom->getElementsByTagName('modification')->item(0); 276 $vqmver = $mod->getElementsByTagName('vqmver')->item(0); 277 278 if($vqmver) { 279 $version_check = $vqmver->getAttribute('required'); 280 if(strtolower($version_check) == 'true') { 281 if(version_compare(self::$_vqversion, $vqmver->nodeValue, '<')) { 282 self::$log->write('VQMod::_parseMods - FILE "' . $modFile . '" REQUIRES VQMOD "' . $vqmver->nodeValue . '" OR ABOVE AND HAS BEEN SKIPPED'); 283 continue; 284 } 285 } 286 } 287 288 self::$_mods[] = new VQModObject($mod, $modFile); 289 } catch (Exception $e) { 290 self::$log->write('VQMod::_parseMods - INVALID XML FILE: ' . $e->getMessage()); 291 } 292 } else { 293 self::$log->write('VQMod::_parseMods - FILE NOT FOUND: ' . $modFile); 294 } 295 } 296 297 restore_error_handler(); 298 299 $modCache = self::path(self::$modCache, true); 300 $result = file_put_contents($modCache, serialize(self::$_mods), LOCK_EX); 301 if(!$result) { 302 die('VQMod::_parseMods - "/vqmod/mods.cache" FILE NOT WRITEABLE'); 303 } 304 } 305 306 /** 307 * VQMod::_loadProtected() 308 * 309 * @return null 310 * @description Loads protected list and adds them to _doNotMod array 311 */ 312 private static function _loadProtected() { 313 $file = self::path(self::$protectedFilelist); 314 if($file && is_file($file)) { 315 $paths = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 316 if(!empty($paths)) { 317 foreach($paths as $path) { 318 $fullPath = self::path($path); 319 if($fullPath && !in_array($fullPath, self::$_doNotMod)) { 320 self::$_doNotMod[] = $fullPath; 321 } 322 } 323 } 324 } 325 } 326 327 /** 328 * VQMod::_loadChecked() 329 * 330 * @return null 331 * @description Loads already checked files and adds them to _doNotMod array 332 */ 333 private static function _loadChecked() { 334 $file = self::path(self::$checkedCache); 335 if($file && is_file($file)) { 336 $paths = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 337 if(!empty($paths)) { 338 foreach($paths as $path) { 339 $fullPath = self::path($path, true); 340 if($fullPath) { 341 self::$_doNotMod[] = $fullPath; 342 } 343 } 344 } 345 } 346 } 347 348 /** 349 * VQMod::_cacheName() 350 * 351 * @param string $file Filename to be converted to cache filename 352 * @return string 353 * @description Returns cache file name for a path 354 */ 355 private static function _cacheName($file) { 356 return self::$_cachePathFull . 'vq2-' . preg_replace('~[/\\\\]+~', '_', $file); 357 } 358 359 /** 360 * VQMod::_setCwd() 361 * 362 * @param string $path Path to be used as current working directory 363 * @return null 364 * @description Sets the current working directory variable 365 */ 366 private static function _setCwd($path) { 367 self::$_cwd = self::_realpath($path); 368 } 369 370 /** 371 * VQMod::_realpath() 372 * 373 * @param string $file 374 * @return string 375 * @description Returns real path of any path, adding directory slashes if necessary 376 */ 377 private static function _realpath($file) { 378 $path = realpath($file); 379 if(!$path) { 380 return false; 381 } 382 383 if(is_dir($path)) { 384 $path = rtrim($path, self::$directorySeparator) . self::$directorySeparator; 385 } 386 387 return $path; 388 } 389 390 /** 391 * VQMod::_checkMatch() 392 * 393 * @param string $modFilePath Modification path from a <file> node 394 * @param string $checkFilePath File path 395 * @return bool 396 * @description Checks a modification path against a file path 397 */ 398 private static function _checkMatch($modFilePath, $checkFilePath) { 399 $modFilePath = str_replace('\\', '/', $modFilePath); 400 $checkFilePath = str_replace('\\', '/', $checkFilePath); 401 402 if(self::$windows) { 403 $modFilePath = strtolower($modFilePath); 404 $checkFilePath = strtolower($checkFilePath); 405 } 406 407 if($modFilePath == $checkFilePath) { 408 $return = true; 409 } elseif(strpos($modFilePath, '*') !== false) { 410 $return = true; 411 $modParts = explode('/', $modFilePath); 412 $checkParts = explode('/', $checkFilePath); 413 414 if(count($modParts) !== count($checkParts)) { 415 $return = false; 416 } else { 417 418 $toCheck = array_diff_assoc($modParts, $checkParts); 419 420 foreach($toCheck as $k => $part) { 421 if($part === '*') { 422 continue; 423 } elseif(strpos($part, '*') !== false) { 424 $part = preg_replace_callback('~([^*]+)~', array('self', '_quotePath'), $part); 425 $part = str_replace('*', '[^/]*', $part); 426 $part = (bool) preg_match('~^' . $part . '$~', $checkParts[$k]); 427 428 if($part) { 429 continue; 430 } 431 } elseif($part === $checkParts[$k]) { 432 continue; 433 } 434 435 $return = false; 436 break; 437 } 438 439 } 440 } else { 441 $return = false; 442 } 443 444 return $return; 445 } 446 447 /** 448 * VQMod::_quotePath() 449 * 450 * @param string $matches callback matches 451 * @return string 452 * @description apply's preg_quote to string from callback 453 */ 454 private static function _quotePath($matches) { 455 return preg_quote($matches[1], '~'); 456 } 457 } 458 459 /** 460 * VQModLog 461 * @description Object to log information to a file 462 */ 463 class VQModLog { 464 private $_sep; 465 private $_defhash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; 466 private $_logs = array(); 467 468 /** 469 * VQModLog::__construct() 470 * 471 * @return null 472 * @description Object instantiation method 473 */ 474 public function __construct() { 475 $this->_sep = str_repeat('-', 70); 476 } 477 478 /** 479 * VQModLog::__destruct() 480 * 481 * @return null 482 * @description Logs any messages to the log file just before object is destroyed 483 */ 484 public function __destruct() { 485 if(empty($this->_logs) || VQMod::$logging == false) { 486 return; 487 } 488 489 $logPath = VQMod::path(VQMod::$logFolder . date('w_D') . '.log', true); 490 491 $txt = array(); 492 493 $txt[] = str_repeat('-', 10) . ' Date: ' . date('Y-m-d H:i:s') . ' ~ IP : ' . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'N/A') . ' ' . str_repeat('-', 10); 494 $txt[] = 'REQUEST URI : ' . $_SERVER['REQUEST_URI']; 495 496 foreach($this->_logs as $count => $log) { 497 if($log['obj']) { 498 $vars = get_object_vars($log['obj']); 499 $txt[] = 'MOD DETAILS:'; 500 foreach($vars as $k => $v) { 501 if(is_string($v)) { 502 $txt[] = ' ' . str_pad($k, 10, ' ', STR_PAD_RIGHT) . ': ' . $v; 503 } 504 } 505 506 } 507 508 foreach($log['log'] as $msg) { 509 $txt[] = $msg; 510 } 511 512 if ($count > count($this->_logs)-1) { 513 $txt[] = ''; 514 } 515 } 516 517 $txt[] = $this->_sep; 518 $txt[] = str_repeat(PHP_EOL, 2); 519 $append = true; 520 521 if(!file_exists($logPath)) { 522 $append = false; 523 } else { 524 $content = file_get_contents($logPath); 525 if(!empty($content) && strpos($content, ' Date: ' . date('Y-m-d ')) === false) { 526 $append = false; 527 } 528 } 529 530 $result = file_put_contents($logPath, implode(PHP_EOL, $txt), ($append ? FILE_APPEND | LOCK_EX : LOCK_EX)); 531 if(!$result) { 532 die('VQModLog::__destruct - LOG FILE "' . $logPath . '" COULD NOT BE WRITTEN'); 533 } 534 } 535 536 /** 537 * VQModLog::write() 538 * 539 * @param string $data Text to be added to log file 540 * @param VQModObject $obj Modification the error belongs to 541 * @return null 542 * @description Adds error to log object ready to be output 543 */ 544 public function write($data, VQModObject $obj = NULL) { 545 if($obj) { 546 $hash = sha1($obj->id); 547 } else { 548 $hash = $this->_defhash; 549 } 550 551 if(empty($this->_logs[$hash])) { 552 $this->_logs[$hash] = array( 553 'obj' => $obj, 554 'log' => array() 555 ); 556 } 557 558 if(VQMod::$fileModding) { 559 $this->_logs[$hash]['log'][] = PHP_EOL . 'File Name : ' . VQMod::$fileModding; 560 } 561 562 $this->_logs[$hash]['log'][] = $data; 563 564 } 565 } 566 567 /** 568 * VQModObject 569 * @description Object for the <modification> that orchestrates each applied modification 570 */ 571 class VQModObject { 572 public $modFile = ''; 573 public $id = ''; 574 public $version = ''; 575 public $vqmver = ''; 576 public $author = ''; 577 public $mods = array(); 578 579 private $_skip = false; 580 581 /** 582 * VQModObject::__construct() 583 * 584 * @param DOMNode $node <modification> node 585 * @param string $modFile File modification is from 586 * @return null 587 * @description Loads modification meta information 588 */ 589 public function __construct(DOMNode $node, $modFile) { 590 if($node->hasChildNodes()) { 591 foreach($node->childNodes as $child) { 592 $name = (string) $child->nodeName; 593 if(isset($this->$name)) { 594 $this->$name = (string) $child->nodeValue; 595 } 596 } 597 } 598 599 $this->modFile = $modFile; 600 $this->_parseMods($node); 601 } 602 603 /** 604 * VQModObject::skip() 605 * 606 * @return bool 607 * @description Returns the skip status of a modification 608 */ 609 public function skip() { 610 return $this->_skip; 611 } 612 613 /** 614 * VQModObject::applyMod() 615 * 616 * @param array $mods Array of search add nodes 617 * @param string $data File contents to be altered 618 * @return null 619 * @description Applies all modifications to the text data 620 */ 621 public function applyMod($mods, &$data) { 622 if($this->_skip) return; 623 $tmp = $data; 624 625 foreach($mods as $mod) { 626 VQMod::$fileModding = $mod['fileToMod'] . '(' . $mod['opIndex'] . ')'; 627 if(!empty($mod['ignoreif'])) { 628 if($mod['ignoreif']->regex == 'true') { 629 if (preg_match($mod['ignoreif']->getContent(), $tmp)) { 630 continue; 631 } 632 } else { 633 if (strpos($tmp, $mod['ignoreif']->getContent()) !== false) { 634 continue; 635 } 636 } 637 } 638 639 $indexCount = 0; 640 641 $tmp = $this->_explodeData($tmp); 642 $lineMax = count($tmp) - 1; 643 644 // <add> tag attributes - Override <search> attributes if set 645 foreach(array_keys((array)$mod['search']) as $key) { 646 if ($key == "\x0VQNode\x0_content") { continue; } 647 if ($key == "trim") { continue; } 648 if (isset($mod['add']->$key) && $mod['add']->$key) { 649 $mod['search']->$key = $mod['add']->$key; 650 } 651 } 652 653 switch($mod['search']->position) { 654 case 'top': 655 $tmp[$mod['search']->offset] = $mod['add']->getContent() . $tmp[$mod['search']->offset]; 656 break; 657 658 case 'bottom': 659 $offset = $lineMax - $mod['search']->offset; 660 if($offset < 0){ 661 $tmp[-1] = $mod['add']->getContent(); 662 } else { 663 $tmp[$offset] .= $mod['add']->getContent(); 664 } 665 break; 666 667 default: 668 669 $changed = false; 670 foreach($tmp as $lineNum => $line) { 671 if(strlen($mod['search']->getContent()) == 0) { 672 if($mod['error'] == 'log' || $mod['error'] == 'abort') { 673 VQMod::$log->write('VQModObject::applyMod - EMPTY SEARCH CONTENT ERROR', $this); 674 } 675 break; 676 } 677 678 if($mod['search']->regex == 'true') { 679 $pos = @preg_match($mod['search']->getContent(), $line); 680 if($pos === false) { 681 if($mod['error'] == 'log' || $mod['error'] == 'abort' ) { 682 VQMod::$log->write('VQModObject::applyMod - INVALID REGEX ERROR - ' . $mod['search']->getContent(), $this); 683 } 684 break 2; 685 } elseif($pos == 0) { 686 $pos = false; 687 } 688 } else { 689 $pos = strpos($line, $mod['search']->getContent()); 690 } 691 692 if($pos !== false) { 693 $indexCount++; 694 $changed = true; 695 696 if(!$mod['search']->indexes() || ($mod['search']->indexes() && in_array($indexCount, $mod['search']->indexes()))) { 697 698 switch($mod['search']->position) { 699 case 'before': 700 $offset = ($lineNum - $mod['search']->offset < 0) ? -1 : $lineNum - $mod['search']->offset; 701 $tmp[$offset] = empty($tmp[$offset]) ? $mod['add']->getContent() : $mod['add']->getContent() . "\n" . $tmp[$offset]; 702 break; 703 704 case 'after': 705 $offset = ($lineNum + $mod['search']->offset > $lineMax) ? $lineMax : $lineNum + $mod['search']->offset; 706 $tmp[$offset] = $tmp[$offset] . "\n" . $mod['add']->getContent(); 707 break; 708 709 case 'ibefore': 710 $tmp[$lineNum] = str_replace($mod['search']->getContent(), $mod['add']->getContent() . $mod['search']->getContent(), $line); 711 break; 712 713 case 'iafter': 714 $tmp[$lineNum] = str_replace($mod['search']->getContent(), $mod['search']->getContent() . $mod['add']->getContent(), $line); 715 break; 716 717 default: 718 if(!empty($mod['search']->offset)) { 719 if($mod['search']->offset > 0) { 720 for($i = 1; $i <= $mod['search']->offset; $i++) { 721 if(isset($tmp[$lineNum + $i])) { 722 $tmp[$lineNum + $i] = ''; 723 } 724 } 725 } elseif($mod['search']->offset < 0) { 726 for($i = -1; $i >= $mod['search']->offset; $i--) { 727 if(isset($tmp[$lineNum + $i])) { 728 $tmp[$lineNum + $i] = ''; 729 } 730 } 731 } 732 } 733 734 if($mod['search']->regex == 'true') { 735 $tmp[$lineNum] = preg_replace($mod['search']->getContent(), $mod['add']->getContent(), $line); 736 } else { 737 $tmp[$lineNum] = str_replace($mod['search']->getContent(), $mod['add']->getContent(), $line); 738 } 739 break; 740 } 741 } 742 } 743 } 744 745 if(!$changed) { 746 $skip = ($mod['error'] == 'skip' || $mod['error'] == 'log') ? ' (SKIPPED)' : ' (ABORTING MOD)'; 747 748 if($mod['error'] == 'log' || $mod['error'] == 'abort') { 749 VQMod::$log->write('VQModObject::applyMod - SEARCH NOT FOUND' . $skip . ': ' . $mod['search']->getContent(), $this); 750 } 751 752 if($mod['error'] == 'abort') { 753 $this->_skip = true; 754 return; 755 } 756 757 } 758 759 break; 760 } 761 ksort($tmp); 762 $tmp = $this->_implodeData($tmp); 763 } 764 765 VQMod::$fileModding = false; 766 767 $data = $tmp; 768 } 769 770 /** 771 * VQModObject::_parseMods() 772 * 773 * @param DOMNode $node <modification> node to be parsed 774 * @return null 775 * @description Parses modifications in preparation for the applyMod method to work 776 */ 777 private function _parseMods(DOMNode $node){ 778 $files = $node->getElementsByTagName('file'); 779 780 $replaces = VQMod::$replaces; 781 782 foreach($files as $file) { 783 $path = $file->getAttribute('path') ? $file->getAttribute('path') : ''; 784 $filesToMod = explode(',', $file->getAttribute('name')); 785 786 foreach($filesToMod as $filename) { 787 788 $fileToMod = $path . $filename; 789 if(!empty($replaces)) { 790 foreach($replaces as $r) { 791 if(count($r) == 2) { 792 $fileToMod = preg_replace($r[0], $r[1], $fileToMod); 793 } 794 } 795 } 796 797 $error = ($file->hasAttribute('error')) ? $file->getAttribute('error') : 'log'; 798 $fullPath = VQMod::path($fileToMod); 799 800 if(!$fullPath || !file_exists($fullPath)){ 801 if(strpos($fileToMod, '*') !== false) { 802 $fullPath = VQMod::getCwd() . $fileToMod; 803 } else { 804 if ($error == 'log' || $error == 'abort') { 805 $skip = ($error == 'log') ? ' (SKIPPED)' : ' (ABORTING MOD)'; 806 VQMod::$log->write('VQModObject::parseMods - Could not resolve path for [' . $fileToMod . ']' . $skip, $this); 807 } 808 809 if ($error == 'log' || $error == 'skip') { 810 continue; 811 } elseif ($error == 'abort') { 812 return false; 813 } 814 } 815 } 816 817 $operations = $file->getElementsByTagName('operation'); 818 819 foreach($operations as $opIndex => $operation) { 820 VQMod::$fileModding = $fileToMod . '(' . $opIndex . ')'; 821 $skipOperation = false; 822 823 $error = ($operation->hasAttribute('error')) ? $operation->getAttribute('error') : 'abort'; 824 $ignoreif = $operation->getElementsByTagName('ignoreif')->item(0); 825 826 if($ignoreif) { 827 $ignoreif = new VQSearchNode($ignoreif); 828 } else { 829 $ignoreif = false; 830 } 831 832 $search = $operation->getElementsByTagName('search')->item(0); 833 $add = $operation->getElementsByTagName('add')->item(0); 834 835 if(!$search) { 836 VQMod::$log->write('Operation <search> tag missing', $this); 837 $skipOperation = true; 838 } 839 840 if(!$add) { 841 VQMod::$log->write('Operation <add> tag missing', $this); 842 $skipOperation = true; 843 } 844 845 if(!$skipOperation) { 846 $this->mods[$fullPath][] = array( 847 'search' => new VQSearchNode($search), 848 'add' => new VQAddNode($add), 849 'ignoreif' => $ignoreif, 850 'error' => $error, 851 'fileToMod' => $fileToMod, 852 'opIndex' => $opIndex, 853 ); 854 } 855 } 856 VQMod::$fileModding = false; 857 } 858 } 859 } 860 861 /** 862 * VQModObject::_explodeData() 863 * 864 * @param string $data File contents 865 * @return string 866 * @description Splits a file into an array of individual lines 867 */ 868 private function _explodeData($data) { 869 return explode("\n", $data); 870 } 871 872 /** 873 * VQModObject::_implodeData() 874 * 875 * @param array $data Array of lines 876 * @return string 877 * @description Joins an array of lines back into a text file 878 */ 879 private function _implodeData($data) { 880 return implode("\n", $data); 881 } 882 } 883 884 /** 885 * VQNode 886 * @description Basic node object blueprint 887 */ 888 class VQNode { 889 public $regex = 'false'; 890 public $trim = 'false'; 891 private $_content = ''; 892 893 /** 894 * VQNode::__construct() 895 * 896 * @param DOMNode $node Search/add node 897 * @return null 898 * @description Parses the node attributes and sets the node property 899 */ 900 public function __construct(DOMNode $node) { 901 $this->_content = $node->nodeValue; 902 903 if($node->hasAttributes()) { 904 foreach($node->attributes as $attr) { 905 $name = $attr->nodeName; 906 if(isset($this->$name)) { 907 $this->$name = $attr->nodeValue; 908 } 909 } 910 } 911 } 912 913 /** 914 * VQNode::getContent() 915 * 916 * @return string 917 * @description Returns the content, trimmed if applicable 918 */ 919 public function getContent() { 920 $content = ($this->trim == 'true') ? trim($this->_content) : $this->_content; 921 return $content; 922 } 923 } 924 925 /** 926 * VQSearchNode 927 * @description Object for the <search> xml tags 928 */ 929 class VQSearchNode extends VQNode { 930 public $position = 'replace'; 931 public $offset = 0; 932 public $index = 'false'; 933 public $regex = 'false'; 934 public $trim = 'true'; 935 936 /** 937 * VQSearchNode::indexes() 938 * 939 * @return bool, array 940 * @description Returns the index values to use the search on, or false if none 941 */ 942 public function indexes() { 943 if($this->index == 'false') { 944 return false; 945 } 946 $tmp = explode(',', $this->index); 947 foreach($tmp as $k => $v) { 948 if(!is_int($v)) { 949 unset($k); 950 } 951 } 952 $tmp = array_unique($tmp); 953 return empty($tmp) ? false : $tmp; 954 } 955 } 956 957 /** 958 * VQAddNode 959 * @description Object for the <add> xml tags 960 */ 961 class VQAddNode extends VQNode { 962 public $position = false; 963 public $offset = false; 964 public $index = false; 965 public $regex = false; 966 public $trim = 'false'; 967 }