shop.balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

ugrsr.class.php (13128B)


      1 <?php
      2 
      3 /**
      4  * UGRSR
      5  * 
      6  * @package Universal Global RegEx Search/Replace
      7  * @author Qphoria - http://theqdomain.com/ & Jay Gilford - http://jaygilford.com/
      8  * @copyright Qphoria & Jay Gilford 2011
      9  * @version 0.3
     10  * @access public
     11  * 
     12  * @information
     13  * This class will perform mass search and replace actions
     14  * based on regex pattern matching. It recursively grabs all files
     15  * below it's given path and applies the specified change(s)
     16  * 
     17  * @license
     18  * Permission is hereby granted, free of charge, to any person to
     19  * use, copy, modify, distribute, sublicense, and/or sell copies
     20  * of the Software, subject to the following conditions:
     21  * 
     22  * The above copyright notice and this permission notice shall be
     23  * included in all copies or substantial portions of the Software
     24  * 
     25  * @warning
     26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     27  * EXPRESSED OR IMPLIED.
     28  *  
     29  */
     30 class UGRSR {
     31 	
     32 	public $debug = false;				// Show debug messages switch
     33 	public $flags = 0;					// Flags for the file recursive glob
     34 	public $pattern = '*.php';			// File pattern to match with glob
     35 	public $recursive = true;			// Recursion into subdirectories switch
     36 	public $test_mode = false;			// Test mode only switch
     37 	public $file_search = true;			// Search for files switch
     38 	
     39 	
     40 	private $_regexes = array();		// Array for regex patterns and replaces
     41 	private $_path = '';				// Path to directory to work with
     42 	private $_protected = array();		// Array of protected files
     43 	private $_files = array();			// Array of manually added file locations
     44 	
     45 	/**
     46 	 * UGRSR::__construct()
     47 	 * 
     48 	 * @param string $path
     49 	 * @return null
     50 	 */
     51 	function __construct($path = '') {
     52 		
     53 		// Use current working directory if none given as a parameter
     54 		if(empty($path)) {
     55 			$path = getcwd();
     56 		}
     57 		
     58 		// Apply path to var
     59 		$this->setPath($path);
     60 		
     61 		// Check to make sure the script calling the class is set to be protected
     62 		if(!isset($_SERVER['SCRIPT_FILENAME'])) {
     63 			DIE('SCRIPT FILENAME COULD NOT BE DETERMINED');
     64 		}
     65 		
     66 		// Set default file protections
     67 		$this->resetProtected();
     68 	}
     69 	
     70 	/**
     71 	 * UGRSR::addPattern()
     72 	 * 
     73 	 * @param string $pattern
     74 	 * @param string $replace
     75 	 * @return bool
     76 	 */
     77 	public function addPattern($pattern, $replace) {
     78 		
     79 		// If pattern is empty throw error
     80 		if(empty($pattern)) {
     81 			$this->_dbg('PATTERN EMPTY');
     82 			return false;
     83 		}
     84 		
     85 		// Add regex pattern and replace vars to _regexes array
     86 		$this->_regexes[] = array(
     87 			'pattern'		=> $pattern,
     88 			'replace'		=> $replace
     89 		);
     90 		
     91 		return true;
     92 	}
     93 	
     94 	/**
     95 	 * UGRSR::mergePatterns()
     96 	 * 
     97 	 * @param array $pattern_array
     98 	 * @return bool
     99 	 */
    100 	public function mergePatterns($pattern_array) {
    101 		
    102 		// If the param is not an array throw error
    103 		if(!is_array($pattern_array)) {
    104 			$this->_dbg('PARAM IS NOT AN ARRAY');
    105 			return false;
    106 		}
    107 		
    108 		//Loop through pattern array
    109 		foreach($pattern_array as $data) {
    110 			
    111 			// If pattern or replace keys not set throw error and continue loop
    112 			if(!isset($data['pattern']) || !isset($data['replace'])) {
    113 				$this->_dbg('ARRAY KEYS NOT SET');
    114 				continue;
    115 			}
    116 			
    117 			// Add regex and replace
    118 			$this->addPattern($data['pattern'], $data['replace']);
    119 		}
    120 		
    121 		return true;
    122 	}
    123 	
    124 	/**
    125 	 * UGRSR::clearPatterns()
    126 	 * 
    127 	 * @return null
    128 	 */
    129 	public function clearPatterns() {
    130 		
    131 		// Set regexes var to empty array
    132 		$this->_regexes = array();
    133 	}
    134 	
    135 	/**
    136 	 * UGRSR::addFile()
    137 	 * 
    138 	 * @param string $filename
    139 	 * @param bool 
    140 	 * @return bool
    141 	 */
    142 	public function addFile($filename, $omit_path = false) {
    143 		
    144 		$file = $omit_path ? $filename : $this->_path . $filename;
    145 		
    146 		// If the protection isnt for a file throw an error
    147 		if(!is_file($file)) {
    148 			$this->_dbg('FILE [' . $file . '] IS NOT A FILE');
    149 			return false;
    150 		}
    151 		
    152 		// Get real full path to file
    153 		$real_filename = realpath($file);
    154 		
    155 		// If real path for file can't be found throw error
    156 		if(!$real_filename) {
    157 			$this->_dbg('FILE [' . $file . '] IS NOT A FILE');
    158 			return false;
    159 		}
    160 		
    161 		// Don't add file if it's already in the file list
    162 		if(in_array($real_filename, $this->_files)) {
    163 			$this->_dbg('FILE [' . $file . '] ALREADY IN FILE LIST');
    164 			return false;
    165 		}
    166 		
    167 		// Add filename to file list
    168 		$this->_dbg('FILE [' . $real_filename . '] ADDED TO FILE LIST');
    169 		$this->_files[] = $real_filename;
    170 		
    171 		return true;
    172 	}
    173 	
    174 	/**
    175 	 * UGRSR::resetFileList()
    176 	 * 
    177 	 * @return true
    178 	 */
    179 	public function resetFileList() {
    180 		// Clear file list
    181 		$this->_files = array();
    182 		
    183 		$this->_dbg('FILE LIST RESET');
    184 		
    185 		return true;
    186 	}
    187 	
    188 	/**
    189 	 * UGRSR::addProtected()
    190 	 * 
    191 	 * @param string $filename
    192 	 * @return bool
    193 	 */
    194 	public function addProtected($filename) {
    195 		
    196 		// If the protection isnt for a file throw an error
    197 		if(!is_file($filename)) {
    198 			$this->_dbg('FILE [' . $filename . '] IS NOT A FILE');
    199 			return false;
    200 		}
    201 		
    202 		// Get real full path to file
    203 		$real_filename = realpath($filename);
    204 		
    205 		// If real path for file can't be found throw error
    206 		if(!$real_filename) {
    207 			$this->_dbg('FILE [' . $filename . '] IS NOT A FILE');
    208 			return false;
    209 		}
    210 		
    211 		// Add filename to protected list
    212 		$this->_dbg('FILE [' . $filename . '] ADDED TO PROTECTED LIST');
    213 		$this->_protected[] = $real_filename;
    214 		
    215 		return true;
    216 	}
    217 	
    218 	/**
    219 	 * UGRSR::resetProtected()
    220 	 * 
    221 	 * @return true
    222 	 */
    223 	public function resetProtected() {
    224 		// Clear protected list
    225 		$this->_protected = array();
    226 		
    227 		$this->_dbg('PROTECTED FILES RESET');
    228 		//Add this class to protected list
    229 		$this->_protected[] = realpath(__FILE__);
    230 		
    231 		// Add script that called the class to protected list
    232 		$this->_protected[] = realpath($_SERVER['SCRIPT_FILENAME']);
    233 		
    234 		return true;
    235 	}
    236 	
    237 	/**
    238 	 * UGRSR::setPath()
    239 	 * 
    240 	 * @param string $path
    241 	 * @return bool
    242 	 */
    243 	public function setPath($path) {
    244 		
    245 		// Get full real path to given path
    246 		if(is_executable($path)) {
    247 			$realpath = realpath($path) . '/';
    248 		} else {
    249 			$realpath = $path;
    250 		}
    251 		
    252 		// If path can't be found or isn't a directory throw an error
    253 		if(!file_exists($realpath)) {
    254 			$this->_dbg('INVALID PATH [' . $realpath . ']');
    255 			return false;
    256 		}
    257 		
    258 		// Set path to new value
    259 		$this->_dbg('NEW PATH SET [' . $realpath . ']');
    260 		$this->_path = $realpath;
    261 		
    262 		return true;
    263 	}
    264 	
    265 	/**
    266 	 * UGRSR::run()
    267 	 * 
    268 	 * @return bool
    269 	 */
    270 	public function run() {
    271 		
    272 		// If regexes array is empty throw an error
    273 		if(empty($this->_regexes)) {
    274 			$this->_dbg('REGEX LIST IS EMPTY');
    275 			return false;
    276 		}
    277 		
    278 		$this->_dbg('STARTING RUN');
    279 		$this->_dbg();
    280 		
    281 		// Set files to list of manually added files
    282 		$files = $this->_files;
    283 
    284 		// Check if file searching is enabled
    285 		if($this->file_search) {
    286 			$this->_dbg('GETTING FILE LIST');
    287 			
    288 			// Get a list of files under defined path
    289 			$found = array_merge($this->_rglob());
    290 	
    291 			$this->_dbg(count($found) . ' FILES FOUND');
    292 			$this->_dbg();
    293 			
    294 			// merge list of files with manually added file list
    295 			$files = array_merge($files, $found);
    296 		}
    297 		
    298 		// Check files found or throw error and return
    299 		if(count($files) == 0) {
    300 			$this->_dbg('NO FILES TO BE PROCESSED');
    301 			return false;
    302 		}
    303 		
    304 		$this->_dbg('STARTING FILE PROCESSING');
    305 		
    306 		// Var for total regex matches throughout files
    307 		$global_change_count = 0;
    308 		
    309 		// Var to hold 
    310 		$global_write_count = 0;
    311 		
    312 		// Var to hold number of bytes saved
    313 		$bytes_saved = 0;
    314 		
    315 		// Loop through files one at a time
    316 		foreach($files as $filename) {
    317 			
    318 			// Var for total regex matches in current file
    319 			$file_change_count = 0;
    320 			
    321 			// Load file contents
    322 			$content = $original_content = file_get_contents($filename);
    323 			
    324 			// If content couldn't be loaded throw error
    325 			if($content === FALSE) {
    326 				$this->_dbg('COULD NOT OPEN [' . $filename . ']');
    327 				continue;
    328 			}
    329 			
    330 			// If file length is 0 throw error
    331 			if(strlen($content) == 0) {
    332 				$this->_dbg('EMPTY FILE SKIPPED [' . $filename . ']');
    333 				continue;
    334 			}
    335 			
    336 			// Loop through _regexes array applying changes to content
    337 			foreach($this->_regexes as $regex) {
    338 				
    339 				// Var for total regex matches for individual pattern
    340 				$change_count = 0;
    341 				
    342 				// Try replacing content
    343 				$content = preg_replace($regex['pattern'], $regex['replace'], $content, -1, $change_count);
    344 				
    345 				// If regex operation fails throw error and abort all operations
    346 				if($content === NULL) {
    347 					$this->_dbg('REGEX PATTERN ERROR <strong>' . $regex['pattern'] . '</strong>');
    348 					$this->_dbg('ABORTING ALL OPERATIONS');
    349 					break 2;
    350 				}
    351 				
    352 				// Add individual pattern change count to file change count
    353 				$file_change_count += $change_count;
    354 				$this->_dbg('REGEX <strong>' . $regex['pattern'] . '</strong> FOUND ' . ($change_count ? $change_count : 'NO') . ' MATCHES IN [' . $filename . ']');
    355 				
    356 			}
    357 			
    358 			// If not in test mode and content has changed attempt to write back to file			
    359 			if($content !== $original_content && !$this->test_mode) {
    360 				$this->_dbg('ATTEMPTING TO WRITE TO FILE [' . $filename . ']');
    361 				
    362 				// If file isn't writeable throw error
    363 				if(!is_writeable($filename)) {
    364 					$this->_dbg('CANNOT WRITE TO [' . $filename . ']');
    365 				} else {
    366 					
    367 					// Write file data back to file and show result
    368 					$result = file_put_contents($filename, $content);
    369 					if($result) {
    370 						$this->_dbg('SUCCESSFULLY WROTE ' . $result . ' BYTES  TO [' . $filename . ']');
    371 						$global_write_count++;
    372 					} else {
    373 						$this->_dbg('WRITE OPERATION FAILED IN [' . $filename . ']');
    374 					}
    375 				}
    376 			}
    377 			
    378 			// Add byte difference to $bytes_saved
    379 			$bytes_saved += (strlen($original_content) - strlen($content));
    380 			
    381 			// Add total file changes count to global file changes count 
    382 			$global_change_count += $file_change_count;
    383 			
    384 			$this->_dbg('TOTAL NUMBER OF FILE CHANGES: ' . $file_change_count);
    385 			$this->_dbg();
    386 		}
    387 		
    388 		$this->_dbg();
    389 		$this->_dbg('FINISHED FILE PROCESSING');
    390 		$this->_dbg('TOTAL CHANGES APPLIED ACROSS ALL FILES: <strong>' . $global_change_count . '</strong>');
    391 		$this->_dbg('TOTAL BYTES SAVED ACROSS ALL FILES: <strong>' . $bytes_saved . '</strong>');
    392 		$this->_dbg();
    393 		
    394 		$this->_dbg('FINISHED RUN');
    395 		$this->_dbg();
    396 		
    397 		// Pass back the number of changes and writes matched
    398 		return array(
    399 			'changes' => $global_change_count,
    400 			'writes' => $global_write_count
    401 		);
    402 	}
    403 	
    404 	/**
    405 	 * UGRSR::_dbg()
    406 	 * 
    407 	 * @param string $message
    408 	 * @return NULL;
    409 	 */
    410 	private function _dbg($message = '') {
    411 		
    412 		// If in debug mode show output
    413 		if($this->debug) {
    414 			
    415 			// Set mode type
    416 			$mode = $this->test_mode ? 'TEST MODE' : 'LIVE MODE';
    417 			
    418 			// If there's a message echo that otherwise echo some whitespace for formatting
    419 			if(!empty($message)) {
    420 				echo $mode . ': *** ' . $message . " ***<br />\r\n";
    421 			} else {
    422 				echo str_repeat("<br />\r\n", 2);
    423 			}
    424 		}
    425 	}
    426 	
    427 	/**
    428 	 * UGRSR::_rglob()
    429 	 * 
    430 	 * @param string $path
    431 	 * @return array
    432 	 */
    433 	private function _rglob($path = NULL) {
    434 		
    435 		// If the path isn't supplied use the one stored in _path
    436 		if($path === NULL) $path = $this->_path;
    437 		$this->_dbg('SEARCHING PATH [' . $path .']');
    438 		
    439 		// Get list of files under current directory
    440 		$files = glob($path . $this->pattern, $this->flags);
    441 		
    442 		// Loop through all files
    443 		foreach($files as $key => &$file) {
    444 			// Flag to allow file to be kept in file array
    445 			$remove_file = false;
    446 			
    447 			// Get full path of file
    448 			$realfile = realpath($file);
    449 			
    450 			// Report if file path can't be resolved
    451 			if($realfile === FALSE) {
    452 				$this->_dbg('REAL PATH OF FILE [' . $file . '] COULD NOT BE RESOLVED');
    453 				$remove_file = true;
    454 			}
    455 			
    456 			// Report if file path is in the protected list
    457 			if($realfile && in_array($realfile, $this->_protected)) {
    458 				$this->_dbg('PROTECTED FILE [' . $realfile . '] REMOVED FROM FILES LIST');
    459 				$remove_file = true;
    460 			}
    461 			
    462 			// Report if file path is in the protected list
    463 			if($realfile && in_array($realfile, $this->_files)) {
    464 				$this->_dbg('FILE [' . $realfile . '] SKIPPED. ALREADY IN FILE LIST');
    465 				$remove_file = true;
    466 			}
    467 			
    468 			// Report if write access cannot be granted for file
    469 			if($realfile && !is_writeable($realfile)) {
    470 				$this->_dbg('FILE [' . $file . '] SKIPPED AS CANNOT WRITE TO IT');
    471 				$remove_file = true;
    472 			}
    473 			
    474 			// Remove from file list if any issues
    475 			if($remove_file) {
    476 				unset($files[$key]);
    477 			}
    478 		}
    479 		
    480 		// If recursion is set get files in subdirectories
    481 		if($this->recursive) {
    482 		
    483 			// Get list of directories under current path
    484 			$paths = glob($path . '*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
    485 			
    486 			// Loop through subdirectories and merge files into directory list
    487 			foreach($paths as $p) {
    488 				$files = array_merge($files, $this->_rglob($p));
    489 			}
    490 		}
    491 		
    492 		// Pass file array back		
    493 		return $files;
    494 	}
    495 }