[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Filesystem; 11 12 use Joomla\CMS\Language\Text; 13 14 // phpcs:disable PSR1.Files.SideEffects 15 \defined('JPATH_PLATFORM') or die; 16 // phpcs:enable PSR1.Files.SideEffects 17 18 /** 19 * A Unified Diff Format Patcher class 20 * 21 * @link http://sourceforge.net/projects/phppatcher/ This has been derived from the PhpPatcher version 0.1.1 written by Giuseppe Mazzotta 22 * @since 3.0.0 23 */ 24 class Patcher 25 { 26 /** 27 * Regular expression for searching source files 28 */ 29 public const SRC_FILE = '/^---\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A'; 30 31 /** 32 * Regular expression for searching destination files 33 */ 34 public const DST_FILE = '/^\\+\\+\\+\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A'; 35 36 /** 37 * Regular expression for searching hunks of differences 38 */ 39 public const HUNK = '/@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@($)/A'; 40 41 /** 42 * Regular expression for splitting lines 43 */ 44 public const SPLIT = '/(\r\n)|(\r)|(\n)/'; 45 46 /** 47 * @var array sources files 48 * @since 3.0.0 49 */ 50 protected $sources = array(); 51 52 /** 53 * @var array destination files 54 * @since 3.0.0 55 */ 56 protected $destinations = array(); 57 58 /** 59 * @var array removal files 60 * @since 3.0.0 61 */ 62 protected $removals = array(); 63 64 /** 65 * @var array patches 66 * @since 3.0.0 67 */ 68 protected $patches = array(); 69 70 /** 71 * @var array instance of this class 72 * @since 3.0.0 73 */ 74 protected static $instance; 75 76 /** 77 * Constructor 78 * 79 * The constructor is protected to force the use of FilesystemPatcher::getInstance() 80 * 81 * @since 3.0.0 82 */ 83 protected function __construct() 84 { 85 } 86 87 /** 88 * Method to get a patcher 89 * 90 * @return Patcher an instance of the patcher 91 * 92 * @since 3.0.0 93 */ 94 public static function getInstance() 95 { 96 if (!isset(static::$instance)) { 97 static::$instance = new static(); 98 } 99 100 return static::$instance; 101 } 102 103 /** 104 * Reset the patcher 105 * 106 * @return Patcher This object for chaining 107 * 108 * @since 3.0.0 109 */ 110 public function reset() 111 { 112 $this->sources = array(); 113 $this->destinations = array(); 114 $this->removals = array(); 115 $this->patches = array(); 116 117 return $this; 118 } 119 120 /** 121 * Apply the patches 122 * 123 * @return integer The number of files patched 124 * 125 * @since 3.0.0 126 * @throws \RuntimeException 127 */ 128 public function apply() 129 { 130 foreach ($this->patches as $patch) { 131 // Separate the input into lines 132 $lines = self::splitLines($patch['udiff']); 133 134 // Loop for each header 135 while (self::findHeader($lines, $src, $dst)) { 136 $done = false; 137 138 $regex = '#^([^/]*/)*#'; 139 140 if ($patch['strip'] !== null) { 141 $regex = '#^([^/]*/){' . (int) $patch['strip'] . '}#'; 142 } 143 144 $src = $patch['root'] . preg_replace($regex, '', $src); 145 $dst = $patch['root'] . preg_replace($regex, '', $dst); 146 147 // Loop for each hunk of differences 148 while (self::findHunk($lines, $src_line, $src_size, $dst_line, $dst_size)) { 149 $done = true; 150 151 // Apply the hunk of differences 152 $this->applyHunk($lines, $src, $dst, $src_line, $src_size, $dst_line, $dst_size); 153 } 154 155 // If no modifications were found, throw an exception 156 if (!$done) { 157 throw new \RuntimeException('Invalid Diff'); 158 } 159 } 160 } 161 162 // Initialize the counter 163 $done = 0; 164 165 // Patch each destination file 166 foreach ($this->destinations as $file => $content) { 167 $buffer = implode("\n", $content); 168 169 if (File::write($file, $buffer)) { 170 if (isset($this->sources[$file])) { 171 $this->sources[$file] = $content; 172 } 173 174 $done++; 175 } 176 } 177 178 // Remove each removed file 179 foreach ($this->removals as $file) { 180 if (File::delete($file)) { 181 if (isset($this->sources[$file])) { 182 unset($this->sources[$file]); 183 } 184 185 $done++; 186 } 187 } 188 189 // Clear the destinations cache 190 $this->destinations = array(); 191 192 // Clear the removals 193 $this->removals = array(); 194 195 // Clear the patches 196 $this->patches = array(); 197 198 return $done; 199 } 200 201 /** 202 * Add a unified diff file to the patcher 203 * 204 * @param string $filename Path to the unified diff file 205 * @param string $root The files root path 206 * @param integer $strip The number of '/' to strip 207 * 208 * @return Patcher $this for chaining 209 * 210 * @since 3.0.0 211 */ 212 public function addFile($filename, $root = JPATH_BASE, $strip = 0) 213 { 214 return $this->add(file_get_contents($filename), $root, $strip); 215 } 216 217 /** 218 * Add a unified diff string to the patcher 219 * 220 * @param string $udiff Unified diff input string 221 * @param string $root The files root path 222 * @param integer $strip The number of '/' to strip 223 * 224 * @return Patcher $this for chaining 225 * 226 * @since 3.0.0 227 */ 228 public function add($udiff, $root = JPATH_BASE, $strip = 0) 229 { 230 $this->patches[] = array( 231 'udiff' => $udiff, 232 'root' => isset($root) ? rtrim($root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : '', 233 'strip' => $strip, 234 ); 235 236 return $this; 237 } 238 239 /** 240 * Separate CR or CRLF lines 241 * 242 * @param string $data Input string 243 * 244 * @return array The lines of the inputdestination file 245 * 246 * @since 3.0.0 247 */ 248 protected static function splitLines($data) 249 { 250 return preg_split(self::SPLIT, $data); 251 } 252 253 /** 254 * Find the diff header 255 * 256 * The internal array pointer of $lines is on the next line after the finding 257 * 258 * @param array $lines The udiff array of lines 259 * @param string $src The source file 260 * @param string $dst The destination file 261 * 262 * @return boolean TRUE in case of success, FALSE in case of failure 263 * 264 * @since 3.0.0 265 * @throws \RuntimeException 266 */ 267 protected static function findHeader(&$lines, &$src, &$dst) 268 { 269 // Get the current line 270 $line = current($lines); 271 272 // Search for the header 273 while ($line !== false && !preg_match(self::SRC_FILE, $line, $m)) { 274 $line = next($lines); 275 } 276 277 if ($line === false) { 278 // No header found, return false 279 return false; 280 } 281 282 // Set the source file 283 $src = $m[1]; 284 285 // Advance to the next line 286 $line = next($lines); 287 288 if ($line === false) { 289 throw new \RuntimeException('Unexpected EOF'); 290 } 291 292 // Search the destination file 293 if (!preg_match(self::DST_FILE, $line, $m)) { 294 throw new \RuntimeException('Invalid Diff file'); 295 } 296 297 // Set the destination file 298 $dst = $m[1]; 299 300 // Advance to the next line 301 if (next($lines) === false) { 302 throw new \RuntimeException('Unexpected EOF'); 303 } 304 305 return true; 306 } 307 308 /** 309 * Find the next hunk of difference 310 * 311 * The internal array pointer of $lines is on the next line after the finding 312 * 313 * @param array $lines The udiff array of lines 314 * @param string $srcLine The beginning of the patch for the source file 315 * @param string $srcSize The size of the patch for the source file 316 * @param string $dstLine The beginning of the patch for the destination file 317 * @param string $dstSize The size of the patch for the destination file 318 * 319 * @return boolean TRUE in case of success, false in case of failure 320 * 321 * @since 3.0.0 322 * @throws \RuntimeException 323 */ 324 protected static function findHunk(&$lines, &$srcLine, &$srcSize, &$dstLine, &$dstSize) 325 { 326 $line = current($lines); 327 328 if (preg_match(self::HUNK, $line, $m)) { 329 $srcLine = (int) $m[1]; 330 331 $srcSize = 1; 332 333 if ($m[3] !== '') { 334 $srcSize = (int) $m[3]; 335 } 336 337 $dstLine = (int) $m[4]; 338 339 $dstSize = 1; 340 341 if ($m[6] !== '') { 342 $dstSize = (int) $m[6]; 343 } 344 345 if (next($lines) === false) { 346 throw new \RuntimeException('Unexpected EOF'); 347 } 348 349 return true; 350 } 351 352 return false; 353 } 354 355 /** 356 * Apply the patch 357 * 358 * @param array $lines The udiff array of lines 359 * @param string $src The source file 360 * @param string $dst The destination file 361 * @param string $srcLine The beginning of the patch for the source file 362 * @param string $srcSize The size of the patch for the source file 363 * @param string $dstLine The beginning of the patch for the destination file 364 * @param string $dstSize The size of the patch for the destination file 365 * 366 * @return void 367 * 368 * @since 3.0.0 369 * @throws \RuntimeException 370 */ 371 protected function applyHunk(&$lines, $src, $dst, $srcLine, $srcSize, $dstLine, $dstSize) 372 { 373 $srcLine--; 374 $dstLine--; 375 $line = current($lines); 376 377 // Source lines (old file) 378 $source = array(); 379 380 // New lines (new file) 381 $destin = array(); 382 $src_left = $srcSize; 383 $dst_left = $dstSize; 384 385 do { 386 if (!isset($line[0])) { 387 $source[] = ''; 388 $destin[] = ''; 389 $src_left--; 390 $dst_left--; 391 } elseif ($line[0] == '-') { 392 if ($src_left == 0) { 393 throw new \RuntimeException(Text::sprintf('JLIB_FILESYSTEM_PATCHER_UNEXPECTED_REMOVE_LINE', key($lines))); 394 } 395 396 $source[] = substr($line, 1); 397 $src_left--; 398 } elseif ($line[0] == '+') { 399 if ($dst_left == 0) { 400 throw new \RuntimeException(Text::sprintf('JLIB_FILESYSTEM_PATCHER_UNEXPECTED_ADD_LINE', key($lines))); 401 } 402 403 $destin[] = substr($line, 1); 404 $dst_left--; 405 } elseif ($line != '\\ No newline at end of file') { 406 $line = substr($line, 1); 407 $source[] = $line; 408 $destin[] = $line; 409 $src_left--; 410 $dst_left--; 411 } 412 413 if ($src_left == 0 && $dst_left == 0) { 414 // Now apply the patch, finally! 415 if ($srcSize > 0) { 416 $src_lines = & $this->getSource($src); 417 418 if (!isset($src_lines)) { 419 throw new \RuntimeException( 420 Text::sprintf( 421 'JLIB_FILESYSTEM_PATCHER_UNEXISTING_SOURCE', 422 Path::removeRoot($src) 423 ) 424 ); 425 } 426 } 427 428 if ($dstSize > 0) { 429 if ($srcSize > 0) { 430 $dst_lines = & $this->getDestination($dst, $src); 431 $src_bottom = $srcLine + \count($source); 432 433 for ($l = $srcLine; $l < $src_bottom; $l++) { 434 if ($src_lines[$l] != $source[$l - $srcLine]) { 435 throw new \RuntimeException( 436 Text::sprintf( 437 'JLIB_FILESYSTEM_PATCHER_FAILED_VERIFY', 438 Path::removeRoot($src), 439 $l 440 ) 441 ); 442 } 443 } 444 445 array_splice($dst_lines, $dstLine, \count($source), $destin); 446 } else { 447 $this->destinations[$dst] = $destin; 448 } 449 } else { 450 $this->removals[] = $src; 451 } 452 453 next($lines); 454 455 return; 456 } 457 458 $line = next($lines); 459 } while ($line !== false); 460 throw new \RuntimeException('Unexpected EOF'); 461 } 462 463 /** 464 * Get the lines of a source file 465 * 466 * @param string $src The path of a file 467 * 468 * @return array The lines of the source file 469 * 470 * @since 3.0.0 471 */ 472 protected function &getSource($src) 473 { 474 if (!isset($this->sources[$src])) { 475 $this->sources[$src] = null; 476 477 if (is_readable($src)) { 478 $this->sources[$src] = self::splitLines(file_get_contents($src)); 479 } 480 } 481 482 return $this->sources[$src]; 483 } 484 485 /** 486 * Get the lines of a destination file 487 * 488 * @param string $dst The path of a destination file 489 * @param string $src The path of a source file 490 * 491 * @return array The lines of the destination file 492 * 493 * @since 3.0.0 494 */ 495 protected function &getDestination($dst, $src) 496 { 497 if (!isset($this->destinations[$dst])) { 498 $this->destinations[$dst] = $this->getSource($src); 499 } 500 501 return $this->destinations[$dst]; 502 } 503 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |