protected function validateEnablingTwoFactorAuth(Request $request)
    {
        $input = $request->all();

        if (isset($input['phone_number'])) {
            $input['phone_number'] = str_replace(['-', '.'], '', $input['phone_number']);
        }

        $validator = Validator::make($input, [
            'country_code' => 'required|numeric|integer',
            'phone_number' => 'required|numeric',
        ]);

        if ($validator->fails()) {
            return response()->json($validator->errors()->all(), 422);
        }
    }

protected function validateEnablingTwoFactorAuth ( Request $ request ) { $ input = $ request -> all ( ) ; if ( isset ( $ input [ 'phone_number' ] ) ) { $ input [ 'phone_number' ] = str_replace ( [ '-' , '.' ] , '' , $ input [ 'phone_number' ] ) ; } $ validator = Validator :: make ( $ input , [ 'country_code' => 'required|numeric|integer' , 'phone_number' => 'required|numeric' , ] ) ; if ( $ validator -> fails ( ) ) { return response ( ) -> json ( $ validator -> errors ( ) -> all ( ) , 422 ) ; } }

Validate an incoming request to enable two-factor authentication.

@param  \Illuminate\Http\Request  $request
@return \Illuminate\Http\Response|null

Validate an incoming request to enable two - factor authentication .


public function disableTwoFactorAuth(Request $request)
    {
        Spark::twoFactorProvider()->delete(Auth::user());

        Auth::user()->save();

        return $this->users->getCurrentUser();
    }

public function disableTwoFactorAuth ( Request $ request ) { Spark :: twoFactorProvider ( ) -> delete ( Auth :: user ( ) ) ; Auth :: user ( ) -> save ( ) ; return $ this -> users -> getCurrentUser ( ) ; }

Disable two-factor authentication for the user.

@param  \Illuminate\Http\Request  $request
@return \Illuminate\Http\Response

Disable two - factor authentication for the user .


protected function authenticated(Request $request, Authenticatable $user)
    {
        if (Spark::supportsTwoFactorAuth() && Spark::twoFactorProvider()->isEnabled($user)) {
            return $this->logoutAndRedirectToTokenScreen($request, $user);
        }

        return redirect()->intended($this->redirectPath());
    }

protected function authenticated ( Request $ request , Authenticatable $ user ) { if ( Spark :: supportsTwoFactorAuth ( ) && Spark :: twoFactorProvider ( ) -> isEnabled ( $ user ) ) { return $ this -> logoutAndRedirectToTokenScreen ( $ request , $ user ) ; } return redirect ( ) -> intended ( $ this -> redirectPath ( ) ) ; }

Send the post-authentication response.

@param  \Illuminate\Http\Request  $request
@param  \Illuminate\Contracts\Auth\Authenticatable  $user
@return \Illuminate\Http\Response

Send the post - authentication response .


protected function logoutAndRedirectToTokenScreen(Request $request, Authenticatable $user)
    {
        Auth::logout();

        $request->session()->put('spark:auth:id', $user->id);

        return redirect('login/token');
    }

protected function logoutAndRedirectToTokenScreen ( Request $ request , Authenticatable $ user ) { Auth :: logout ( ) ; $ request -> session ( ) -> put ( 'spark:auth:id' , $ user -> id ) ; return redirect ( 'login/token' ) ; }

Generate a redirect response to the two-factor token screen.

@param  \Illuminate\Http\Request  $request
@param  \Illuminate\Contracts\Auth\Authenticatable  $users
@return \Illuminate\Http\Response

Generate a redirect response to the two - factor token screen .


public function postToken(Request $request)
    {
        $this->validate($request, ['token' => 'required']);

        if (! session('spark:auth:id')) {
            return redirect('login');
        }

        $model = config('auth.model');

        $user = (new $model)->findOrFail(
            $request->session()->pull('spark:auth:id')
        );

        if (Spark::twoFactorProvider()->tokenIsValid($user, $request->token)) {
            Auth::login($user);

            return redirect()->intended($this->redirectPath());
        } else {
            return back();
        }
    }

public function postToken ( Request $ request ) { $ this -> validate ( $ request , [ 'token' => 'required' ] ) ; if ( ! session ( 'spark:auth:id' ) ) { return redirect ( 'login' ) ; } $ model = config ( 'auth.model' ) ; $ user = ( new $ model ) -> findOrFail ( $ request -> session ( ) -> pull ( 'spark:auth:id' ) ) ; if ( Spark :: twoFactorProvider ( ) -> tokenIsValid ( $ user , $ request -> token ) ) { Auth :: login ( $ user ) ; return redirect ( ) -> intended ( $ this -> redirectPath ( ) ) ; } else { return back ( ) ; } }

Verify the two-factor authentication token.

@param  \Illuminate\Http\Request  $request
@return \Illuminate\Http\Response

Verify the two - factor authentication token .


public function getRegister(Request $request)
    {
        if (Spark::forcingPromotion() && ! $request->query('coupon')) {
            if (count($request->query()) > 0) {
                return redirect($request->fullUrl().'&coupon='.Spark::forcedPromotion());
            } else {
                return redirect($request->fullUrl().'?coupon='.Spark::forcedPromotion());
            }
        }

        if (count($this->plans->paid()) > 0) {
            return view('spark::auth.registration.subscription');
        } else {
            return view('spark::auth.registration.simple');
        }
    }

public function getRegister ( Request $ request ) { if ( Spark :: forcingPromotion ( ) && ! $ request -> query ( 'coupon' ) ) { if ( count ( $ request -> query ( ) ) > 0 ) { return redirect ( $ request -> fullUrl ( ) . '&coupon=' . Spark :: forcedPromotion ( ) ) ; } else { return redirect ( $ request -> fullUrl ( ) . '?coupon=' . Spark :: forcedPromotion ( ) ) ; } } if ( count ( $ this -> plans -> paid ( ) ) > 0 ) { return view ( 'spark::auth.registration.subscription' ) ; } else { return view ( 'spark::auth.registration.simple' ) ; } }

Show the application registration form.

@param  \Illuminate\Http\Request
@return \Illuminate\Http\Response

Show the application registration form .


protected function validateRegistration(Request $request, $withSubscription = false)
    {
        if (Spark::$validateProfileUpdatesWith) {
            $this->callCustomValidator(
                Spark::$validateProfileUpdatesWith, $request, [$withSubscription]
            );
        } else {
            $this->validateDefaultRegistration($request, $withSubscription);
        }
    }

protected function validateRegistration ( Request $ request , $ withSubscription = false ) { if ( Spark :: $ validateProfileUpdatesWith ) { $ this -> callCustomValidator ( Spark :: $ validateProfileUpdatesWith , $ request , [ $ withSubscription ] ) ; } else { $ this -> validateDefaultRegistration ( $ request , $ withSubscription ) ; } }

Validate the new registration.

@param  \Illuminate\Http\Request  $request
@param  bool  $withSubscription
@return void


protected function validateDefaultRegistration(Request $request, $withSubscription)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|confirmed|min:6',
            'terms' => 'required|accepted',
        ]);

        if ($withSubscription) {
            $validator->mergeRules('stripe_token', 'required');

            if ($request->coupon) {
                $validator->after(function ($validator) use ($request) {
                    $this->validateCoupon($validator, $request);
                });
            }
        }

        if ($validator->fails()) {
            $this->throwValidationException($request, $validator);
        }
    }

protected function validateDefaultRegistration ( Request $ request , $ withSubscription ) { $ validator = Validator :: make ( $ request -> all ( ) , [ 'name' => 'required|max:255' , 'email' => 'required|email|unique:users' , 'password' => 'required|confirmed|min:6' , 'terms' => 'required|accepted' , ] ) ; if ( $ withSubscription ) { $ validator -> mergeRules ( 'stripe_token' , 'required' ) ; if ( $ request -> coupon ) { $ validator -> after ( function ( $ validator ) use ( $ request ) { $ this -> validateCoupon ( $ validator , $ request ) ; } ) ; } } if ( $ validator -> fails ( ) ) { $ this -> throwValidationException ( $ request , $ validator ) ; } }

Validate a new registration using the default rules.

@param  \Illuminate\Http\Request  $request
@param  bool  $withSubscription
@return void

Validate a new registration using the default rules .


protected function validateCoupon($validator, Request $request)
    {
        try {
            $coupon = StripeCoupon::retrieve(
                $request->coupon, ['api_key' => config('services.stripe.secret')]
            );

            if ($coupon && $coupon->valid) {
                return;
            }
        } catch (Exception $e) {
            //
        }

        $validator->errors()->add('coupon', 'The provided coupon code is invalid.');
    }

protected
function
validateCoupon
(
$
validator
,
Request
$
request
)
{
try
{
$
coupon
=
StripeCoupon
::
retrieve
(
$
request
->
coupon
,
[
'api_key'
=>
config
(
'services.stripe.secret'
)
]
)
;
if
(
$
coupon
&&
$
coupon
->
valid
)
{
return
;
}
}
catch
(
Exception
$
e
)
{
//
}
$
validator
->
errors
(
)
->
add
(
'coupon'
,
'The provided coupon code is invalid.'
)
;
}

Validate that the provided coupon actually exists on Stripe.

@param  \Illuminate\Validation\Validator  $validator
@param  \Illuminate\Http\Request  $request
@return void

Validate that the provided coupon actually exists on Stripe .


public function getLogout(Request $request)
    {
        $request->session()->flush();

        Auth::logout();

        return redirect(property_exists($this, 'redirectAfterLogout') ? $this->redirectAfterLogout : '/');
    }

public function getLogout ( Request $ request ) { $ request -> session ( ) -> flush ( ) ; Auth :: logout ( ) ; return redirect ( property_exists ( $ this , 'redirectAfterLogout' ) ? $ this -> redirectAfterLogout : '/' ) ; }

Log the user out of the application.

@param  \Illuminate\Http\Request  $request
@return \Illuminate\Http\Response


public function flashMessage($message, $type = 'info')
	{
		return parent::flashMessage($this->translator->translate($message), $type);
	}

public function flashMessage ( $ message , $ type = 'info' ) { return parent :: flashMessage ( $ this -> translator -> translate ( $ message ) , $ type ) ; }

Flash zprava s podporou translatoru

@param $message
@param string $type
@return \stdClass


protected function _createEvent($name, $params = [], $target = null, $propagation = true)
    {
        return new Event($name, $params, $target, $propagation);
    }

protected function _createEvent ( $ name , $ params = [ ] , $ target = null , $ propagation = true ) { return new Event ( $ name , $ params , $ target , $ propagation ) ; }

Creates a new event instance.

@since [*next-version*]

@param string $name        The event name.
@param array  $params      The event parameters.
@param mixed  $target      The target object. Used for context.
@param bool   $propagation True to propagate the event, false to not.

@return EventInterface The new event.


function registerException(string $class, callable $exporter, callable $importer) {
        if(!\class_exists($class, true)) {
            throw new \InvalidArgumentException('Given class does not exist');
        } elseif(!\in_array(\Throwable::class, \class_implements($class))) {
            throw new \InvalidArgumentException('Given class is not a Throwable');
        } elseif($exporter instanceof \Closure || $importer instanceof \Closure) {
            throw new \LogicException('Closures are not supported');
        }

        $this->exceptions[$class] = array(
            'export' => $exporter,
            'import' => $importer
        );

        return $this;
    }

function
registerException
(
string
$
class
,
callable
$
exporter
,
callable
$
importer
)
{
if
(
!
\
class_exists
(
$
class
,
true
)
)
{
throw
new
\
InvalidArgumentException
(
'Given class does not exist'
)
;
}
elseif
(
!
\
in_array
(
\
Throwable
::
class
,
\
class_implements
(
$
class
)
)
)
{
throw
new
\
InvalidArgumentException
(
'Given class is not a Throwable'
)
;
}
elseif
(
$
exporter
instanceof
\
Closure
||
$
importer
instanceof
\
Closure
)
{
throw
new
\
LogicException
(
'Closures are not supported'
)
;
}
$
this
->
exceptions
[
$
class
]
=
array
(
'export'
=>
$
exporter
,
'import'
=>
$
importer
)
;
return
$
this
;
}

