[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 namespace Defuse\Crypto; 4 5 use Defuse\Crypto\Exception as Ex; 6 7 final class File 8 { 9 /** 10 * Encrypts the input file, saving the ciphertext to the output file. 11 * 12 * @param string $inputFilename 13 * @param string $outputFilename 14 * @param Key $key 15 * @return void 16 * 17 * @throws Ex\EnvironmentIsBrokenException 18 * @throws Ex\IOException 19 */ 20 public static function encryptFile($inputFilename, $outputFilename, Key $key) 21 { 22 self::encryptFileInternal( 23 $inputFilename, 24 $outputFilename, 25 KeyOrPassword::createFromKey($key) 26 ); 27 } 28 29 /** 30 * Encrypts a file with a password, using a slow key derivation function to 31 * make password cracking more expensive. 32 * 33 * @param string $inputFilename 34 * @param string $outputFilename 35 * @param string $password 36 * @return void 37 * 38 * @throws Ex\EnvironmentIsBrokenException 39 * @throws Ex\IOException 40 */ 41 public static function encryptFileWithPassword($inputFilename, $outputFilename, $password) 42 { 43 self::encryptFileInternal( 44 $inputFilename, 45 $outputFilename, 46 KeyOrPassword::createFromPassword($password) 47 ); 48 } 49 50 /** 51 * Decrypts the input file, saving the plaintext to the output file. 52 * 53 * @param string $inputFilename 54 * @param string $outputFilename 55 * @param Key $key 56 * @return void 57 * 58 * @throws Ex\EnvironmentIsBrokenException 59 * @throws Ex\IOException 60 * @throws Ex\WrongKeyOrModifiedCiphertextException 61 */ 62 public static function decryptFile($inputFilename, $outputFilename, Key $key) 63 { 64 self::decryptFileInternal( 65 $inputFilename, 66 $outputFilename, 67 KeyOrPassword::createFromKey($key) 68 ); 69 } 70 71 /** 72 * Decrypts a file with a password, using a slow key derivation function to 73 * make password cracking more expensive. 74 * 75 * @param string $inputFilename 76 * @param string $outputFilename 77 * @param string $password 78 * @return void 79 * 80 * @throws Ex\EnvironmentIsBrokenException 81 * @throws Ex\IOException 82 * @throws Ex\WrongKeyOrModifiedCiphertextException 83 */ 84 public static function decryptFileWithPassword($inputFilename, $outputFilename, $password) 85 { 86 self::decryptFileInternal( 87 $inputFilename, 88 $outputFilename, 89 KeyOrPassword::createFromPassword($password) 90 ); 91 } 92 93 /** 94 * Takes two resource handles and encrypts the contents of the first, 95 * writing the ciphertext into the second. 96 * 97 * @param resource $inputHandle 98 * @param resource $outputHandle 99 * @param Key $key 100 * @return void 101 * 102 * @throws Ex\EnvironmentIsBrokenException 103 * @throws Ex\WrongKeyOrModifiedCiphertextException 104 */ 105 public static function encryptResource($inputHandle, $outputHandle, Key $key) 106 { 107 self::encryptResourceInternal( 108 $inputHandle, 109 $outputHandle, 110 KeyOrPassword::createFromKey($key) 111 ); 112 } 113 114 /** 115 * Encrypts the contents of one resource handle into another with a 116 * password, using a slow key derivation function to make password cracking 117 * more expensive. 118 * 119 * @param resource $inputHandle 120 * @param resource $outputHandle 121 * @param string $password 122 * @return void 123 * 124 * @throws Ex\EnvironmentIsBrokenException 125 * @throws Ex\IOException 126 * @throws Ex\WrongKeyOrModifiedCiphertextException 127 */ 128 public static function encryptResourceWithPassword($inputHandle, $outputHandle, $password) 129 { 130 self::encryptResourceInternal( 131 $inputHandle, 132 $outputHandle, 133 KeyOrPassword::createFromPassword($password) 134 ); 135 } 136 137 /** 138 * Takes two resource handles and decrypts the contents of the first, 139 * writing the plaintext into the second. 140 * 141 * @param resource $inputHandle 142 * @param resource $outputHandle 143 * @param Key $key 144 * @return void 145 * 146 * @throws Ex\EnvironmentIsBrokenException 147 * @throws Ex\IOException 148 * @throws Ex\WrongKeyOrModifiedCiphertextException 149 */ 150 public static function decryptResource($inputHandle, $outputHandle, Key $key) 151 { 152 self::decryptResourceInternal( 153 $inputHandle, 154 $outputHandle, 155 KeyOrPassword::createFromKey($key) 156 ); 157 } 158 159 /** 160 * Decrypts the contents of one resource into another with a password, using 161 * a slow key derivation function to make password cracking more expensive. 162 * 163 * @param resource $inputHandle 164 * @param resource $outputHandle 165 * @param string $password 166 * @return void 167 * 168 * @throws Ex\EnvironmentIsBrokenException 169 * @throws Ex\IOException 170 * @throws Ex\WrongKeyOrModifiedCiphertextException 171 */ 172 public static function decryptResourceWithPassword($inputHandle, $outputHandle, $password) 173 { 174 self::decryptResourceInternal( 175 $inputHandle, 176 $outputHandle, 177 KeyOrPassword::createFromPassword($password) 178 ); 179 } 180 181 /** 182 * Encrypts a file with either a key or a password. 183 * 184 * @param string $inputFilename 185 * @param string $outputFilename 186 * @param KeyOrPassword $secret 187 * @return void 188 * 189 * @throws Ex\CryptoException 190 * @throws Ex\IOException 191 */ 192 private static function encryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret) 193 { 194 if (file_exists($inputFilename) && file_exists($outputFilename) && realpath($inputFilename) === realpath($outputFilename)) { 195 throw new Ex\IOException('Input and output filenames must be different.'); 196 } 197 198 /* Open the input file. */ 199 $if = @\fopen($inputFilename, 'rb'); 200 if ($if === false) { 201 throw new Ex\IOException( 202 'Cannot open input file for encrypting: ' . 203 self::getLastErrorMessage() 204 ); 205 } 206 if (\is_callable('\\stream_set_read_buffer')) { 207 /* This call can fail, but the only consequence is performance. */ 208 \stream_set_read_buffer($if, 0); 209 } 210 211 /* Open the output file. */ 212 $of = @\fopen($outputFilename, 'wb'); 213 if ($of === false) { 214 \fclose($if); 215 throw new Ex\IOException( 216 'Cannot open output file for encrypting: ' . 217 self::getLastErrorMessage() 218 ); 219 } 220 if (\is_callable('\\stream_set_write_buffer')) { 221 /* This call can fail, but the only consequence is performance. */ 222 \stream_set_write_buffer($of, 0); 223 } 224 225 /* Perform the encryption. */ 226 try { 227 self::encryptResourceInternal($if, $of, $secret); 228 } catch (Ex\CryptoException $ex) { 229 \fclose($if); 230 \fclose($of); 231 throw $ex; 232 } 233 234 /* Close the input file. */ 235 if (\fclose($if) === false) { 236 \fclose($of); 237 throw new Ex\IOException( 238 'Cannot close input file after encrypting' 239 ); 240 } 241 242 /* Close the output file. */ 243 if (\fclose($of) === false) { 244 throw new Ex\IOException( 245 'Cannot close output file after encrypting' 246 ); 247 } 248 } 249 250 /** 251 * Decrypts a file with either a key or a password. 252 * 253 * @param string $inputFilename 254 * @param string $outputFilename 255 * @param KeyOrPassword $secret 256 * @return void 257 * 258 * @throws Ex\CryptoException 259 * @throws Ex\IOException 260 */ 261 private static function decryptFileInternal($inputFilename, $outputFilename, KeyOrPassword $secret) 262 { 263 if (file_exists($inputFilename) && file_exists($outputFilename) && realpath($inputFilename) === realpath($outputFilename)) { 264 throw new Ex\IOException('Input and output filenames must be different.'); 265 } 266 267 /* Open the input file. */ 268 $if = @\fopen($inputFilename, 'rb'); 269 if ($if === false) { 270 throw new Ex\IOException( 271 'Cannot open input file for decrypting: ' . 272 self::getLastErrorMessage() 273 ); 274 } 275 276 if (\is_callable('\\stream_set_read_buffer')) { 277 /* This call can fail, but the only consequence is performance. */ 278 \stream_set_read_buffer($if, 0); 279 } 280 281 /* Open the output file. */ 282 $of = @\fopen($outputFilename, 'wb'); 283 if ($of === false) { 284 \fclose($if); 285 throw new Ex\IOException( 286 'Cannot open output file for decrypting: ' . 287 self::getLastErrorMessage() 288 ); 289 } 290 291 if (\is_callable('\\stream_set_write_buffer')) { 292 /* This call can fail, but the only consequence is performance. */ 293 \stream_set_write_buffer($of, 0); 294 } 295 296 /* Perform the decryption. */ 297 try { 298 self::decryptResourceInternal($if, $of, $secret); 299 } catch (Ex\CryptoException $ex) { 300 \fclose($if); 301 \fclose($of); 302 throw $ex; 303 } 304 305 /* Close the input file. */ 306 if (\fclose($if) === false) { 307 \fclose($of); 308 throw new Ex\IOException( 309 'Cannot close input file after decrypting' 310 ); 311 } 312 313 /* Close the output file. */ 314 if (\fclose($of) === false) { 315 throw new Ex\IOException( 316 'Cannot close output file after decrypting' 317 ); 318 } 319 } 320 321 /** 322 * Encrypts a resource with either a key or a password. 323 * 324 * @param resource $inputHandle 325 * @param resource $outputHandle 326 * @param KeyOrPassword $secret 327 * @return void 328 * 329 * @throws Ex\EnvironmentIsBrokenException 330 * @throws Ex\IOException 331 * @psalm-suppress PossiblyInvalidArgument 332 * Fixes erroneous errors caused by PHP 7.2 switching the return value 333 * of hash_init from a resource to a HashContext. 334 */ 335 private static function encryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret) 336 { 337 if (! \is_resource($inputHandle)) { 338 throw new Ex\IOException( 339 'Input handle must be a resource!' 340 ); 341 } 342 if (! \is_resource($outputHandle)) { 343 throw new Ex\IOException( 344 'Output handle must be a resource!' 345 ); 346 } 347 348 $inputStat = \fstat($inputHandle); 349 $inputSize = $inputStat['size']; 350 351 $file_salt = Core::secureRandom(Core::SALT_BYTE_SIZE); 352 $keys = $secret->deriveKeys($file_salt); 353 $ekey = $keys->getEncryptionKey(); 354 $akey = $keys->getAuthenticationKey(); 355 356 $ivsize = Core::BLOCK_BYTE_SIZE; 357 $iv = Core::secureRandom($ivsize); 358 359 /* Initialize a streaming HMAC state. */ 360 /** @var mixed $hmac */ 361 $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey); 362 Core::ensureTrue( 363 \is_resource($hmac) || \is_object($hmac), 364 'Cannot initialize a hash context' 365 ); 366 367 /* Write the header, salt, and IV. */ 368 self::writeBytes( 369 $outputHandle, 370 Core::CURRENT_VERSION . $file_salt . $iv, 371 Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE + $ivsize 372 ); 373 374 /* Add the header, salt, and IV to the HMAC. */ 375 \hash_update($hmac, Core::CURRENT_VERSION); 376 \hash_update($hmac, $file_salt); 377 \hash_update($hmac, $iv); 378 379 /* $thisIv will be incremented after each call to the encryption. */ 380 $thisIv = $iv; 381 382 /* How many blocks do we encrypt at a time? We increment by this value. */ 383 /** 384 * @psalm-suppress RedundantCast 385 */ 386 $inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE); 387 388 /* Loop until we reach the end of the input file. */ 389 $at_file_end = false; 390 while (! (\feof($inputHandle) || $at_file_end)) { 391 /* Find out if we can read a full buffer, or only a partial one. */ 392 /** @var int */ 393 $pos = \ftell($inputHandle); 394 if (!\is_int($pos)) { 395 throw new Ex\IOException( 396 'Could not get current position in input file during encryption' 397 ); 398 } 399 if ($pos + Core::BUFFER_BYTE_SIZE >= $inputSize) { 400 /* We're at the end of the file, so we need to break out of the loop. */ 401 $at_file_end = true; 402 $read = self::readBytes( 403 $inputHandle, 404 $inputSize - $pos 405 ); 406 } else { 407 $read = self::readBytes( 408 $inputHandle, 409 Core::BUFFER_BYTE_SIZE 410 ); 411 } 412 413 /* Encrypt this buffer. */ 414 /** @var string */ 415 $encrypted = \openssl_encrypt( 416 $read, 417 Core::CIPHER_METHOD, 418 $ekey, 419 OPENSSL_RAW_DATA, 420 $thisIv 421 ); 422 423 Core::ensureTrue(\is_string($encrypted), 'OpenSSL encryption error'); 424 425 /* Write this buffer's ciphertext. */ 426 self::writeBytes($outputHandle, $encrypted, Core::ourStrlen($encrypted)); 427 /* Add this buffer's ciphertext to the HMAC. */ 428 \hash_update($hmac, $encrypted); 429 430 /* Increment the counter by the number of blocks in a buffer. */ 431 $thisIv = Core::incrementCounter($thisIv, $inc); 432 /* WARNING: Usually, unless the file is a multiple of the buffer 433 * size, $thisIv will contain an incorrect value here on the last 434 * iteration of this loop. */ 435 } 436 437 /* Get the HMAC and append it to the ciphertext. */ 438 $final_mac = \hash_final($hmac, true); 439 self::writeBytes($outputHandle, $final_mac, Core::MAC_BYTE_SIZE); 440 } 441 442 /** 443 * Decrypts a file-backed resource with either a key or a password. 444 * 445 * @param resource $inputHandle 446 * @param resource $outputHandle 447 * @param KeyOrPassword $secret 448 * @return void 449 * 450 * @throws Ex\EnvironmentIsBrokenException 451 * @throws Ex\IOException 452 * @throws Ex\WrongKeyOrModifiedCiphertextException 453 * @psalm-suppress PossiblyInvalidArgument 454 * Fixes erroneous errors caused by PHP 7.2 switching the return value 455 * of hash_init from a resource to a HashContext. 456 */ 457 public static function decryptResourceInternal($inputHandle, $outputHandle, KeyOrPassword $secret) 458 { 459 if (! \is_resource($inputHandle)) { 460 throw new Ex\IOException( 461 'Input handle must be a resource!' 462 ); 463 } 464 if (! \is_resource($outputHandle)) { 465 throw new Ex\IOException( 466 'Output handle must be a resource!' 467 ); 468 } 469 470 /* Make sure the file is big enough for all the reads we need to do. */ 471 $stat = \fstat($inputHandle); 472 if ($stat['size'] < Core::MINIMUM_CIPHERTEXT_SIZE) { 473 throw new Ex\WrongKeyOrModifiedCiphertextException( 474 'Input file is too small to have been created by this library.' 475 ); 476 } 477 478 /* Check the version header. */ 479 $header = self::readBytes($inputHandle, Core::HEADER_VERSION_SIZE); 480 if ($header !== Core::CURRENT_VERSION) { 481 throw new Ex\WrongKeyOrModifiedCiphertextException( 482 'Bad version header.' 483 ); 484 } 485 486 /* Get the salt. */ 487 $file_salt = self::readBytes($inputHandle, Core::SALT_BYTE_SIZE); 488 489 /* Get the IV. */ 490 $ivsize = Core::BLOCK_BYTE_SIZE; 491 $iv = self::readBytes($inputHandle, $ivsize); 492 493 /* Derive the authentication and encryption keys. */ 494 $keys = $secret->deriveKeys($file_salt); 495 $ekey = $keys->getEncryptionKey(); 496 $akey = $keys->getAuthenticationKey(); 497 498 /* We'll store the MAC of each buffer-sized chunk as we verify the 499 * actual MAC, so that we can check them again when decrypting. */ 500 $macs = []; 501 502 /* $thisIv will be incremented after each call to the decryption. */ 503 $thisIv = $iv; 504 505 /* How many blocks do we encrypt at a time? We increment by this value. */ 506 /** 507 * @psalm-suppress RedundantCast 508 */ 509 $inc = (int) (Core::BUFFER_BYTE_SIZE / Core::BLOCK_BYTE_SIZE); 510 511 /* Get the HMAC. */ 512 if (\fseek($inputHandle, (-1 * Core::MAC_BYTE_SIZE), SEEK_END) === -1) { 513 throw new Ex\IOException( 514 'Cannot seek to beginning of MAC within input file' 515 ); 516 } 517 518 /* Get the position of the last byte in the actual ciphertext. */ 519 /** @var int $cipher_end */ 520 $cipher_end = \ftell($inputHandle); 521 if (!\is_int($cipher_end)) { 522 throw new Ex\IOException( 523 'Cannot read input file' 524 ); 525 } 526 /* We have the position of the first byte of the HMAC. Go back by one. */ 527 --$cipher_end; 528 529 /* Read the HMAC. */ 530 /** @var string $stored_mac */ 531 $stored_mac = self::readBytes($inputHandle, Core::MAC_BYTE_SIZE); 532 533 /* Initialize a streaming HMAC state. */ 534 /** @var mixed $hmac */ 535 $hmac = \hash_init(Core::HASH_FUNCTION_NAME, HASH_HMAC, $akey); 536 Core::ensureTrue(\is_resource($hmac) || \is_object($hmac), 'Cannot initialize a hash context'); 537 538 /* Reset file pointer to the beginning of the file after the header */ 539 if (\fseek($inputHandle, Core::HEADER_VERSION_SIZE, SEEK_SET) === -1) { 540 throw new Ex\IOException( 541 'Cannot read seek within input file' 542 ); 543 } 544 545 /* Seek to the start of the actual ciphertext. */ 546 if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize, SEEK_CUR) === -1) { 547 throw new Ex\IOException( 548 'Cannot seek input file to beginning of ciphertext' 549 ); 550 } 551 552 /* PASS #1: Calculating the HMAC. */ 553 554 \hash_update($hmac, $header); 555 \hash_update($hmac, $file_salt); 556 \hash_update($hmac, $iv); 557 /** @var mixed $hmac2 */ 558 $hmac2 = \hash_copy($hmac); 559 560 $break = false; 561 while (! $break) { 562 /** @var int $pos */ 563 $pos = \ftell($inputHandle); 564 if (!\is_int($pos)) { 565 throw new Ex\IOException( 566 'Could not get current position in input file during decryption' 567 ); 568 } 569 570 /* Read the next buffer-sized chunk (or less). */ 571 if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) { 572 $break = true; 573 $read = self::readBytes( 574 $inputHandle, 575 $cipher_end - $pos + 1 576 ); 577 } else { 578 $read = self::readBytes( 579 $inputHandle, 580 Core::BUFFER_BYTE_SIZE 581 ); 582 } 583 584 /* Update the HMAC. */ 585 \hash_update($hmac, $read); 586 587 /* Remember this buffer-sized chunk's HMAC. */ 588 /** @var mixed $chunk_mac */ 589 $chunk_mac = \hash_copy($hmac); 590 Core::ensureTrue(\is_resource($chunk_mac) || \is_object($chunk_mac), 'Cannot duplicate a hash context'); 591 $macs []= \hash_final($chunk_mac); 592 } 593 594 /* Get the final HMAC, which should match the stored one. */ 595 /** @var string $final_mac */ 596 $final_mac = \hash_final($hmac, true); 597 598 /* Verify the HMAC. */ 599 if (! Core::hashEquals($final_mac, $stored_mac)) { 600 throw new Ex\WrongKeyOrModifiedCiphertextException( 601 'Integrity check failed.' 602 ); 603 } 604 605 /* PASS #2: Decrypt and write output. */ 606 607 /* Rewind to the start of the actual ciphertext. */ 608 if (\fseek($inputHandle, Core::SALT_BYTE_SIZE + $ivsize + Core::HEADER_VERSION_SIZE, SEEK_SET) === -1) { 609 throw new Ex\IOException( 610 'Could not move the input file pointer during decryption' 611 ); 612 } 613 614 $at_file_end = false; 615 while (! $at_file_end) { 616 /** @var int $pos */ 617 $pos = \ftell($inputHandle); 618 if (!\is_int($pos)) { 619 throw new Ex\IOException( 620 'Could not get current position in input file during decryption' 621 ); 622 } 623 624 /* Read the next buffer-sized chunk (or less). */ 625 if ($pos + Core::BUFFER_BYTE_SIZE >= $cipher_end) { 626 $at_file_end = true; 627 $read = self::readBytes( 628 $inputHandle, 629 $cipher_end - $pos + 1 630 ); 631 } else { 632 $read = self::readBytes( 633 $inputHandle, 634 Core::BUFFER_BYTE_SIZE 635 ); 636 } 637 638 /* Recalculate the MAC (so far) and compare it with the one we 639 * remembered from pass #1 to ensure attackers didn't change the 640 * ciphertext after MAC verification. */ 641 \hash_update($hmac2, $read); 642 /** @var mixed $calc_mac */ 643 $calc_mac = \hash_copy($hmac2); 644 Core::ensureTrue(\is_resource($calc_mac) || \is_object($calc_mac), 'Cannot duplicate a hash context'); 645 $calc = \hash_final($calc_mac); 646 647 if (empty($macs)) { 648 throw new Ex\WrongKeyOrModifiedCiphertextException( 649 'File was modified after MAC verification' 650 ); 651 } elseif (! Core::hashEquals(\array_shift($macs), $calc)) { 652 throw new Ex\WrongKeyOrModifiedCiphertextException( 653 'File was modified after MAC verification' 654 ); 655 } 656 657 /* Decrypt this buffer-sized chunk. */ 658 /** @var string $decrypted */ 659 $decrypted = \openssl_decrypt( 660 $read, 661 Core::CIPHER_METHOD, 662 $ekey, 663 OPENSSL_RAW_DATA, 664 $thisIv 665 ); 666 Core::ensureTrue(\is_string($decrypted), 'OpenSSL decryption error'); 667 668 /* Write the plaintext to the output file. */ 669 self::writeBytes( 670 $outputHandle, 671 $decrypted, 672 Core::ourStrlen($decrypted) 673 ); 674 675 /* Increment the IV by the amount of blocks in a buffer. */ 676 /** @var string $thisIv */ 677 $thisIv = Core::incrementCounter($thisIv, $inc); 678 /* WARNING: Usually, unless the file is a multiple of the buffer 679 * size, $thisIv will contain an incorrect value here on the last 680 * iteration of this loop. */ 681 } 682 } 683 684 /** 685 * Read from a stream; prevent partial reads. 686 * 687 * @param resource $stream 688 * @param int $num_bytes 689 * @return string 690 * 691 * @throws Ex\IOException 692 * @throws Ex\EnvironmentIsBrokenException 693 */ 694 public static function readBytes($stream, $num_bytes) 695 { 696 Core::ensureTrue($num_bytes >= 0, 'Tried to read less than 0 bytes'); 697 698 if ($num_bytes === 0) { 699 return ''; 700 } 701 702 $buf = ''; 703 $remaining = $num_bytes; 704 while ($remaining > 0 && ! \feof($stream)) { 705 /** @var string $read */ 706 $read = \fread($stream, $remaining); 707 if (!\is_string($read)) { 708 throw new Ex\IOException( 709 'Could not read from the file' 710 ); 711 } 712 $buf .= $read; 713 $remaining -= Core::ourStrlen($read); 714 } 715 if (Core::ourStrlen($buf) !== $num_bytes) { 716 throw new Ex\IOException( 717 'Tried to read past the end of the file' 718 ); 719 } 720 return $buf; 721 } 722 723 /** 724 * Write to a stream; prevents partial writes. 725 * 726 * @param resource $stream 727 * @param string $buf 728 * @param int $num_bytes 729 * @return int 730 * 731 * @throws Ex\IOException 732 */ 733 public static function writeBytes($stream, $buf, $num_bytes = null) 734 { 735 $bufSize = Core::ourStrlen($buf); 736 if ($num_bytes === null) { 737 $num_bytes = $bufSize; 738 } 739 if ($num_bytes > $bufSize) { 740 throw new Ex\IOException( 741 'Trying to write more bytes than the buffer contains.' 742 ); 743 } 744 if ($num_bytes < 0) { 745 throw new Ex\IOException( 746 'Tried to write less than 0 bytes' 747 ); 748 } 749 $remaining = $num_bytes; 750 while ($remaining > 0) { 751 /** @var int $written */ 752 $written = \fwrite($stream, $buf, $remaining); 753 if (!\is_int($written)) { 754 throw new Ex\IOException( 755 'Could not write to the file' 756 ); 757 } 758 $buf = (string) Core::ourSubstr($buf, $written, null); 759 $remaining -= $written; 760 } 761 return $num_bytes; 762 } 763 764 /** 765 * Returns the last PHP error's or warning's message string. 766 * 767 * @return string 768 */ 769 private static function getLastErrorMessage() 770 { 771 $error = error_get_last(); 772 if ($error === null) { 773 return '[no PHP error]'; 774 } else { 775 return $error['message']; 776 } 777 } 778 }
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 |