8000 Throw an exception on error, introduce error domains to handle them safely by adamziel · Pull Request #124 · WordPress/php-toolkit · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Throw 8000 an exception on error, introduce error domains to handle them safely #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/WordPress/ByteReader/WP_Byte_Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,12 @@ abstract public function seek( int $offset ): bool;
abstract public function is_finished(): bool;
abstract public function next_bytes(): bool;
abstract public function get_bytes(): ?string;
abstract public function get_last_error(): ?string;
abstract public function close(): bool;
public function read_all(): string {
$buffer = '';
while( $this->next_bytes() ) {
$buffer .= $this->get_bytes();
}
if( $this->get_last_error() ) {
return false;
}
return $buffer;
}
}
28 changes: 11 additions & 17 deletions src/WordPress/ByteReader/WP_File_Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace WordPress\ByteReader;

use WordPress\Error\WordPressException;

class WP_File_Reader extends WP_Byte_Reader {

const STATE_STREAMING = '#streaming';
Expand All @@ -13,19 +15,14 @@ class WP_File_Reader extends WP_Byte_Reader {
protected $offset_in_file;
protected $output_bytes = '';
protected $last_chunk_size = 0;
protected $last_error;
protected $state = self::STATE_STREAMING;

static public function create( $file_path, $chunk_size = 8096 ) {
if(!file_exists($file_path)) {
throw new \Exception(sprintf( 'File %s does not exist', $file_path ));
_doing_it_wrong( __METHOD__, sprintf( 'File %s does not exist', $file_path ), '1.0.0' );
return false;
throw new WordPressException(sprintf( 'File %s does not exist', $file_path ));
}
if(!is_file($file_path)) {
throw new \Exception(sprintf( '%s is not a file', $file_path ));
_doing_it_wrong( __METHOD__, sprintf( '%s is not a file', $file_path ), '1.0.0' );
return false;
throw new WordPressException(sprintf( '%s is not a file', $file_path ));
}
return new self( $file_path, $chunk_size );
}
Expand All @@ -48,8 +45,7 @@ public function tell(): int {

public function seek( $offset_in_file ): bool {
if ( ! is_int( $offset_in_file ) ) {
_doing_it_wrong( __METHOD__, 'Cannot set a file reader cursor to a non-integer offset.', '1.0.0' );
return false;
throw new WordPressException('Cannot set a file reader cursor to a non-integer offset.');
}
$this->offset_in_file = $offset_in_file;
$this->last_chunk_size = 0;
Expand All @@ -64,11 +60,10 @@ public function seek( $offset_in_file ): bool {

public function close(): bool {
if(!$this->file_pointer) {
return false;
throw new WordPressException('File pointer is not open');
}
if(!fclose($this->file_pointer)) {
$this->last_error = 'Failed to close file pointer';
return false;
throw new WordPressException('Failed to close file pointer');
}
$this->file_pointer = null;
$this->state = static::STATE_FINISHED;
Expand All @@ -83,18 +78,17 @@ public function get_bytes(): string {
return $this->output_bytes;
}

public function get_last_error(): ?string {
return $this->last_error;
}

public function next_bytes(): bool {
$this->output_bytes = '';
$this->last_chunk_size = 0;
if ( $this->last_error || $this->is_finished() ) {
if ( $this->is_finished() ) {
return false;
}
if ( ! $this->file_pointer ) {
$this->file_pointer = fopen( $this->file_path, 'r' );
if(false === $this->file_pointer) {
throw new WordPressException(sprintf('Failed to open the file: %s', $this->file_path));
}
if ( $this->offset_in_file ) {
fseek( $this->file_pointer, $this->offset_in_file );
}
Expand Down
2 changes: 1 addition & 1 deletion src/WordPress/ByteReader/WP_GZ_File_Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class WP_GZ_File_Reader extends WP_File_Reader {

public function next_bytes(): bool {
$this->output_bytes = '';
if ( $this->last_error || $this->is_finished() ) {
if ( $this->is_finished() ) {
return false;
}
if ( ! $this->file_pointer ) {
Expand Down
10 changes: 0 additions & 10 deletions src/WordPress/ByteReader/WP_Remote_File_Ranged_Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,6 @@ static private function redirect_output_to_disk( WP_Byte_Reader $reader ) {
fwrite($file, $reader->get_bytes());
}
fclose($file);
if($reader->get_last_error()) {
// How should we log this error?
return false;
}
return WP_File_Reader::create( $file_path );
}

Expand Down Expand Up @@ -185,12 +181,6 @@ public function get_bytes(): ?string {
return $this->current_reader->get_bytes();
}

public function get_last_error(): ?string {
// @TODO: Preserve the error information when the current reader
// is reset.
return $this->current_reader->get_last_error();
}

private function ensure_content_length() {
if ( null !== $this->remote_file_length ) {
return $this->remote_file_length;
Expand Down
37 changes: 9 additions & 28 deletions src/WordPress/ByteReader/WP_Remote_File_Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace WordPress\ByteReader;

use WordPress\Error\WordPressException;

/**
* Streams bytes from a remote file.
*/
Expand Down Expand Up @@ -33,13 +35,10 @@ public function tell(): int {

public function seek( $offset_in_file ): bool {
if ( $this->request ) {
_doing_it_wrong(
__METHOD__,
throw new WordPressException(
'Cannot seek() a WP_Remote_File_Reader instance once the request was initialized. ' .
'Use WP_Remote_File_Ranged_Reader to seek() using range requests instead.',
'1.0.0'
'Use WP_Remote_File_Ranged_Reader to seek() using range requests instead.'
);
return false;
}
$this->skip_bytes = $offset_in_file;
return true;
Expand All @@ -52,8 +51,7 @@ public function next_bytes(): bool {
array( 'headers' => $this->headers )
);
if ( false === $this->client->enqueue( $this->request ) ) {
// TODO: Think through error handling
return false;
throw new WordPressException(sprintf('Failed to enqueue the request to %s', $this->url));
}
}

Expand Down Expand Up @@ -85,8 +83,7 @@ public function next_bytes(): bool {
case \WordPress\AsyncHttp\Client::EVENT_BODY_CHUNK_AVAILABLE:
$chunk = $this->client->get_response_body_chunk();
if ( ! is_string( $chunk ) ) {
// TODO: Think through error handling
return false;
throw new WordPressException(sprintf('Failed to get the response body chunk from %s', $this->url));
}
$this->current_chunk = $chunk;

Expand All @@ -108,11 +105,7 @@ public function next_bytes(): bool {
}
return true;
case \WordPress\AsyncHttp\Client::EVENT_FAILED:
// TODO: Think through error handling. Errors are expected when working with
// the network. Should we auto retry? Make it easy for the caller to retry?
// Something else?
$this->last_error = $this->client->get_request()->error;
return false;
throw new WordPressException(sprintf('Failed to fetch data from %s', $this->url));
case \WordPress\AsyncHttp\Client::EVENT_FINISHED:
$this->is_finished = true;
return false;
Expand All @@ -130,23 +123,16 @@ public function length(): ?int {
array( 'method' => 'HEAD' )
);
if ( false === $this->client->enqueue( $request ) ) {
// TODO: Think through error handling
return false;
throw new WordPressException(sprintf('Failed to enqueue the request to %s', $this->url));
}
while ( $this->client->await_next_event() ) {
switch ( $this->client->get_event() ) {
case \WordPress\AsyncHttp\Client::EVENT_GOT_HEADERS:
$request = $this->client->get_request();
if ( ! $request ) {
return false;
}
if($request->redirected_to) {
continue 2;
}
$response = $request->response;
if ( false === $response ) {
return false;
}
$content_length = $response->get_header( 'Content-Length' );
if ( false === $content_length ) {
return false;
Expand Down Expand Up @@ -181,11 +167,6 @@ public function is_finished(): bool {
}

public function close(): bool {
_doing_it_wrong(
__METHOD__,
'Not implemented yet',
'1.0.0'
);
return false;
throw new WordPressException('Not implemented yet');
}
}
17 changes: 6 additions & 11 deletions src/WordPress/ByteReader/WP_String_Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace WordPress\ByteReader;

use WordPress\Error\WordPressException;

class WP_String_Reader extends WP_Byte_Reader {

const STATE_STREAMING = '#streaming';
Expand All @@ -12,13 +14,11 @@ class WP_String_Reader extends WP_Byte_Reader {
protected $offset = 0;
protected $output_bytes = '';
protected $last_chunk_size = 0;
protected $last_error;
protected $state = self::STATE_STREAMING;

static public function create($string, $chunk_size = 8096) {
if (!is_string($string)) {
_doing_it_wrong(__METHOD__, 'Input must be a string', '1.0.0');
return false;
throw new WordPressException('Input must be a string');
}
return new self($string, $chunk_size);
}
Expand All @@ -38,11 +38,10 @@ public function tell(): int {

public function seek($offset): bool {
if (!is_int($offset)) {
_doing_it_wrong(__METHOD__, 'Cannot set cursor to a non-integer offset.', '1.0.0');
return false;
throw new WordPressException('Cannot set cursor to a non-integer offset.');
}
if ($offset < 0 || $offset > strlen($this->string)) {
return false;
throw new WordPressException('Cannot set cursor to an offset outside the string bounds.');
}
$this->offset = $offset;
$this->last_chunk_size = 0;
Expand All @@ -63,15 +62,11 @@ public function get_bytes(): string {
return $this->output_bytes;
}

public function get_last_error(): ?string {
return $this->last_error;
}

public function next_bytes(): bool {
$this->output_bytes = '';
$this->last_chunk_size = 0;

if ($this->last_error || $this->is_finished()) {
if ($this->is_finished()) {
return false;
}

Expand Down
104 changes: 104 additions & 0 deletions src/WordPress/Error/ErrorDomain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace WordPress\Error;

use \WP_Error;

/**
* A Domain groups error handling into a container, similar to Node.js domains.
* It allows grouping multiple different operations into one error handling context.
*/
class ErrorDomain {

/**
* @var callable|null The error handler registered for this domain
*/
private $error_handler = null;

/**
* @var string|null A label for the domain, useful for debugging
*/
private $label = null;

/**
* @var callable|null A fallback fallback error handler
*/
private static $fallback_error_handler = null;

private $last_return_value = null;

/**
* Creates a new domain instance with an optional label.
*
* @param string|null $label A label for the domain
* @return self
*/
public static function create($label = null) {
$instance = new self();
$instance->label = $label;
return $instance;
}

/**
* Sets a fallback fallback error handler.
*
* @param callable $handler The fallback error handler function
* @return void
*/
public static function set_fallback_error_handler($handler) {
self::$fallback_error_handler = $handler;
}

public static function bail($exception) {
throw $exception;
}

/**
* Adds an error handler to this domain.
*
* @param callable $handler The error handler function
* @return void
*/
public function set_error_handler($handler) {
$this->error_handler = $handler;
}

/**
* Executes a callback within this domain's error handling context.
*
* @param callable $callback The code to execute
* @return bool True if the callback executed successfully, false otherwise
*/
public function safe_run(callable $callback) {
try {
$this->last_return_value = $callback($this);
return true;
} catch (\Throwable $exception) {
if ($this->error_handler) {
call_user_func($this->error_handler, $exception, $this);
} else {
call_user_func(self::$fallback_error_handler, $exception);
}
return false;
}
}

public function get_last_return_value() {
return $this->last_return_value;
}

/**
* Gets the label of the domain.
*
* @return string|null The label of the domain
*/
public function get_label() {
return $this->label;
}

}

// Set the default fallback error handler
ErrorDomain::set_fallback_error_handler(function($exception) {
error_log('fallback handler: ' . $exception->getMessage() . "\n" . $exception->getTraceAsString());
});
7 changes: 7 additions & 0 deletions src/WordPress/Error/WordPressException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace WordPress\Error;

class WordPressException extends \Exception {

}
Loading
Loading
0