Registers an exception.
@param string    $class
@param callable  $exporter  MUST NOT be a closure.
@param callable  $importer  MUST NOT be a closure.
@return $this
@throws \InvalidArgumentException
@throws \LogicException


function exportException(\Throwable $error) {
        $class = \get_class($error);

        if(!isset($this->exceptions[$class])) {
            throw new \BadMethodCallException('Given exception "'.$class.'" is not registered');
        }

        $export = ((array) $this->exceptions[$class]['export'])($error);

        $export['class'] = $class;
        $export['phoebeExceptionHandler'] = true;

        return $export;
    }

function
exportException
(
\
Throwable
$
error
)
{
$
class
=
\
get_class
(
$
error
)
;
if
(
!
isset
(
$
this
->
exceptions
[
$
class
]
)
)
{
throw
new
\
BadMethodCallException
(
'Given exception "'
.
$
class
.
'" is not registered'
)
;
}
$
export
=
(
(
array
)
$
this
->
exceptions
[
$
class
]
[
'export'
]
)
(
$
error
)
;
$
export
[
'class'
]
=
$
class
;
$
export
[
'phoebeExceptionHandler'
]
=
true
;
return
$
export
;
}

Exports the exception.
@param \Throwable  $error
@return array
@throws \BadMethodCallException  Thrown if the exception class is not registered.


function importException(array $error) {
        $class = $error['class'] ?? null;

        if(!isset($this->exceptions[$class])) {
            throw new \BadMethodCallException('Given exception "'.$class.'" is not registered');
        }

        return ((array) $this->exceptions[$class]['import'])($error);
    }

function
importException
(
array
$
error
)
{
$
class
=
$
error
[
'class'
]
??
;
if
(
!
isset
(
$
this
->
exceptions
[
$
class
]
)
)
{
throw
new
\
BadMethodCallException
(
'Given exception "'
.
$
class
.
'" is not registered'
)
;
}
return
(
(
array
)
$
this
->
exceptions
[
$
class
]
[
'import'
]
)
(
$
error
)
;
}

Imports the exception.
@param array  $error
@return \Throwable
@throws \BadMethodCallException  Thrown if the exception class is not registered.


protected function _normalizeEvent($event, $params = [], $target = null)
    {
        if (is_string($event) || $event instanceof Stringable) {
            return $this->_createEvent($this->_normalizeString($event), $params, $target);
        }

        if (!($event instanceof EventInterface)) {
            throw $this->_createInvalidArgumentException(
                $this->__('Argument is not a string, stringable object or event instance'),
                null,
                null,
                $event
            );
        }

        if ($target !== null) {
            $event->setTarget($target);
        }

        if (count($params) > 0) {
            $event->setParams($params + $event->getParams());
        }

        return $event;
    }

Normalizes the given event into an Event instance.

@since [*next-version*]

@param EventInterface|string|Stringable $event  Event instance or an event name string.
@param array                            $params The event parameters; will be added to existing parameters if
the $event argument is an {@see EventInterface} instance.
@param object                           $target The target of the event.

@return EventInterface The event instance.

Normalizes the given event into an Event instance .


protected function _getWpHandlerWrapper($name, $callback)
    {
        $cbHash = $this->_hashWpHandler($name, $callback);

        // Create if not exists
        if (!isset($this->handlerWrappers[$cbHash])) {
            $this->handlerWrappers[$cbHash] = $this->_createWpHandlerWrapper(
                $name,
                $callback,
                $this->_getThrowOnPropStop()
            );
        }

        return $this->handlerWrappers[$cbHash];
    }

protected
function
_getWpHandlerWrapper
(
$
name
,
$
callback
)
{
$
cbHash
=
$
this
->
_hashWpHandler
(
$
name
,
$
callback
)
;
// Create if not exists
if
(
!
isset
(
$
this
->
handlerWrappers
[
$
cbHash
]
)
)
{
$
this
->
handlerWrappers
[
$
cbHash
]
=
$
this
->
_createWpHandlerWrapper
(
$
name
,
$
callback
,
$
this
->
_getThrowOnPropStop
(
)
)
;
}
return
$
this
->
handlerWrappers
[
$
cbHash
]
;
}

Gets a WordPress event handler wrapper.

For the same name/callback pair, returns the same wrapper.

@since [*next-version*]

@param string   $name     Name of the event.
@param callable $callback Handler of the event.

@return Closure The wrapper.

Gets a WordPress event handler wrapper .


protected function _createCachedEvent($event, array $args = [])
    {
        $event = $this->_normalizeEvent($event, $args);
        $name  = $event->getName();

        return $this->eventCache[$name] = $event;
    }

protected function _createCachedEvent ( $ event , array $ args = [ ] ) { $ event = $ this -> _normalizeEvent ( $ event , $ args ) ; $ name = $ event -> getName ( ) ; return $ this -> eventCache [ $ name ] = $ event ; }

Creates an event instance in cache.

@since [*next-version*]

@param string|Stringable|EventInterface $event The name of the event, or the event instance.
@param array                            $args  Optional array of event arguments.

@return EventInterface The created event instance.


protected function _hasCachedEvent($event)
    {
        $name = ($event instanceof EventInterface)
            ? $event->getName()
            : $event;

        return isset($this->eventCache[$name]);
    }

protected function _hasCachedEvent ( $ event ) { $ name = ( $ event instanceof EventInterface ) ? $ event -> getName ( ) : $ event ; return isset ( $ this -> eventCache [ $ name ] ) ; }

Checks if an event instance exists in cache for the given event name.

@since [*next-version*]

@param string|Stringable|EventInterface $event The name of the event, or the event instance.

@return bool True if an event instance exists for the given event name, false otherwise.

Checks if an event instance exists in cache for the given event name .


protected function _getCachedEvent($event, array $args = [])
    {
        $name = ($event instanceof EventInterface)
            ? $event->getName()
            : $event;

        // Create event instance if it does not exist
        if (!$this->_hasCachedEvent($name)) {
            $this->_createCachedEvent($event, $args);
        }

        return $this->eventCache[$name];
    }

Gets the cached event instance for the given name, creating it if needed.

@since [*next-version*]

@param string|Stringable|EventInterface $event The name of the event, or the event instance.
@param array                            $args  Optional array of event arguments.

@return EventInterface The event instance.

Gets the cached event instance for the given name creating it if needed .


protected function _removeCachedEvent($event)
    {
        $name = ($event instanceof EventInterface)
            ? $event->getName()
            : $event;

        unset($this->eventCache[$name]);

        return $this;
    }

protected function _removeCachedEvent ( $ event ) { $ name = ( $ event instanceof EventInterface ) ? $ event -> getName ( ) : $ event ; unset ( $ this -> eventCache [ $ name ] ) ; return $ this ; }

Removes a cached event instance.

@since [*next-version*]

@param string|Stringable|EventInterface $event The name of the event, or the event instance.

@return $this This instance.


protected function _createWpHandlerWrapper($name, $callback, $throwOnPropStop = false)
    {
        $eventCache = $this;

        /*
         * $name - The name of the event
         * $callback - The actual handler callback
         * $eventCache - The callback for retrieving events from cache
         * $throwOnPropStop - If true, an exception is thrown when propagation is stopped.
         */

        return function () use ($name, &$callback, $eventCache, $throwOnPropStop) {
            $fnArgs = func_get_args();
            // Detect whether the first argument given to the handler is an EventInterface
            $firstArg = count($fnArgs) === 1
                ? $fnArgs[0]
                : null;

            if ($firstArg instanceof EventInterface) {
                // If first argument is an event instance, put in cache
                $event = $eventCache->_getCachedEvent($firstArg);
            } else {
                // Creates and retrieves an instance if an event name
                $event = $eventCache->_getCachedEvent($name, $fnArgs);
            }

            // Call original handler if propagation is not stopped
            if (!$event->isPropagationStopped()) {
                \call_user_func_array($callback, [$event]);
            }

            if ($event->isPropagationStopped() && $throwOnPropStop) {
                throw $this->_createStoppedPropagationException(
                    $this->__('Propagation has been stopped.'),
                    0,
                    null,
                    $event
                );
            }

            return $event->getParam(0);
        };
    }

Creates a wrapper for a WordPress event handler, containing and replacing it.

If the wrapper receives an {@see EventInterface}, it will use that and assume it was triggered and normalized by
a PSR-14 event manager.

Otherwise it will assume that regular arguments have been passed (as would be done through the WordPress
functional API for hooks) and an event object will be created for those arguments.

This consequently allows handlers to stop event propagation in WordPress. Profit!

@since [*next-version*]

@param string   $name            The name of the event, for which a wrapper is created.
@param callable $callback        The callback to wrap.
@param bool     $throwOnPropStop If true, a {@see StoppedPropagationExceptionInterface} exception
is thrown when propagation is stopped.

@return Closure The wrapper.

Creates a wrapper for a WordPress event handler containing and replacing it .


static function exportException(?\Throwable $e) {
        if($e === null) {
            return null;
        }

        $handler = \CharlotteDunois\Phoebe\ExceptionHandler::getInstance();
        if($handler->handlesException($e)) {
            return $handler->exportException($e);
        }

        $trace = array();
        foreach($e->getTrace() as $entry) {
            if(isset($entry['args'])) {
                foreach($entry['args'] as $key => $arg) {
                    $entry['args'][$key] = static::exportTraceArgs($arg);
                }
            } else {
                $entry['args'] = array();
            }

            $trace[] = $entry;
        }

        return array(
            'class' => \get_class($e),
            'message' => $e->getMessage(),
            'code' => $e->getCode(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $trace,
            'previous' => static::exportException($e->getPrevious())
        );
    }

static function exportException ( ? \ Throwable $ e ) { if ( $ e === null ) { return null ; } $ handler = \ CharlotteDunois \ Phoebe \ ExceptionHandler :: getInstance ( ) ; if ( $ handler -> handlesException ( $ e ) ) { return $ handler -> exportException ( $ e ) ; } $ trace = array ( ) ; foreach ( $ e -> getTrace ( ) as $ entry ) { if ( isset ( $ entry [ 'args' ] ) ) { foreach ( $ entry [ 'args' ] as $ key => $ arg ) { $ entry [ 'args' ] [ $ key ] = static :: exportTraceArgs ( $ arg ) ; } } else { $ entry [ 'args' ] = array ( ) ; } $ trace [ ] = $ entry ; } return array ( 'class' => \ get_class ( $ e ) , 'message' => $ e -> getMessage ( ) , 'code' => $ e -> getCode ( ) , 'file' => $ e -> getFile ( ) , 'line' => $ e -> getLine ( ) , 'trace' => $ trace , 'previous' => static :: exportException ( $ e -> getPrevious ( ) ) ) ; }

Exports an exception.
@param \Throwable|null  $e
@return array|null


static function importException(array $export, ?array $args = null) {
        if(!empty($export['phoebeExceptionHandler'])) {
            return \CharlotteDunois\Phoebe\ExceptionHandler::getInstance()->importException($export);
        }

        if($args === null) {
            $args = array($export['message'], $export['code']);
        }

        $args[] = (!empty($export['previous']) ? static::importException($export['previous']) : null);

        $class = new \ReflectionClass($export['class']);
        $err = $class->newInstanceArgs($args);

        $prop = $class->getProperty('file');
        $prop->setAccessible(true);
        $prop->setValue($err, $export['file']);

        $prop = $class->getProperty('line');
        $prop->setAccessible(true);
        $prop->setValue($err, $export['line']);

        if($class->hasProperty('trace')) {
            $prop = $class->getProperty('trace');

            $prop->setAccessible(true);
            $prop->setValue($err, $export['trace']);
        } else {
            $parent = $class;

            while($parent = $parent->getParentClass()) {
                if($parent->hasProperty('trace')) {
                    $prop = $parent->getProperty('trace');

                    $prop->setAccessible(true);
                    $prop->setValue($err, $export['trace']);
                    break;
                }
            }
        }

        return $err;
    }

static function importException ( array $ export , ? array $ args = null ) { if ( ! empty ( $ export [ 'phoebeExceptionHandler' ] ) ) { return \ CharlotteDunois \ Phoebe \ ExceptionHandler :: getInstance ( ) -> importException ( $ export ) ; } if ( $ args === null ) { $ args = array ( $ export [ 'message' ] , $ export [ 'code' ] ) ; } $ args [ ] = ( ! empty ( $ export [ 'previous' ] ) ? static :: importException ( $ export [ 'previous' ] ) : null ) ; $ class = new \ ReflectionClass ( $ export [ 'class' ] ) ; $ err = $ class -> newInstanceArgs ( $ args ) ; $ prop = $ class -> getProperty ( 'file' ) ; $ prop -> setAccessible ( true ) ; $ prop -> setValue ( $ err , $ export [ 'file' ] ) ; $ prop = $ class -> getProperty ( 'line' ) ; $ prop -> setAccessible ( true ) ; $ prop -> setValue ( $ err , $ export [ 'line' ] ) ; if ( $ class -> hasProperty ( 'trace' ) ) { $ prop = $ class -> getProperty ( 'trace' ) ; $ prop -> setAccessible ( true ) ; $ prop -> setValue ( $ err , $ export [ 'trace' ] ) ; } else { $ parent = $ class ; while ( $ parent = $ parent -> getParentClass ( ) ) { if ( $ parent -> hasProperty ( 'trace' ) ) { $ prop = $ parent -> getProperty ( 'trace' ) ; $ prop -> setAccessible ( true ) ; $ prop -> setValue ( $ err , $ export [ 'trace' ] ) ; break ; } } } return $ err ; }

Converts an exported exception back into a Throwable.
@param array       $export
@param array|null  $args    Passed to the constructor if not using the Exception Handler.
@return \Throwable

Converts an exported exception back into a Throwable .


protected function _removeWpHook($name, callable $handler, $priority = null)
    {
        $name = $this->_normalizeString($name);

        $priority = ($priority === null)
            ? $this->_getWpHookDefaultPriority()
            : $this->_normalizeInt($priority);

        \remove_filter($name, $handler, $priority);
    }

protected function _removeWpHook ( $ name , callable $ handler , $ priority = null ) { $ name = $ this -> _normalizeString ( $ name ) ; $ priority = ( $ priority === null ) ? $ this -> _getWpHookDefaultPriority ( ) : $ this -> _normalizeInt ( $ priority ) ; \ remove_filter ( $ name , $ handler , $ priority ) ; }

Removes a hook handler from a WordPress event.

@since [*next-version*]

@param string|Stringable $name     The hook name.
@param callable          $handler  The hook handler callback.
@param int|null          $priority The priority of the hook - larger numbers signify later execution.

Removes a hook handler from a WordPress event .


protected function _replaceWpHook($hook)
    {
        global $wp_filter;

        if (!isset($wp_filter[$hook])) {
            return;
        }

        $wp_filter[$hook] = $this->_createWpHookReplacement($wp_filter[$hook]);
    }

protected function _replaceWpHook ( $ hook ) { global $ wp_filter ; if ( ! isset ( $ wp_filter [ $ hook ] ) ) { return ; } $ wp_filter [ $ hook ] = $ this -> _createWpHookReplacement ( $ wp_filter [ $ hook ] ) ; }

Replaces a WordPress hook instance.

@since [*next-version*]

@param string $hook The name of the hook.


function getStacked() {
        $stack = $this->stack->count();
        $stack += (int) $this->bag->currentlyBusy;

        return $stack;
    }

function getStacked ( ) { $ stack = $ this -> stack -> count ( ) ; $ stack += ( int ) $ this -> bag -> currentlyBusy ; return $ stack ; }

Returns the amount of work to do. Give this worker a break, will you?
@return int

Returns the amount of work to do . Give this worker a break will you?


function clearRefBag(int $timeout) {
        $time = \time() - $timeout;

        foreach($this->refBag as $id => $timestamp) {
            if($timestamp <= $time) {
                unset($this->refBag[$id]);
            }
        }

        if(\count($this->refBag) === 0 && $this->stack->count() === 0) {
            $this->bag->lastEmptyStack = \time();
            $this->bag->currentlyBusy = false;

            $msg = new \CharlotteDunois\Phoebe\Message('internal-worker-stack-free', null);
            $this->sendMessageToPool($msg);
        }
    }

function clearRefBag ( int $ timeout ) { $ time = \ time ( ) - $ timeout ; foreach ( $ this -> refBag as $ id => $ timestamp ) { if ( $ timestamp <= $ time ) { unset ( $ this -> refBag [ $ id ] ) ; } } if ( \ count ( $ this -> refBag ) === 0 && $ this -> stack -> count ( ) === 0 ) { $ this -> bag -> lastEmptyStack = \ time ( ) ; $ this -> bag -> currentlyBusy = false ; $ msg = new \ CharlotteDunois \ Phoebe \ Message ( 'internal-worker-stack-free' , null ) ; $ this -> sendMessageToPool ( $ msg ) ; } }

Clears too old references.
@param int  $timeout
@return void
@internal


function sendMessageToPool(\CharlotteDunois\Phoebe\Message $message) {
        if(static::$socket) {
            $data = \CharlotteDunois\Phoebe\Socket::encodeMessage($message);
            static::$socket->write($data);
        } else {
            $this->messagesOut[] = $message;
        }
    }

function sendMessageToPool ( \ CharlotteDunois \ Phoebe \ Message $ message ) { if ( static :: $ socket ) { $ data = \ CharlotteDunois \ Phoebe \ Socket :: encodeMessage ( $ message ) ; static :: $ socket -> write ( $ data ) ; } else { $ this -> messagesOut [ ] = $ message ; } }

Sends a message to the pool. Only usable in the worker thread.
@param \CharlotteDunois\Phoebe\Message  $message
@return void
@codeCoverageIgnore

Sends a message to the pool . Only usable in the worker thread .


function derefTask(\CharlotteDunois\Phoebe\AsyncTask $task) {
        if(isset($this->refBag[$task->id])) {
            unset($this->refBag[$task->id]);

            if(\count($this->refBag) === 0 && $this->stack->count() === 0) {
                $this->bag->lastEmptyStack = \time();
                $this->bag->currentlyBusy = false;

                $msg = new \CharlotteDunois\Phoebe\Message('internal-worker-stack-free', null);
                $this->sendMessageToPool($msg);
            }
        }
    }

function derefTask ( \ CharlotteDunois \ Phoebe \ AsyncTask $ task ) { if ( isset ( $ this -> refBag [ $ task -> id ] ) ) { unset ( $ this -> refBag [ $ task -> id ] ) ; if ( \ count ( $ this -> refBag ) === 0 && $ this -> stack -> count ( ) === 0 ) { $ this -> bag -> lastEmptyStack = \ time ( ) ; $ this -> bag -> currentlyBusy = false ; $ msg = new \ CharlotteDunois \ Phoebe \ Message ( 'internal-worker-stack-free' , null ) ; $ this -> sendMessageToPool ( $ msg ) ; } } }

Dereferences a task. Decrements the worker's busy counter.
This method is automatically called by the task.
@param \CharlotteDunois\Phoebe\AsyncTask  $task
@return void

Dereferences a task . Decrements the worker s busy counter . This method is automatically called by the task .


protected function bootstrap() {
        \set_error_handler(function ($errno, $errstr, $errfile, $errline) {
            if(\error_reporting() === 0) {
                return true;
            }

            throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
        });

        \error_reporting(-1);

        if(\file_exists(__DIR__.'/../vendor/autoload.php')) {
            require_once __DIR__.'/../vendor/autoload.php';
        } else {
            require_once __DIR__.'/../../../autoload.php';
        }

        $this->exceptions->__construct(); // Set the static variable again
        \CharlotteDunois\Phoebe\ResourceSocket::$closeResource = false;

        static::$events = new \CharlotteDunois\Events\EventEmitter();
        self::$events = static::$events;

        static::$loop = \React\EventLoop\Factory::create();
        self::$loop = static::$loop;

        static::$me = $this;
        self::$me = static::$me;
    }

protected
function
bootstrap
(
)
{
\
set_error_handler
(
function
(
$
errno
,
$
errstr
,
$
errfile
,
$
errline
)
{
if
(
\
error_reporting
(
)
===
0
)
{
return
true
;
}
throw
new
\
ErrorException
(
$
errstr
,
0
,
$
errno
,
$
errfile
,
$
errline
)
;
}
)
;
\
error_reporting
(
1
)
;
if
(
\
file_exists
(
__DIR__
.
'/../vendor/autoload.php'
)
)
{
require_once
__DIR__
.
'/../vendor/autoload.php'
;
}
else
{
require_once
__DIR__
.
'/../../../autoload.php'
;
}
$
this
->
exceptions
->
__construct
(
)
;
// Set the static variable again
\
CharlotteDunois
\
Phoebe
\
ResourceSocket
::
$
closeResource
=
false
;
static
::
$
events
=
new
\
CharlotteDunois
\
Events
\
EventEmitter
(
)
;
self
::
$
events
=
static
::
$
events
;
static
::
$
loop
=
\
React
\
EventLoop
\
Factory
::
create
(
)
;
self
::
$
loop
=
static
::
$
loop
;
static
::
$
me
=
$
this
;
self
::
$
me
=
static
::
$
me
;
}

This method MUST be always invoked in the worker when booting up.
@return void
@internal
@codeCoverageIgnore

This method MUST be always invoked in the worker when booting up .


static function filter(\CharlotteDunois\Phoebe\Pool $pool, array $array, ?callable $function = null) {
        if($function === null) {
            $function = function ($value) {
                return ((bool) $value); // @codeCoverageIgnore
            };
        }

        $promises = array();

        foreach($array as $id => $arg) {
            $promises[$id] = static::parallelize($pool, $function, array($arg))->then(null, function (\Throwable $e) {
                return $e;
            });
        }

        return \React\Promise\all($promises)->then(function (array $result) use ($array) {
            $result = static::convertResult($result);

            foreach($array as $key => $_) {
                if(!$result[$key]) {
                    unset($array[$key]);
                }
            }

            return $array;
        });
    }

static
function
filter
(
\
CharlotteDunois
\
Phoebe
\
Pool
$
pool
,
array
$
array
,
callable
$
function
=
)
{
if
(
$
function
===
)
{
$
function
=
function
(
$
value
)
{
return
(
(
bool
)
$
value
)
;
// @codeCoverageIgnore
}
;
}
$
promises
=
array
(
)
;
foreach
(
$
array
as
$
id
=>
$
arg
)
{
$
promises
[
$
id
]
=
static
::
parallelize
(
$
pool
,
$
function
,
array
(
$
arg
)
)
->
then
(
,
function
(
\
Throwable
$
e
)
{
return
$
e
;
}
)
;
}
return
\
React
\
Promise
\
all
(
$
promises
)
->
then
(
function
(
array
$
result
)
use
(
$
array
)
{
$
result
=
static
::
convertResult
(
$
result
)
;
foreach
(
$
array
as
$
key
=>
$
_
)
{
if
(
!
$
result
[
$
key
]
)
{
unset
(
$
array
[
$
key
]
)
;
}
}
return
$
array
;
}
)
;
}

Parallelizes a given function across a number of available workers, collects the values passing the truth test and reconstructs the array.

Internally this method makes use of `AsyncTask`s. The pool schedules the tasks across the workers automatically.
@param \CharlotteDunois\Phoebe\Pool  $pool
@param array                         $array     Must be serializable.
@param callable|null                 $function
@return \React\Promise\PromiseInterface
@throws \CharlotteDunois\Phoebe\MultiReasonException  The exception the promise gets rejected with.

Parallelizes a given function across a number of available workers collects the values passing the truth test and reconstructs the array .


static function flatMap(\CharlotteDunois\Phoebe\Pool $pool, array $array, callable $function) {
        $promises = array();

        $howMany = (int) \ceil((\count($array) / $pool->getSize()));
        $chunks = \array_chunk($array, $howMany);

        $newFun = function ($value) use ($function) {
            return \array_reduce($value, $function, array()); // @codeCoverageIgnore
        };

        foreach($chunks as $arg) {
            $promises[] = static::parallelize($pool, $newFun, array($arg))->then(null, function (\Throwable $e) {
                return $e;
            });
        }

        return \React\Promise\all($promises)->then(function (array $result) use ($function) {
            return \array_reduce(static::convertResult($result), $function, array());
        });
    }

static
function
flatMap
(
\
CharlotteDunois
\
Phoebe
\
Pool
$
pool
,
array
$
array
,
callable
$
function
)
{
$
promises
=
array
(
)
;
$
howMany
=
(
int
)
\
ceil
(
(
\
count
(
$
array
)
/
$
pool
->
getSize
(
)
)
)
;
$
chunks
=
\
array_chunk
(
$
array
,
$
howMany
)
;
$
newFun
=
function
(
$
value
)
use
(
$
function
)
{
return
\
array_reduce
(
$
value
,
$
function
,
array
(
)
)
;
// @codeCoverageIgnore
}
;
foreach
(
$
chunks
as
$
arg
)
{
$
promises
[
]
=
static
::
parallelize
(
$
pool
,
$
newFun
,
array
(
$
arg
)
)
->
then
(
,
function
(
\
Throwable
$
e
)
{
return
$
e
;
}
)
;
}
return
\
React
\
Promise
\
all
(
$
promises
)
->
then
(
function
(
array
$
result
)
use
(
$
function
)
{
return
\
array_reduce
(
static
::
convertResult
(
$
result
)
,
$
function
,
array
(
)
)
;
}
)
;
}

Parallelizes a given flattening function across a number of available workers, collects their return values and flattens the final array.
Whether the resulting array is an actual flat map depends on the given callable.

Internally this method makes use of `AsyncTask`s. The pool schedules the tasks across the workers automatically.
@param \CharlotteDunois\Phoebe\Pool  $pool
@param array                         $array     Must be serializable.
@param callable                      $function  Flattens the array.
@return \React\Promise\PromiseInterface
@throws \CharlotteDunois\Phoebe\MultiReasonException  The exception the promise gets rejected with.

Parallelizes a given flattening function across a number of available workers collects their return values and flattens the final array . Whether the resulting array is an actual flat map depends on the given callable .


static function map(\CharlotteDunois\Phoebe\Pool $pool, array $array, callable $function) {
        $promises = array();

        foreach($array as $id => $arg) {
            $promises[$id] = static::parallelize($pool, $function, array($arg))->then(null, function (\Throwable $e) {
                return $e;
            });
        }

        return \React\Promise\all($promises)->then(\Closure::fromCallable(array(self::class, 'convertResult')));
    }

static function map ( \ CharlotteDunois \ Phoebe \ Pool $ pool , array $ array , callable $ function ) { $ promises = array ( ) ; foreach ( $ array as $ id => $ arg ) { $ promises [ $ id ] = static :: parallelize ( $ pool , $ function , array ( $ arg ) ) -> then ( null , function ( \ Throwable $ e ) { return $ e ; } ) ; } return \ React \ Promise \ all ( $ promises ) -> then ( \ Closure :: fromCallable ( array ( self :: class , 'convertResult' ) ) ) ; }

Parallelizes a given function across a number of available workers, collects their return values and reconstructs the array.

Internally this method makes use of `AsyncTask`s. The pool schedules the tasks across the workers automatically.
@param \CharlotteDunois\Phoebe\Pool  $pool
@param array                         $array     Must be serializable.
@param callable                      $function
@return \React\Promise\PromiseInterface
@throws \CharlotteDunois\Phoebe\MultiReasonException  The exception the promise gets rejected with.

Parallelizes a given function across a number of available workers collects their return values and reconstructs the array .


protected static function parallelize(\CharlotteDunois\Phoebe\Pool $pool, callable $function, array $args) {
        $task = new \CharlotteDunois\Phoebe\ParallelTask($function, $args);
        return $pool->submitTask($task);
    }

protected static function parallelize ( \ CharlotteDunois \ Phoebe \ Pool $ pool , callable $ function , array $ args ) { $ task = new \ CharlotteDunois \ Phoebe \ ParallelTask ( $ function , $ args ) ; return $ pool -> submitTask ( $ task ) ; }

Creates a parallelized function from a callable and the arguments.
@param \CharlotteDunois\Phoebe\Pool  $pool
@param callable                      $function
@param mixed[]                       $args      Must be serializable.
@return \React\Promise\PromiseInterface
@codeCoverageIgnore

Creates a parallelized function from a callable and the arguments .


protected static function convertResult(array $result) {
        $errors = array();

        foreach($result as $id => $value) {
            if($value instanceof \Throwable) {
                $errors[$id] = $value;
            }
        }

        if(!empty($errors)) {
            throw new \CharlotteDunois\Phoebe\MultiReasonException($errors);
        }

        return $result;
    }

protected static function convertResult ( array $ result ) { $ errors = array ( ) ; foreach ( $ result as $ id => $ value ) { if ( $ value instanceof \ Throwable ) { $ errors [ $ id ] = $ value ; } } if ( ! empty ( $ errors ) ) { throw new \ CharlotteDunois \ Phoebe \ MultiReasonException ( $ errors ) ; } return $ result ; }

Converts the result into a multi reason exception, or returns the result.
@param array  $result
@return array
@throws \CharlotteDunois\Phoebe\MultiReasonException

Converts the result into a multi reason exception or returns the result .


protected function _setNumArgs($numArgs)
    {
        $numArgs = $this->_normalizeInt($numArgs);

        if ($numArgs < 0) {
            throw $this->_createOutOfRangeException(
                $this->__('Number of arguments cannot be smaller than zero'), null, null, $numArgs
            );
        }

        $this->numArgs = $numArgs;
    }

Sets the argument count value to pass to the native WordPress action and filter hooking functions.

@since [*next-version*]

@param int $numArgs A positive argument count to pass to the native WordPress action and filter hooking
functions.

Sets the argument count value to pass to the native WordPress action and filter hooking functions .


protected function _runWpHook($name, array $args = [])
    {
        $name = $this->_normalizeString($name);

        // Add the event name to the args array
        \array_unshift($args, $name);

        return \call_user_func_array('apply_filters', $args);
    }

protected
function
_runWpHook
(
$
name
,
array
$
args
=
[
]
)
{
$
name
=
$
this
->
_normalizeString
(
$
name
)
;
// Add the event name to the args array
\
array_unshift
(
$
args
,
$
name
)
;
return
\
call_user_func_array
(
'apply_filters'
,
$
args
)
;
}

Causes WordPress to execute all handlers of the specified hook.

@since [*next-version*]

@param string|Stringable $name Name of the hook to run handlers for.
@param array             $args Arguments to pass to the handler.

@return mixed The result returned by the last handler.

Causes WordPress to execute all handlers of the specified hook .


public function apply_filters($value, $args)
    {
        try {
            $value = $this->wpHook->apply_filters($value, $args);
        } catch (StoppedPropagationExceptionInterface $stoppedPropagationException) {
            $event = $stoppedPropagationException->getEvent();
            $value = $event->getParam(0);
        }

        return $value;
    }

public function apply_filters ( $ value , $ args ) { try { $ value = $ this -> wpHook -> apply_filters ( $ value , $ args ) ; } catch ( StoppedPropagationExceptionInterface $ stoppedPropagationException ) { $ event = $ stoppedPropagationException -> getEvent ( ) ; $ value = $ event -> getParam ( 0 ) ; } return $ value ; }

Calls the callback functions added to a filter hook.

This method is called instead of `$this->wpHook->apply_filters()`. This is done due to the fact that calls to
existing methods are not passed on to the `__call()` magic method.

@param mixed $value The value to filter.
@param array $args  Arguments to pass to callbacks.

@return mixed The filtered value after all hooked functions are applied to it.

Calls the callback functions added to a filter hook .


protected function _addWpHook($name, callable $handler, $priority = null, $numArgs = 1)
    {
        $name = $this->_normalizeString($name);

        $priority = ($priority === null)
            ? $this->_getWpHookDefaultPriority()
            : $this->_normalizeInt($priority);

        $numArgs = ($numArgs === null)
            ? 1
            : $this->_normalizeInt($numArgs);

        \add_filter($name, $handler, $priority, $numArgs);
    }

protected function _addWpHook ( $ name , callable $ handler , $ priority = null , $ numArgs = 1 ) { $ name = $ this -> _normalizeString ( $ name ) ; $ priority = ( $ priority === null ) ? $ this -> _getWpHookDefaultPriority ( ) : $ this -> _normalizeInt ( $ priority ) ; $ numArgs = ( $ numArgs === null ) ? 1 : $ this -> _normalizeInt ( $ numArgs ) ; \ add_filter ( $ name , $ handler , $ priority , $ numArgs ) ; }

Adds a hook handler to a WordPress event.

@since [*next-version*]

@param string|Stringable $name     The hook name.
@param callable          $handler  The hook handler callback.
@param int|null          $priority The priority of the hook - larger numbers signify later execution.
@param int|null          $numArgs  The number of arguments to accept from the event invoker.

@throws InvalidArgumentException If the $name, $priority or $numArgs arguments are invalid.

Adds a hook handler to a WordPress event .


final function wrap($functionResult) {
        try {
            if($functionResult instanceof \Closure) {
                $functionResult = $functionResult();
            }

            if($functionResult instanceof \Throwable) {
                return $this->wrapUp(null, $functionResult);
            } elseif(!($functionResult instanceof \React\Promise\PromiseInterface)) {
                return $this->wrapUp($functionResult, null);
            }

            $functionResult->done(function ($val) {
                $this->wrapUp($val, null);
            }, function ($val) {
                $this->wrapUp(null, $val);
            });
        } catch (\Throwable $e) {
            $this->wrapUp(null, $e);
        }
    }

final function wrap ( $ functionResult ) { try { if ( $ functionResult instanceof \ Closure ) { $ functionResult = $ functionResult ( ) ; } if ( $ functionResult instanceof \ Throwable ) { return $ this -> wrapUp ( null , $ functionResult ) ; } elseif ( ! ( $ functionResult instanceof \ React \ Promise \ PromiseInterface ) ) { return $ this -> wrapUp ( $ functionResult , null ) ; } $ functionResult -> done ( function ( $ val ) { $ this -> wrapUp ( $ val , null ) ; } , function ( $ val ) { $ this -> wrapUp ( null , $ val ) ; } ) ; } catch ( \ Throwable $ e ) { $ this -> wrapUp ( null , $ e ) ; } }

Wrapper for the function result. Closures get called. Promises get resolved and their resolve value get sent upstream to the pool.
@param mixed|\React\Promise\PromiseInterface|\Closure  $functionResult  Result must be serializable.
@return void
@codeCoverageIgnore

Wrapper for the function result . Closures get called . Promises get resolved and their resolve value get sent upstream to the pool .


function resume() {
        if(!$this->sockets) {
            if(!$this->timer) {
                $this->timer = $this->loop->addPeriodicTimer($this->timerInterval, array($this->pool, 'timerTick'));
            }

            return;
        }

        if($this->streams->count() > 0) {
            foreach($this->streams as $stream) {
                $stream->resume();
            }
        }
    }

function resume ( ) { if ( ! $ this -> sockets ) { if ( ! $ this -> timer ) { $ this -> timer = $ this -> loop -> addPeriodicTimer ( $ this -> timerInterval , array ( $ this -> pool , 'timerTick' ) ) ; } return ; } if ( $ this -> streams -> count ( ) > 0 ) { foreach ( $ this -> streams as $ stream ) { $ stream -> resume ( ) ; } } }

Resumes whatever the implementation would do. Adds the timer or resumes all streams.
@return void

Resumes whatever the implementation would do . Adds the timer or resumes all streams .


function stop() {
        if(!$this->sockets) {
            if($this->timer) {
                $this->loop->cancelTimer($this->timer);
                $this->timer = null;
            }

            return;
        }

        if($this->streams->count() > 0) {
            foreach($this->streams as $stream) {
                $stream->pause();
            }
        }
    }

function stop ( ) { if ( ! $ this -> sockets ) { if ( $ this -> timer ) { $ this -> loop -> cancelTimer ( $ this -> timer ) ; $ this -> timer = null ; } return ; } if ( $ this -> streams -> count ( ) > 0 ) { foreach ( $ this -> streams as $ stream ) { $ stream -> pause ( ) ; } } }

Stops whatever the implementation would do. Cancels the timer or pauses all streams.
@return void

Stops whatever the implementation would do . Cancels the timer or pauses all streams .


function destroy() {
        if(!$this->sockets) {
            if($this->timer) {
                $this->loop->cancelTimer($this->timer);
                $this->timer = null;
            }

            return;
        }

        if($this->streams->count() > 0) {
            foreach($this->streams as $stream) {
                $stream->close();
            }
        }
    }

function destroy ( ) { if ( ! $ this -> sockets ) { if ( $ this -> timer ) { $ this -> loop -> cancelTimer ( $ this -> timer ) ; $ this -> timer = null ; } return ; } if ( $ this -> streams -> count ( ) > 0 ) { foreach ( $ this -> streams as $ stream ) { $ stream -> close ( ) ; } } }

Destroys the messenger. And everything.
@return void

Destroys the messenger . And everything .


function getWorkerSocket(\CharlotteDunois\Phoebe\Worker $worker) {
        return $this->streams->get($worker->id);
    }

function getWorkerSocket ( \ CharlotteDunois \ Phoebe \ Worker $ worker ) { return $ this -> streams -> get ( $ worker -> id ) ; }

Gets the ResourceSocket for the worker.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return \CharlotteDunois\Phoebe\ResourceSocket|null

Gets the ResourceSocket for the worker .


function removeWorkerSocket(\CharlotteDunois\Phoebe\Worker $worker) {
        $stream = $this->streams->get($worker->id);
        if($stream !== null) {
            $this->streams->delete($worker->id);
            $stream->close();

            $socket = $worker->getSocket();
            if($socket !== null) {
                $worker->setSocket(null);

                $sock = $socket->getPoolSocket();
                $boot = $socket->getWorkerSocket();

                if(\is_resource($sock)) {
                    @\socket_shutdown($sock, 2);
                    \socket_close($sock);
                }

                if(\is_resource($boot)) {
                    @\socket_shutdown($boot, 2);
                    \socket_close($boot);
                }
            }
        }
    }

function removeWorkerSocket ( \ CharlotteDunois \ Phoebe \ Worker $ worker ) { $ stream = $ this -> streams -> get ( $ worker -> id ) ; if ( $ stream !== null ) { $ this -> streams -> delete ( $ worker -> id ) ; $ stream -> close ( ) ; $ socket = $ worker -> getSocket ( ) ; if ( $ socket !== null ) { $ worker -> setSocket ( null ) ; $ sock = $ socket -> getPoolSocket ( ) ; $ boot = $ socket -> getWorkerSocket ( ) ; if ( \ is_resource ( $ sock ) ) { @ \ socket_shutdown ( $ sock , 2 ) ; \ socket_close ( $ sock ) ; } if ( \ is_resource ( $ boot ) ) { @ \ socket_shutdown ( $ boot , 2 ) ; \ socket_close ( $ boot ) ; } } } }

Removes the ResourceSocket of the worker.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return void

Removes the ResourceSocket of the worker .


function prepareWorker(\CharlotteDunois\Phoebe\Worker $worker) {
        if($this->sockets) {
            $socket = new \CharlotteDunois\Phoebe\Socket($worker->id);
            $worker->setSocket($socket);

            $sock = $socket->getPoolSocket();

            $stream = new \CharlotteDunois\Phoebe\ResourceSocket($sock, $this->loop);
            $this->streams->set($worker->id, $stream);

            $buffer = '';
            $stream->on('data', function ($data) use (&$buffer, &$worker) {
                try {
                    $buffer .= $data;

                    $msgs = \CharlotteDunois\Phoebe\Socket::decodeMessages($buffer);
                    $buffer = \array_shift($msgs);

                    foreach($msgs as $message) {
                        $this->pool->handleMessage($worker, $message);
                    }
                } catch (\Throwable $error) {
                    $this->pool->emit('error', $error);
                }
            });

            $stream->on('error', function ($error) {
                $this->pool->emit('error', $error);
            });

            $stream->on('close', function () use ($worker) {
                $this->pool->handleStreamClose($worker);
            });
        }
    }

function prepareWorker ( \ CharlotteDunois \ Phoebe \ Worker $ worker ) { if ( $ this -> sockets ) { $ socket = new \ CharlotteDunois \ Phoebe \ Socket ( $ worker -> id ) ; $ worker -> setSocket ( $ socket ) ; $ sock = $ socket -> getPoolSocket ( ) ; $ stream = new \ CharlotteDunois \ Phoebe \ ResourceSocket ( $ sock , $ this -> loop ) ; $ this -> streams -> set ( $ worker -> id , $ stream ) ; $ buffer = '' ; $ stream -> on ( 'data' , function ( $ data ) use ( & $ buffer , & $ worker ) { try { $ buffer .= $ data ; $ msgs = \ CharlotteDunois \ Phoebe \ Socket :: decodeMessages ( $ buffer ) ; $ buffer = \ array_shift ( $ msgs ) ; foreach ( $ msgs as $ message ) { $ this -> pool -> handleMessage ( $ worker , $ message ) ; } } catch ( \ Throwable $ error ) { $ this -> pool -> emit ( 'error' , $ error ) ; } } ) ; $ stream -> on ( 'error' , function ( $ error ) { $ this -> pool -> emit ( 'error' , $ error ) ; } ) ; $ stream -> on ( 'close' , function ( ) use ( $ worker ) { $ this -> pool -> handleStreamClose ( $ worker ) ; } ) ; } }

Prepares the worker if needed.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return void


protected function _attachMethodHandler($eventName, $methodName, $priority = null, $numArgs = 1)
    {
        $methodName = $this->_normalizeString($methodName);
        $reflection = $this->_createReflectionMethod(get_class($this), $methodName);
        $handler    = $reflection->getClosure($this);

        $this->_addWpHook($eventName, $handler, $priority, $numArgs);
    }

protected function _attachMethodHandler ( $ eventName , $ methodName , $ priority = null , $ numArgs = 1 ) { $ methodName = $ this -> _normalizeString ( $ methodName ) ; $ reflection = $ this -> _createReflectionMethod ( get_class ( $ this ) , $ methodName ) ; $ handler = $ reflection -> getClosure ( $ this ) ; $ this -> _addWpHook ( $ eventName , $ handler , $ priority , $ numArgs ) ; }

Attaches a handler for a method on this instance.

@since [*next-version*]

@param string|Stringable $eventName  The name of the event to attach to.
@param string|Stringable $methodName The name of the method to attach.
@param int|null          $priority   The priority to attach with.
@param int|null          $numArgs    The number of arguments to accept from the event invoker.

@throws ReflectionException If a handler for the method could not be retrieved.

Attaches a handler for a method on this instance .


protected function _hashCallable($callable)
    {
        $key = null;

        if (is_string($callable) || $callable instanceof Stringable) {
            $key = $this->_normalizeString($callable);
        } elseif (is_object($callable)) {
            $key = spl_object_hash($callable);
        } elseif (is_array($callable) && count($callable) == 2) {
            $part1 = is_object($callable[0])
                ? spl_object_hash($callable[0])
                : $this->_normalizeString($callable[0]);
            $part2 = $this->_normalizeString($callable[1]);

            $key = serialize([$part1, $part2]);
        }

        if ($key === null) {
            throw $this->_createInvalidArgumentException(
                $this->__('Failed to hash - not a valid callable'),
                null,
                null,
                $callable
            );
        }

        return sha1($key);
    }

protected
function
_hashCallable
(
$
callable
)
{
$
key
=
;
if
(
is_string
(
$
callable
)
||
$
callable
instanceof
Stringable
)
{
$
key
=
$
this
->
_normalizeString
(
$
callable
)
;
}
elseif
(
is_object
(
$
callable
)
)
{
$
key
=
spl_object_hash
(
$
callable
)
;
}
elseif
(
is_array
(
$
callable
)
&&
count
(
$
callable
)
==
2
)
{
$
part1
=
is_object
(
$
callable
[
0
]
)
spl_object_hash
(
$
callable
[
0
]
)
:
$
this
->
_normalizeString
(
$
callable
[
0
]
)
;
$
part2
=
$
this
->
_normalizeString
(
$
callable
[
1
]
)
;
$
key
=
serialize
(
[
$
part1
,
$
part2
]
)
;
}
if
(
$
key
===
)
{
throw
$
this
->
_createInvalidArgumentException
(
$
this
->
__
(
'Failed to hash - not a valid callable'
)
,
,
,
$
callable
)
;
}
return
sha1
(
$
key
)
;
}

Computes a hash of a given callable.

@since [*next-version*]

@param callable|Stringable $callable The callable to hash.

@throws OutOfRangeException      If the argument is not a valid callable.
@throws InvalidArgumentException If argument is a string/stringable and cannot be normalized.

@return string A hash of the callable.


function write($data) {
        if(!$this->writable) {
            return false;
        }

        $this->data .= $data;

        $this->addWriteListener();
        return (!isset($this->data[($this->softLimit - 1)]));
    }

function write ( $ data ) { if ( ! $ this -> writable ) { return false ; } $ this -> data .= $ data ; $ this -> addWriteListener ( ) ; return ( ! isset ( $ this -> data [ ( $ this -> softLimit - 1 ) ] ) ) ; }

Write some data into the stream.
@param mixed|string  $data
@return bool


function pipe(\React\Stream\WritableStreamInterface $dest, array $options = array()) {
        return \React\Stream\Util::pipe($this, $dest, $options);
    }

function pipe ( \ React \ Stream \ WritableStreamInterface $ dest , array $ options = array ( ) ) { return \ React \ Stream \ Util :: pipe ( $ this , $ dest , $ options ) ; }

Pipes all the data from this readable source into the given writable destination.
Automatically sends all incoming data to the destination.
Automatically throttles the source based on what the destination can handle.
@param \React\Stream\WritableStreamInterface  $dest
@param array                                  $options
@return \React\Stream\WritableStreamInterface  $dest  Stream as-is

Pipes all the data from this readable source into the given writable destination . Automatically sends all incoming data to the destination . Automatically throttles the source based on what the destination can handle .


function handleData() {
        try {
            $this->setErrorHandler();

            $data = '';
            $bytes = \socket_recv($this->socket, $data, $this->bufferSize, (\defined('MSG_DONTWAIT') ? \MSG_DONTWAIT : 0));

            if($bytes === false) {
                $this->checkSocketErrors();
            }

            $this->restoreErrorHandler();

            if($bytes > 0) {
                $this->emit('data', array($data));
            }
        } catch (\DomainException $error) {
            $this->restoreErrorHandler();

            $this->emit('error', array((new \RuntimeException('Unable to read from socket: '.$error->getMessage(), 0, $error))));
            $this->close();
        }
    }

function
handleData
(
)
{
try
{
$
this
->
setErrorHandler
(
)
;
$
data
=
''
;
$
bytes
=
\
socket_recv
(
$
this
->
socket
,
$
data
,
$
this
->
bufferSize
,
(
\
defined
(
'MSG_DONTWAIT'
)
\
MSG_DONTWAIT
:
0
)
)
;
if
(
$
bytes
===
false
)
{
$
this
->
checkSocketErrors
(
)
;
}
$
this
->
restoreErrorHandler
(
)
;
if
(
$
bytes
>
0
)
{
$
this
->
emit
(
'data'
,
array
(
$
data
)
)
;
}
}
catch
(
\
DomainException
$
error
)
{
$
this
->
restoreErrorHandler
(
)
;
$
this
->
emit
(
'error'
,
array
(
(
new
\
RuntimeException
(
'Unable to read from socket: '
.
$
error
->
getMessage
(
)
,
0
,
$
error
)
)
)
)
;
$
this
->
close
(
)
;
}
}


function handleWrite() {
        try {
            if($this->data !== '') {
                $this->setErrorHandler();

                if($this->writeChunkSize === -1) {
                    $sent = \socket_send($this->socket, $this->data, \strlen($this->data), 0);
                } else {
                    $sent = \socket_send($this->socket, $this->data, $this->writeChunkSize, 0);
                }

                if($sent === false) {
                    $this->checkSocketErrors();
                }

                $this->restoreErrorHandler();

                if($sent > 0) {
                    $exceeded = isset($this->data[($this->softLimit - 1)]);
                    $this->data = (string) \substr($this->data, $sent);

                    // buffer has been above limit and is now below limit
                    if($exceeded && !isset($this->data[($this->softLimit - 1)])) {
                        $this->emit('drain');
                    }
                }
            }

            if($this->data === '') {
                $this->removeWriteListener();

                if(!$this->writable) {
                    $this->close();
                }
            }
        } catch (\DomainException $error) {
            $this->restoreErrorHandler();

            $this->emit('error', array((new \RuntimeException('Unable to write to socket: '.$error->getMessage(), 0, $error))));
            $this->close();
        }
    }


protected function checkSocketErrors() {
        $code = \socket_last_error($this->socket);
        \socket_clear_error($this->socket);

        if($code !== \SOCKET_EWOULDBLOCK) {
            throw new \DomainException(\socket_strerror($code));
        }
    }

protected function checkSocketErrors ( ) { $ code = \ socket_last_error ( $ this -> socket ) ; \ socket_clear_error ( $ this -> socket ) ; if ( $ code !== \ SOCKET_EWOULDBLOCK ) { throw new \ DomainException ( \ socket_strerror ( $ code ) ) ; } }

Checks for errors on the socket.
@return void
@internal


protected function removeReadListener() {
        if($this->listeningRead) {
            $this->loop->removeReadStream($this->socket);
            $this->listeningRead = false;
        }
    }

protected function removeReadListener ( ) { if ( $ this -> listeningRead ) { $ this -> loop -> removeReadStream ( $ this -> socket ) ; $ this -> listeningRead = false ; } }

Removes the read listener.
@return void
@internal


protected function removeWriteListener() {
        if($this->listeningWrite) {
            $this->loop->removeWriteStream($this->socket);
            $this->listeningWrite = false;
        }
    }

protected function removeWriteListener ( ) { if ( $ this -> listeningWrite ) { $ this -> loop -> removeWriteStream ( $ this -> socket ) ; $ this -> listeningWrite = false ; } }

Removes the write listener.
@return void
@internal


static function decodeMessage(string $data) {
        try {
            $message = \unserialize(\base64_decode(\rtrim($data, static::END_BUFFER)));
            if(!($message instanceof \CharlotteDunois\Phoebe\Message)) {
                throw new \Exception('');
            }

            return $message;
        } catch (\Throwable $e) {
            throw new \InvalidArgumentException('Invalid phoebe message');
        }
    }

static
function
decodeMessage
(
string
$
data
)
{
try
{
$
message
=
\
unserialize
(
\
base64_decode
(
\
rtrim
(
$
data
,
static
::
END_BUFFER
)
)
)
;
if
(
!
(
$
message
instanceof
\
CharlotteDunois
\
Phoebe
\
Message
)
)
{
throw
new
\
Exception
(
''
)
;
}
return
$
message
;
}
catch
(
\
Throwable
$
e
)
{
throw
new
\
InvalidArgumentException
(
'Invalid phoebe message'
)
;
}
}

Decodes a message.
@param string  $data
@return \CharlotteDunois\Phoebe\Message
@throws \InvalidArgumentException


function attachTimer() {
        $this->messenger->resume();

        if($this->timer || !$this->messenger->usesSockets()) {
            return;
        }

        $this->timer = $this->loop->addPeriodicTimer(($this->options['internalTimerTick'] ?? 30), function () {
            $this->internalTimerTick();
        });
    }

function attachTimer ( ) { $ this -> messenger -> resume ( ) ; if ( $ this -> timer || ! $ this -> messenger -> usesSockets ( ) ) { return ; } $ this -> timer = $ this -> loop -> addPeriodicTimer ( ( $ this -> options [ 'internalTimerTick' ] ?? 30 ) , function ( ) { $ this -> internalTimerTick ( ) ; } ) ; }

Attaches the pool timer to the loop.
@return void


function cancelTimer() {
        $this->messenger->stop();

        if($this->timer) {
            $this->loop->cancelTimer($this->timer);
            $this->timer = null;
        }
    }

function cancelTimer ( ) { $ this -> messenger -> stop ( ) ; if ( $ this -> timer ) { $ this -> loop -> cancelTimer ( $ this -> timer ) ; $ this -> timer = null ; } }

Cancels only the pool timer. Workers continue to run.
@return void

Cancels only the pool timer . Workers continue to run .


function submit(\Threaded $work) {
        if($this->timer === null) {
            $this->attachTimer();
        }

        $worker = ($this->freeWorkers->count() > 0 ? $this->freeWorkers->dequeue() : null);
        if(!$worker) {
            if(\count($this->workers) >= $this->size) {
                $iwork = new \CharlotteDunois\Phoebe\InternalWork($work);
                $this->workQueue->enqueue($iwork);

                return $iwork->getDeferred()->promise();
            }

            $worker = $this->spawnWorker();
        }

        $worker = $this->checkAlive($worker);

        $this->busyWorkers->add($worker);
        $worker->stack($work);

        if($this->messenger->usesSockets()) {
            $msg = new \CharlotteDunois\Phoebe\Message('internal-worker-stack-increased', null);
            $this->sendMessageToWorker($worker, $msg);
        }

        return \React\Promise\resolve($worker);
    }

function submit ( \ Threaded $ work ) { if ( $ this -> timer === null ) { $ this -> attachTimer ( ) ; } $ worker = ( $ this -> freeWorkers -> count ( ) > 0 ? $ this -> freeWorkers -> dequeue ( ) : null ) ; if ( ! $ worker ) { if ( \ count ( $ this -> workers ) >= $ this -> size ) { $ iwork = new \ CharlotteDunois \ Phoebe \ InternalWork ( $ work ) ; $ this -> workQueue -> enqueue ( $ iwork ) ; return $ iwork -> getDeferred ( ) -> promise ( ) ; } $ worker = $ this -> spawnWorker ( ) ; } $ worker = $ this -> checkAlive ( $ worker ) ; $ this -> busyWorkers -> add ( $ worker ) ; $ worker -> stack ( $ work ) ; if ( $ this -> messenger -> usesSockets ( ) ) { $ msg = new \ CharlotteDunois \ Phoebe \ Message ( 'internal-worker-stack-increased' , null ) ; $ this -> sendMessageToWorker ( $ worker , $ msg ) ; } return \ React \ Promise \ resolve ( $ worker ) ; }

Submits the work to a worker. Resolves with the worker who accepted the job.
@param \Threaded  $work
@return \React\Promise\ExtendedPromiseInterface

Submits the work to a worker . Resolves with the worker who accepted the job .


function submitTo(string $workerID, \Threaded $work) {
        if($this->timer === null) {
            $this->attachTimer();
        }

        if(!isset($this->workers[$workerID])) {
            throw new \InvalidArgumentException('Worker does not exist');
        }

        $worker = $this->checkAlive($this->workers[$workerID]);

        $this->busyWorkers->add($worker);
        $worker->stack($work);

        if($this->messenger->usesSockets()) {
            $msg = new \CharlotteDunois\Phoebe\Message('internal-worker-stack-increased', null);
            $this->sendMessageToWorker($worker, $msg);
        }

        return $worker;
    }

function
submitTo
(
string
$
workerID
,
\
Threaded
$
work
)
{
if
(
$
this
->
timer
===
)
{
$
this
->
attachTimer
(
)
;
}
if
(
!
isset
(
$
this
->
workers
[
$
workerID
]
)
)
{
throw
new
\
InvalidArgumentException
(
'Worker does not exist'
)
;
}
$
worker
=
$
this
->
checkAlive
(
$
this
->
workers
[
$
workerID
]
)
;
$
this
->
busyWorkers
->
add
(
$
worker
)
;
$
worker
->
stack
(
$
work
)
;
if
(
$
this
->
messenger
->
usesSockets
(
)
)
{
$
msg
=
new
\
CharlotteDunois
\
Phoebe
\
Message
(
'internal-worker-stack-increased'
,
)
;
$
this
->
sendMessageToWorker
(
$
worker
,
$
msg
)
;
}
return
$
worker
;
}

Submits the work to a specific worker.
@param string                          $workerID
@param \Threaded                       $work
@return \CharlotteDunois\Phoebe\Worker
@throws \InvalidArgumentException


function submitTask(\CharlotteDunois\Phoebe\AsyncTask $task) {
        if(!$task->id) {
            throw new \InvalidArgumentException('Async Task has no ID - did you call parent::__construct() in your constructor?');
        }

        return $this->submit($task)->then(function (\CharlotteDunois\Phoebe\Worker $worker) use (&$task) {
            $itask = new \CharlotteDunois\Phoebe\InternalTask($worker, $task);
            $this->tasks->set($task->id, $itask);

            return $itask->getDeferred()->promise();
        });
    }

Submits the task to a worker and returns a promise which resolves or rejects with the resolve value of the task.
@param \CharlotteDunois\Phoebe\AsyncTask  $task
@return \React\Promise\ExtendedPromiseInterface
@throws \InvalidArgumentException

Submits the task to a worker and returns a promise which resolves or rejects with the resolve value of the task .


function submitTaskTo(string $workerID, \CharlotteDunois\Phoebe\AsyncTask $task) {
        if(!$task->id) {
            throw new \InvalidArgumentException('Async Task has no ID - did you call parent::__construct() in your constructor?');
        }

        $worker = $this->submitTo($workerID, $task);

        $itask = new \CharlotteDunois\Phoebe\InternalTask($worker, $task);
        $this->tasks->set($task->id, $itask);

        return $itask->getDeferred()->promise();
    }

Submits the task to a specific worker and returns a promise which resolves or rejects with the resolve value of the task.
@param string                             $workerID
@param \CharlotteDunois\Phoebe\AsyncTask  $task
@return \React\Promise\ExtendedPromiseInterface
@throws \InvalidArgumentException

Submits the task to a specific worker and returns a promise which resolves or rejects with the resolve value of the task .


function stopWorker(\CharlotteDunois\Phoebe\Worker $worker) {
        if($worker->isRunning()) {
            try {
                $message = new \CharlotteDunois\Phoebe\Message('internal-worker-exit', null);
                $this->sendMessageToWorker($worker, $message);
            } catch (\RuntimeException $e) {
                /* Continue regardless of error */
            }

            $this->checkWorkerShutdown($worker);
        } else {
            $this->messenger->removeWorkerSocket($worker);
            $this->emit('exitWorker', $worker);
        }

        $this->removeWorker($worker);
    }

function
stopWorker
(
\
CharlotteDunois
\
Phoebe
\
Worker
$
worker
)
{
if
(
$
worker
->
isRunning
(
)
)
{
try
{
$
message
=
new
\
CharlotteDunois
\
Phoebe
\
Message
(
'internal-worker-exit'
,
)
;
$
this
->
sendMessageToWorker
(
$
worker
,
$
message
)
;
}
catch
(
\
RuntimeException
$
e
)
{
/* Continue regardless of error */
}
$
this
->
checkWorkerShutdown
(
$
worker
)
;
}
else
{
$
this
->
messenger
->
removeWorkerSocket
(
$
worker
)
;
$
this
->
emit
(
'exitWorker'
,
$
worker
)
;
}
$
this
->
removeWorker
(
$
worker
)
;
}

Stops a worker correctly and gracefully.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return void

Stops a worker correctly and gracefully .


protected function removeWorker(\CharlotteDunois\Phoebe\Worker $worker) {
        $msgs = $worker->getMessages();
        while($message = $msgs->shift()) {
            $this->handleMessage($worker, $message);
        }

        unset($this->workers[$worker->id]);
        $this->busyWorkers->delete($worker);

        $size = $this->freeWorkers->count();
        for($i = 0; $i < $size; $i++) {
            $free = $this->freeWorkers->dequeue();
            if($free === $worker) {
                break;
            }

            $this->freeWorkers->enqueue($free);
        }
    }

protected function removeWorker ( \ CharlotteDunois \ Phoebe \ Worker $ worker ) { $ msgs = $ worker -> getMessages ( ) ; while ( $ message = $ msgs -> shift ( ) ) { $ this -> handleMessage ( $ worker , $ message ) ; } unset ( $ this -> workers [ $ worker -> id ] ) ; $ this -> busyWorkers -> delete ( $ worker ) ; $ size = $ this -> freeWorkers -> count ( ) ; for ( $ i = 0 ; $ i < $ size ; $ i ++ ) { $ free = $ this -> freeWorkers -> dequeue ( ) ; if ( $ free === $ worker ) { break ; } $ this -> freeWorkers -> enqueue ( $ free ) ; } }

Removes a worker correctly.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return void


function sendMessageToWorker(\CharlotteDunois\Phoebe\Worker $worker, \CharlotteDunois\Phoebe\Message $message) {
        if(!$worker->isRunning()) {
            throw new \RuntimeException('Tried to send a message to a non-running worker');
        }

        if($this->messenger->usesSockets()) {
            $socket = $this->messenger->getWorkerSocket($worker);
            if($socket === null) {
                throw new \RuntimeException('Tried to send a message to a worker with a closed socket (shutting down)');
            }

            $data = \CharlotteDunois\Phoebe\Socket::encodeMessage($message);
            $socket->write($data);
        } else {
            $worker->sendMessageToWorker($message);
        }
    }

Sends a message to the worker thread.
@param \CharlotteDunois\Phoebe\Worker   $worker
@param \CharlotteDunois\Phoebe\Message  $message
@return void
@throws \RuntimeException


function broadcastMessage(\CharlotteDunois\Phoebe\Message $message, ?\CharlotteDunois\Phoebe\Worker $skip = null) {
        foreach($this->workers as $worker) {
            if(!$worker->isRunning()) {
                $this->loop->futureTick(function () use ($worker) {
                    $this->stopWorker($worker);
                });
            } elseif($skip !== $worker) {
                $this->sendMessageToWorker($worker, $message);
            }
        }
    }

function broadcastMessage ( \ CharlotteDunois \ Phoebe \ Message $ message , ? \ CharlotteDunois \ Phoebe \ Worker $ skip = null ) { foreach ( $ this -> workers as $ worker ) { if ( ! $ worker -> isRunning ( ) ) { $ this -> loop -> futureTick ( function ( ) use ( $ worker ) { $ this -> stopWorker ( $ worker ) ; } ) ; } elseif ( $ skip !== $ worker ) { $ this -> sendMessageToWorker ( $ worker , $ message ) ; } } }

Broadcasts a message to all workers.
@param \CharlotteDunois\Phoebe\Message      $message
@param \CharlotteDunois\Phoebe\Worker|null  $skip     The worker to skip, or null.
@return void


function timerTick() {
        foreach($this->workers as $pos => $worker) {
            if(!$worker->isRunning()) {
                $this->loop->futureTick(function () use ($worker) {
                    $this->stopWorker($worker);
                });

                continue;
            }

            $msgs = $worker->getMessages();
            while($message = $msgs->shift()) {
                $this->handleMessage($worker, $message);
            }
        }

        foreach($this->busyWorkers as $worker) {
            if($worker->getStacked() === 0) {
                if($this->workQueue->count() > 0) {
                    /** @var \CharlotteDunois\Phoebe\InternalWork  $work */
                    $work = $this->workQueue->dequeue();

                    $worker->stack($work->getWork());

                    if($this->messenger->usesSockets()) {
                        $msg = new \CharlotteDunois\Phoebe\Message('internal-worker-stack-increased', null);
                        $this->sendMessageToWorker($worker, $msg);
                    }

                    $work->getDeferred()->resolve($worker);
                    continue;
                }

                $this->busyWorkers->delete($worker);
                $this->freeWorkers->enqueue($worker);
            }
        }

        if(!$this->lastTime || (\time() - $this->lastTime) >= ($this->options['internalTimerTick'] ?? 30)) {
            $this->lastTime = \time();
            $this->internalTimerTick();
        }
    }

Timer tick. Used when using the timer implementation.
@return void
@internal

Timer tick . Used when using the timer implementation .


protected function internalTimerTick() {
        if($this->messenger->usesSockets()) {
            foreach($this->workers as $worker) {
                if(!$worker->isRunning()) {
                    $this->stopWorker($worker);
                }
            }
        }

        $this->handleResize();
        $this->handleTasksTimeout();
    }

protected function internalTimerTick ( ) { if ( $ this -> messenger -> usesSockets ( ) ) { foreach ( $ this -> workers as $ worker ) { if ( ! $ worker -> isRunning ( ) ) { $ this -> stopWorker ( $ worker ) ; } } } $ this -> handleResize ( ) ; $ this -> handleTasksTimeout ( ) ; }


protected function spawnWorker() {
        $workerName = $this->worker;

        /** @var \CharlotteDunois\Phoebe\Worker  $worker */
        $worker = new $workerName(...$this->ctor);
        $this->messenger->prepareWorker($worker);

        $worker->start(static::WORKER_START_OPTIONS);
        $this->workers[$worker->id] = $worker;

        return $worker;
    }

Spawns a new worker.
@return \CharlotteDunois\Phoebe\Worker
@throws \Throwable


protected function checkAlive(\CharlotteDunois\Phoebe\Worker $worker) {
        if(!$worker->isRunning()) {
            $this->emit('debug', 'Picked worker '.$worker->id.' is not running, spawning new one...');

            $this->removeWorker($worker);
            return $this->spawnWorker();
        }

        return $worker;
    }

protected
function
checkAlive
(
\
CharlotteDunois
\
Phoebe
\
Worker
$
worker
)
{
if
(
!
$
worker
->
isRunning
(
)
)
{
$
this
->
emit
(
'debug'
,
'Picked worker '
.
$
worker
->
id
.
' is not running, spawning new one...'
)
;
$
this
->
removeWorker
(
$
worker
)
;
return
$
this
->
spawnWorker
(
)
;
}
return
$
worker
;
}

Checks whether the worker is alive, or spawns a new one.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return \CharlotteDunois\Phoebe\Worker

Checks whether the worker is alive or spawns a new one .


protected function checkWorkerShutdown(\CharlotteDunois\Phoebe\Worker $worker) {
        $this->loop->futureTick(function () use ($worker) {
            if($worker->isRunning()) {
                return $this->checkWorkerShutdown($worker);
            }

            $this->messenger->removeWorkerSocket($worker);
            $this->emit('exitWorker', $worker);
        });
    }

protected function checkWorkerShutdown ( \ CharlotteDunois \ Phoebe \ Worker $ worker ) { $ this -> loop -> futureTick ( function ( ) use ( $ worker ) { if ( $ worker -> isRunning ( ) ) { return $ this -> checkWorkerShutdown ( $ worker ) ; } $ this -> messenger -> removeWorkerSocket ( $ worker ) ; $ this -> emit ( 'exitWorker' , $ worker ) ; } ) ; }

Checks periodically whether the worker has shutdown.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return void

Checks periodically whether the worker has shutdown .


function handleMessage(\CharlotteDunois\Phoebe\Worker $worker, \CharlotteDunois\Phoebe\Message $message) {
        $type = $message->getType();

        switch($type) {
            case 'internal-error-handling':
                $error = \CharlotteDunois\Phoebe\Message::importException($message->getPayload());
                $this->emit('error', $error);
            break;
            case 'internal-thread-done':
                $this->handleThreadDone($message);
            break;
            case 'internal-worker-stack-free':
                if($this->workQueue->count() > 0) {
                    /** @var \CharlotteDunois\Phoebe\InternalWork  $work */
                    $work = $this->workQueue->dequeue();

                    $worker->stack($work->getWork());

                    if($this->messenger->usesSockets()) {
                        $msg = new \CharlotteDunois\Phoebe\Message('internal-worker-stack-increased', null);
                        $this->sendMessageToWorker($worker, $msg);
                    }

                    $work->getDeferred()->resolve($worker);
                    return;
                }

                $this->busyWorkers->delete($worker);
                $this->freeWorkers->enqueue($worker);
            break;
            default:
                $this->emit('message', $worker, $message);
            break;
        }
    }

Handles a message received from a worker.
@param \CharlotteDunois\Phoebe\Worker   $worker
@param \CharlotteDunois\Phoebe\Message  $message
@return void
@internal

Handles a message received from a worker .


function handleStreamClose(\CharlotteDunois\Phoebe\Worker $worker) {
        $this->messenger->removeWorkerSocket($worker);
        $this->removeWorker($worker);

        $this->emit('exitWorker', $worker);
    }

function handleStreamClose ( \ CharlotteDunois \ Phoebe \ Worker $ worker ) { $ this -> messenger -> removeWorkerSocket ( $ worker ) ; $ this -> removeWorker ( $ worker ) ; $ this -> emit ( 'exitWorker' , $ worker ) ; }

Handles a stream close.
@param \CharlotteDunois\Phoebe\Worker  $worker
@return void
@internal


public function readBytes() {
        $binaryLength = $this->read(4);
        if ($binaryLength === "\xff\xff\xff\xff")
            return null;

        $length = unpack('N', $binaryLength)[1];
        return $length === 0 ? '' : $this->read($length);
    }

public function readBytes ( ) { $ binaryLength = $ this -> read ( 4 ) ; if ( $ binaryLength === "\xff\xff\xff\xff" ) return null ; $ length = unpack ( 'N' , $ binaryLength ) [ 1 ] ; return $ length === 0 ? '' : $ this -> read ( $ length ) ; }


public function readUuid() {
        $data = unpack('n8', $this->read(16));

        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', $data[1], $data[2], $data[3], $data[4], $data[5], $data[6], $data[7], $data[8]);
    }

public function readUuid ( ) { $ data = unpack ( 'n8' , $ this -> read ( 16 ) ) ; return sprintf ( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x' , $ data [ 1 ] , $ data [ 2 ] , $ data [ 3 ] , $ data [ 4 ] , $ data [ 5 ] , $ data [ 6 ] , $ data [ 7 ] , $ data [ 8 ] ) ; }


public function readList(array $definition) {
        list($valueType) = $definition;
        $list = [];
        $count = $this->readInt();
        for ($i = 0; $i < $count; ++$i) {
            $list[] = $this->readValue($valueType);
        }
        return $list;
    }

public function readList ( array $ definition ) { list ( $ valueType ) = $ definition ; $ list = [ ] ; $ count = $ this -> readInt ( ) ; for ( $ i = 0 ; $ i < $ count ; ++ $ i ) { $ list [ ] = $ this -> readValue ( $ valueType ) ; } return $ list ; }

Read list.

@param array $definition [$valueType]
@return array


public function readMap(array $definition) {
        list($keyType, $valueType) = $definition;
        $map = [];
        $count = $this->readInt();
        for ($i = 0; $i < $count; ++$i) {
            $map[$this->readValue($keyType)] = $this->readValue($valueType);
        }
        return $map;
    }

public function readMap ( array $ definition ) { list ( $ keyType , $ valueType ) = $ definition ; $ map = [ ] ; $ count = $ this -> readInt ( ) ; for ( $ i = 0 ; $ i < $ count ; ++ $ i ) { $ map [ $ this -> readValue ( $ keyType ) ] = $ this -> readValue ( $ valueType ) ; } return $ map ; }

Read map.

@param array $definition [$keyType, $valueType]
@return array


public function readVarint() {
        list($higher, $lower) = array_values(unpack('N2', $this->data));
        return $higher << 32 | $lower;
    }

public function readVarint ( ) { list ( $ higher , $ lower ) = array_values ( unpack ( 'N2' , $ this -> data ) ) ; return $ higher << 32 | $ lower ; }

Read variable length integer.

@return string


public function readDecimal() {
        $scale = $this->readInt();
        $value = $this->readVarint();
        $len = strlen($value);
        return substr($value, 0, $len - $scale) . '.' . substr($value, $len - $scale);
    }

public function readDecimal ( ) { $ scale = $ this -> readInt ( ) ; $ value = $ this -> readVarint ( ) ; $ len = strlen ( $ value ) ; return substr ( $ value , 0 , $ len - $ scale ) . '.' . substr ( $ value , $ len - $ scale ) ; }

Read variable length decimal.

@return string


public function register()
    {
        $l5 = $this->l5 = version_compare(Application::VERSION, '5.0', '>=');

        $factory = function ($app) use($l5) {
            $delim = $l5 ? '.' : '::';
            $config = $app['config'];
            $username  = $config->get("bulk-sms{$delim}username");
            $password  = $config->get("bulk-sms{$delim}password");
            $baseurl   = $config->get("bulk-sms{$delim}baseurl");

            $curl = isset($app['curl']) ? $app['curl'] : null;

            return new BulkSmsService($username, $password, $baseurl, $curl);
        };

        if (version_compare(Application::VERSION, '5.4', '>=')) {
            $this->app->singleton('bulksms', $factory);
        } else {
            $this->app['bulksms'] = $this->app->share($factory);
        }

        if ($l5) {
            $dir = dirname(dirname(__DIR__)).'/resources';
            $this->mergeConfigFrom($dir.'/config.php', 'bulk-sms');
        }
    }

public function register ( ) { $ l5 = $ this -> l5 = version_compare ( Application :: VERSION , '5.0' , '>=' ) ; $ factory = function ( $ app ) use ( $ l5 ) { $ delim = $ l5 ? '.' : '::' ; $ config = $ app [ 'config' ] ; $ username = $ config -> get ( "bulk-sms{$delim}username" ) ; $ password = $ config -> get ( "bulk-sms{$delim}password" ) ; $ baseurl = $ config -> get ( "bulk-sms{$delim}baseurl" ) ; $ curl = isset ( $ app [ 'curl' ] ) ? $ app [ 'curl' ] : null ; return new BulkSmsService ( $ username , $ password , $ baseurl , $ curl ) ; } ; if ( version_compare ( Application :: VERSION , '5.4' , '>=' ) ) { $ this -> app -> singleton ( 'bulksms' , $ factory ) ; } else { $ this -> app [ 'bulksms' ] = $ this -> app -> share ( $ factory ) ; } if ( $ l5 ) { $ dir = dirname ( dirname ( __DIR__ ) ) . '/resources' ; $ this -> mergeConfigFrom ( $ dir . '/config.php' , 'bulk-sms' ) ; } }

Register the service on the IoC container.

@return void

Register the service on the IoC container .


public function boot()
    {
        $dir = dirname(dirname(__DIR__)).'/resources';

        if ($this->l5) {
            $this->publishes([
                $dir.'/config.php' => config_path('bulk-sms.php')
            ], 'config');
        } else {
            $this->app['config']->package('bulk-sms', $dir, 'bulk-sms');
        }
    }

public function boot ( ) { $ dir = dirname ( dirname ( __DIR__ ) ) . '/resources' ; if ( $ this -> l5 ) { $ this -> publishes ( [ $ dir . '/config.php' => config_path ( 'bulk-sms.php' ) ] , 'config' ) ; } else { $ this -> app [ 'config' ] -> package ( 'bulk-sms' , $ dir , 'bulk-sms' ) ; } }

Load the package config files.

@return void


public function sendMessage($recipient, $message, array $params = null)
    {
        $sender = $this->createMessageSender();

        $msg = $this->createMessage($recipient, $message);

        $sender->setMessage($msg);
        if ($params) {
            $sender->setParams($params);
        }
        $response = $sender->send($this->testMode);
        $this->validateResponse($response);

        return $sender->extractResponse($response);
    }

public function sendMessage ( $ recipient , $ message , array $ params = null ) { $ sender = $ this -> createMessageSender ( ) ; $ msg = $ this -> createMessage ( $ recipient , $ message ) ; $ sender -> setMessage ( $ msg ) ; if ( $ params ) { $ sender -> setParams ( $ params ) ; } $ response = $ sender -> send ( $ this -> testMode ) ; $ this -> validateResponse ( $ response ) ; return $ sender -> extractResponse ( $ response ) ; }

Send a single message.

@param  string $recipient
@param  string $message
@param  array  $params

@return mixed


public function validateResponse($response)
    {
        if ($response->code !== '200 OK') {
            throw new BulkSmsException('BulkSMS API responded with HTTP status code ' . $response->code);
        }

        $parts = explode('|', $response->body);

        if (!is_numeric($parts[ 0 ])) {
            throw new \UnexpectedValueException(
                'Unknown response code: ' . $parts[ 0 ] . ' - full response: ' . $response->body
            );
        }

        $code = (int) $parts[ 0 ];

        if ($code === 0 || $code === 1) {
            return true;
        }

        $message = array_key_exists($code, static::$statusMessages)
            ? static::$statusMessages[ $code ]
            : $parts[ 1 ];
        throw new BulkSmsException('BulkSMS API responded with code: ' . $code . ' - ' . $message);
    }

Validate a response from the API to check for errors

@param  anlutro\cURL\Response $response

@return bool
@throws BulkSmsException


public function sendBulkMessages(array $messages, array $params = null)
    {
        $sender = $this->createBulkSender();
        v::notEmpty()->setName("BulkSms Array")->check($messages);

        foreach ($messages as $message) {
            // make sure messages are proper objects
            v::instance('anlutro\BulkSms\Message')->check($message);
            $sender->addMessage($message);
        }

        if ($params) {
            $sender->setParams($params);
        }
        $response = $sender->send($this->testMode);
        $this->validateResponse($response);

        return $sender->extractResponse($response);
    }

Send messages in bulk.

@param  Message[] $messages
@param  array     $params

@return mixed


public function getStatusForBatchId($bulksmsid)
    {
        $sender   = $this->createBulkStatusSender();
        $response = $sender->getStatusForBatchId($bulksmsid, $this->testMode);
        $this->validateResponse($response);

        return $sender->extractResponse($response);
    }

public function getStatusForBatchId ( $ bulksmsid ) { $ sender = $ this -> createBulkStatusSender ( ) ; $ response = $ sender -> getStatusForBatchId ( $ bulksmsid , $ this -> testMode ) ; $ this -> validateResponse ( $ response ) ; return $ sender -> extractResponse ( $ response ) ; }

Check status for single id

@param  string $bulksmsid

@return mixed


public function send($testmode = false)
    {
        if (empty($this->messages)) {
            return false;
        }

        $data = array_replace($this->params, [
            'username'   => $this->username,
            'password'   => $this->password,
            'batch_data' => $this->generateCSV(),
        ]);

        // add test params if required
        if ($testmode) {
            if ($testmode == BulkSmsService::TEST_ALWAYS_SUCCEED) {
                $data['test_always_succeed'] = 1;
            } elseif ($testmode == BulkSmsService::TEST_ALWAYS_FAIL) {
                $data['test_always_fail'] = 1;
            }
        }

        return $this->curl->post($this->getUrl(), $data);
    }

public
function
send
(
$
testmode
=
false
)
{
if
(
empty
(
$
this
->
messages
)
)
{
return
false
;
}
$
data
=
array_replace
(
$
this
->
params
,
[
'username'
=>
$
this
->
username
,
'password'
=>
$
this
->
password
,
'batch_data'
=>
$
this
->
generateCSV
(
)
,
]
)
;
// add test params if required
if
(
$
testmode
)
{
if
(
$
testmode
==
BulkSmsService
::
TEST_ALWAYS_SUCCEED
)
{
$
data
[
'test_always_succeed'
]
=
1
;
}
elseif
(
$
testmode
==
BulkSmsService
::
TEST_ALWAYS_FAIL
)
{
$
data
[
'test_always_fail'
]
=
1
;
}
}
return
$
this
->
curl
->
post
(
$
this
->
getUrl
(
)
,
$
data
)
;
}

Send the queued messages.

@param bool $testmode Testmode to use

@return mixed


protected function generateCSV()
    {
        $str = "msisdn,message";

        foreach ($this->messages as $message) {
            $str .= "\n";
            $recipient = $message->getRecipient();
            $message   = $message->getMessage();
            $str .= '"' . $recipient . '","' . $message . '"';
        }

        return $str;
    }

protected function generateCSV ( ) { $ str = "msisdn,message" ; foreach ( $ this -> messages as $ message ) { $ str .= "\n" ; $ recipient = $ message -> getRecipient ( ) ; $ message = $ message -> getMessage ( ) ; $ str .= '"' . $ recipient . '","' . $ message . '"' ; } return $ str ; }

Generate the CSV to send.

@return string


public function send($testmode = false)
    {
        $data = array_replace($this->params, [
            'username' => $this->username,
            'password' => $this->password,
            'message'  => $this->message->getMessage(),
            'msisdn'   => $this->message->getRecipient(),
        ]);

        $concat = $this->message->getConcatParts();

        if ($concat > 1) {
            $data['allow_concat_text_sms']     = 1;
            $data['concat_text_sms_max_parts'] = $concat;
        }

        // add test params if required
        if ($testmode) {
            if ($testmode == BulkSmsService::TEST_ALWAYS_SUCCEED) {
                $data['test_always_succeed'] = 1;
            } elseif ($testmode == BulkSmsService::TEST_ALWAYS_FAIL) {
                $data['test_always_fail'] = 1;
            }
        }

        return $this->curl->post($this->getUrl(), $data);
    }

public
function
send
(
$
testmode
=
false
)
{
$
data
=
array_replace
(
$
this
->
params
,
[
'username'
=>
$
this
->
username
,
'password'
=>
$
this
->
password
,
'message'
=>
$
this
->
message
->
getMessage
(
)
,
'msisdn'
=>
$
this
->
message
->
getRecipient
(
)
,
]
)
;
$
concat
=
$
this
->
message
->
getConcatParts
(
)
;
if
(
$
concat
>
1
)
{
$
data
[
'allow_concat_text_sms'
]
=
1
;
$
data
[
'concat_text_sms_max_parts'
]
=
$
concat
;
}
// add test params if required
if
(
$
testmode
)
{
if
(
$
testmode
==
BulkSmsService
::
TEST_ALWAYS_SUCCEED
)
{
$
data
[
'test_always_succeed'
]
=
1
;
}
elseif
(
$
testmode
==
BulkSmsService
::
TEST_ALWAYS_FAIL
)
{
$
data
[
'test_always_fail'
]
=
1
;
}
}
return
$
this
->
curl
->
post
(
$
this
->
getUrl
(
)
,
$
data
)
;
}

Send the message.

@param bool $testmode Testmode to use

@return mixed


protected function setMessage($message)
    {
        $this->message = $this->encodeMessage($message);

        if (strlen($this->message) > $this->concatLimit) {
            $this->concat = true;
        }

        return $this;
    }

protected function setMessage ( $ message ) { $ this -> message = $ this -> encodeMessage ( $ message ) ; if ( strlen ( $ this -> message ) > $ this -> concatLimit ) { $ this -> concat = true ; } return $ this ; }

Set the message.

@param  string $message

@return $this


protected function calculateConcat()
    {
        $len = strlen($this->message);
        $i   = $this->concatLimit;
        $j   = 1;

        while ($i < $len) {
            $i += $this->concatLimit;
            $j++;
        }

        return $j;
    }

protected function calculateConcat ( ) { $ len = strlen ( $ this -> message ) ; $ i = $ this -> concatLimit ; $ j = 1 ; while ( $ i < $ len ) { $ i += $ this -> concatLimit ; $ j ++ ; } return $ j ; }

Calculate from the message string how many SMSes it may have to span.

@return int

Calculate from the message string how many SMSes it may have to span .


protected function parseNumber($number)
    {
        $number = (string) $number;

        // remove whitespaces
        $number = trim($number);
        $number = str_replace(' ', '', $number);

        // remove + in front if exists
        if (substr($number, 0, 1) == '+') {
            $number = substr($number, 1);
        }

        // remove 0s in front if exists
        while (substr($number, 0, 1) === '0') {
            $number = substr($number, 1);
        }

        // we should at this point have a normal number
        if (!is_numeric($number)) {
            throw new \InvalidArgumentException("Invalid SMS recipient: $number");
        }

        // is phone number is less than 9 characters, assume we need to append
        // a country code
        if (strlen($number) <= 8) {
            throw new \InvalidArgumentException(
                "Recipient number is too short. Is the country code missing?: " . $number
            );
        }

        return $number;
    }

Parse a phone number.

@param  string $number

@return string


public function send($batchid)
    {
        if (empty($batchid)) {
            throw new \InvalidArgumentException("Batch Id must not be empty");
        }

        $data = [
            'username' => $this->username,
            'password' => $this->password,
            'batch_id' => $batchid,
        ];

        return $this->curl->get($this->getUrl(), $data);
    }

public
function
send
(
$
batchid
)
{
if
(
empty
(
$
batchid
)
)
{
throw
new
\
InvalidArgumentException
(
"Batch Id must not be empty"
)
;
}
$
data
=
[
'username'
=>
$
this
->
username
,
'password'
=>
$
this
->
password
,
'batch_id'
=>
$
batchid
,
]
;
return
$
this
->
curl
->
get
(
$
this
->
getUrl
(
)
,
$
data
)
;
}

Send the status query

@param $batchid

@return \anlutro\cURL\Response|void