public function queueTask($request)
    {
        $name = $request->param('TaskName');
        $tasks = $this->getTasks();

        $variables = $request->getVars();
        unset($variables['url']);
        unset($variables['flush']);
        unset($variables['flushtoken']);
        unset($variables['isDev']);
        $querystring = http_build_query($variables);

        $title = function ($content) {
            printf(Director::is_cli() ? "%s\n\n" : '
%s
', $content);
        };

        $message = function ($content) {
            printf(Director::is_cli() ? "%s\n" : '
%s
', $content);
        };

        foreach ($tasks as $task) {
            if ($task['segment'] == $name) {
                /** @var BuildTask $inst */
                $inst = Injector::inst()->create($task['class']);
                if (!$inst->isEnabled()) {
                    $message('The task is disabled');
                    return;
                }

                $title(sprintf('Queuing Task %s', $inst->getTitle()));

                $job = new RunBuildTaskJob($task['class'], $querystring);
                $jobID = Injector::inst()->get(QueuedJobService::class)->queueJob($job);

                $message('Done: queued with job ID ' . $jobID);
                $adminUrl = Director::baseURL() . AdminRootController::config()->get('url_base');
                $adminLink = $adminUrl . "/queuedjobs/" . str_replace('\\', '-', QueuedJobDescriptor::class);
                $message("Visit queued jobs admin to see job status");
                return;
            }
        }

        $message(sprintf('The build task "%s" could not be found', Convert::raw2xml($name)));
    }

Adds a RunBuildTaskJob to the job queue for a given task

@param HTTPRequest $request


protected function logDescriptorStatus($descriptor, $queue)
    {
        if (is_null($descriptor)) {
            $this->writeLogLine('No new jobs on queue ' . $queue);
        }

        if ($descriptor === false) {
            $this->writeLogLine('Job is still running on queue ' . $queue);
        }

        if ($descriptor instanceof QueuedJobDescriptor) {
            $this->writeLogLine('Running ' . $descriptor->JobTitle . ' and others from queue ' . $queue . '.');
        }
    }

protected
function
logDescriptorStatus
(
$
descriptor
,
$
queue
)
{
if
(
is_null
(
$
descriptor
)
)
{
$
this
->
writeLogLine
(
'No new jobs on queue '
.
$
queue
)
;
}
if
(
$
descriptor
===
false
)
{
$
this
->
writeLogLine
(
'Job is still running on queue '
.
$
queue
)
;
}
if
(
$
descriptor
instanceof
QueuedJobDescriptor
)
{
$
this
->
writeLogLine
(
'Running '
.
$
descriptor
->
JobTitle
.
' and others from queue '
.
$
queue
.
'.'
)
;
}
}

Logs the status of the queued job descriptor.

@param bool|null|QueuedJobDescriptor $descriptor
@param string $queue

Logs the status of the queued job descriptor .


public function listJobs()
    {
        $service = $this->getService();
        for ($i = 1; $i <= 3; $i++) {
            $jobs = $service->getJobList($i);
            $num = $jobs ? $jobs->Count() : 0;
            $this->writeLogLine('Found ' . $num . ' jobs for mode ' . $i . '.');
        }
    }

public
function
listJobs
(
)
{
$
service
=
$
this
->
getService
(
)
;
for
(
$
i
=
1
;
$
i
<=
3
;
$
i
++
)
{
$
jobs
=
$
service
->
getJobList
(
$
i
)
;
$
num
=
$
jobs
$
jobs
->
Count
(
)
:
0
;
$
this
->
writeLogLine
(
'Found '
.
$
num
.
' jobs for mode '
.
$
i
.
'.'
)
;
}
}

Logs the number of current jobs per queue


protected function refreshDescriptor()
    {
        if ($this->descriptor) {
            $this->descriptor = QueuedJobDescriptor::get()->byID($this->descriptor->ID);
        }
    }

protected function refreshDescriptor ( ) { if ( $ this -> descriptor ) { $ this -> descriptor = QueuedJobDescriptor :: get ( ) -> byID ( $ this -> descriptor -> ID ) ; } }


public function run($request)
    {
        $queue = $request->requestVar('queue') ?: QueuedJob::QUEUED;

        $stalledJobCount = $this->getService()->checkJobHealth($queue);

        echo $stalledJobCount === 0 ? 'All jobs are healthy' : 'Detected and attempted restart on ' . $stalledJobCount .
            ' stalled jobs';
    }

public
function
run
(
$
request
)
{
$
queue
=
$
request
->
requestVar
(
'queue'
)
:
QueuedJob
::
QUEUED
;
$
stalledJobCount
=
$
this
->
getService
(
)
->
checkJobHealth
(
$
queue
)
;
echo
$
stalledJobCount
===
0
'All jobs are healthy'
:
'Detected and attempted restart on '
.
$
stalledJobCount
.
' stalled jobs'
;
}

Implement this method in the task subclass to
execute via the TaskRunner

@param HTTPRequest $request
@return


public function pause($force = false)
    {
        if ($force || in_array(
            $this->JobStatus,
            [QueuedJob::STATUS_WAIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_INIT]
        )) {
            $this->JobStatus = QueuedJob::STATUS_PAUSED;
            $this->write();
            return true;
        }
        return false;
    }

public function pause ( $ force = false ) { if ( $ force || in_array ( $ this -> JobStatus , [ QueuedJob :: STATUS_WAIT , QueuedJob :: STATUS_RUN , QueuedJob :: STATUS_INIT ] ) ) { $ this -> JobStatus = QueuedJob :: STATUS_PAUSED ; $ this -> write ( ) ; return true ; } return false ; }

Pause this job, but only if it is waiting, running, or init

@param bool $force Pause this job even if it's not waiting, running, or init

@return bool Return true if this job was paused

Pause this job but only if it is waiting running or init


public function resume($force = false)
    {
        if ($force || in_array($this->JobStatus, [QueuedJob::STATUS_PAUSED, QueuedJob::STATUS_BROKEN])) {
            $this->JobStatus = QueuedJob::STATUS_WAIT;
            $this->ResumeCounts++;
            $this->write();
            QueuedJobService::singleton()->startJob($this);
            return true;
        }
        return false;
    }

public function resume ( $ force = false ) { if ( $ force || in_array ( $ this -> JobStatus , [ QueuedJob :: STATUS_PAUSED , QueuedJob :: STATUS_BROKEN ] ) ) { $ this -> JobStatus = QueuedJob :: STATUS_WAIT ; $ this -> ResumeCounts ++ ; $ this -> write ( ) ; QueuedJobService :: singleton ( ) -> startJob ( $ this ) ; return true ; } return false ; }

Resume this job and schedules it for execution

@param bool $force Resume this job even if it's not paused or broken

@return bool Return true if this job was resumed


public function activateOnQueue()
    {
        // if it's an immediate job, lets cache it to disk to be picked up later
        if ($this->JobType == QueuedJob::IMMEDIATE
            && !Config::inst()->get(QueuedJobService::class, 'use_shutdown_function')
        ) {
            touch($this->getJobDir() . '/queuedjob-' . $this->ID);
        }
    }

Called to indicate that the job is ready to be run on the queue. This is done either as the result of
creating the job and adding it, or when resuming.

Called to indicate that the job is ready to be run on the queue . This is done either as the result of creating the job and adding it or when resuming .


protected function getJobDir()
    {
        // make sure our temp dir is in place. This is what will be inotify watched
        $jobDir = Config::inst()->get(QueuedJobService::class, 'cache_dir');
        if ($jobDir{0} !== '/') {
            $jobDir = TEMP_FOLDER . '/' . $jobDir;
        }

        if (!is_dir($jobDir)) {
            Filesystem::makeFolder($jobDir);
        }
        return $jobDir;
    }

Gets the path to the queuedjob cache directory

@return string


public function cleanupJob()
    {
        // remove the job's temp file if it exists
        $tmpFile = $this->getJobDir() . '/queuedjob-' . $this->ID;
        if (file_exists($tmpFile)) {
            unlink($tmpFile);
        }
    }

Called when the job has completed and we want to cleanup anything the descriptor has lying around
in caches or the like.

Called when the job has completed and we want to cleanup anything the descriptor has lying around in caches or the like .


public function getMessages()
    {
        if (strlen($this->SavedJobMessages)) {
            $messages = @unserialize($this->SavedJobMessages);
            if (!empty($messages)) {
                return DBField::create_field(
                    'HTMLText',
                    '

' . nl2br(implode('

', Convert::raw2xml($messages))) . '

'
                );
            }
            return '';
        }
    }

public function getMessages ( ) { if ( strlen ( $ this -> SavedJobMessages ) ) { $ messages = @ unserialize ( $ this -> SavedJobMessages ) ; if ( ! empty ( $ messages ) ) { return DBField :: create_field ( 'HTMLText' , '

' . nl2br ( implode ( '

' , Convert :: raw2xml ( $ messages ) ) ) . '

' ) ; } return '' ; } }

Get all job messages as an HTML unordered list.

@return string|null

Get all job messages as an HTML unordered list .


public function getLastMessage()
    {
        if (strlen($this->SavedJobMessages)) {
            $msgs = @unserialize($this->SavedJobMessages);
            if (is_array($msgs) && sizeof($msgs)) {
                return array_pop($msgs);
            }
        }
    }

public function getLastMessage ( ) { if ( strlen ( $ this -> SavedJobMessages ) ) { $ msgs = @ unserialize ( $ this -> SavedJobMessages ) ; if ( is_array ( $ msgs ) && sizeof ( $ msgs ) ) { return array_pop ( $ msgs ) ; } } }

Get the last job message as a raw string

@return string|null


public function getJobTypeString()
    {
        $map = $this->getJobTypeValues();
        return isset($map[$this->JobType]) ? $map[$this->JobType] : '(Unknown)';
    }

public function getJobTypeString ( ) { $ map = $ this -> getJobTypeValues ( ) ; return isset ( $ map [ $ this -> JobType ] ) ? $ map [ $ this -> JobType ] : '(Unknown)' ; }

Return a string representation of the numeric JobType
@return string


public function getJobTypeValues()
    {
        return [
            QueuedJob::IMMEDIATE => _t(__CLASS__ . '.TYPE_IMMEDIATE', 'Immediate'),
            QueuedJob::QUEUED => _t(__CLASS__ . '.TYPE_QUEUED', 'Queued'),
            QueuedJob::LARGE => _t(__CLASS__ . '.TYPE_LARGE', 'Large'),
        ];
    }

public function getJobTypeValues ( ) { return [ QueuedJob :: IMMEDIATE => _t ( __CLASS__ . '.TYPE_IMMEDIATE' , 'Immediate' ) , QueuedJob :: QUEUED => _t ( __CLASS__ . '.TYPE_QUEUED' , 'Queued' ) , QueuedJob :: LARGE => _t ( __CLASS__ . '.TYPE_LARGE' , 'Large' ) , ] ; }

Return a map of numeric JobType values to localisable string representations.
@return array

Return a map of numeric JobType values to localisable string representations .


public function setup()
    {
        parent::setup();
        Environment::increaseTimeLimitTo();

        $restart = $this->currentStep == 0;
        if (!$this->tempFile || !file_exists($this->tempFile)) {
            $tmpfile = tempnam(TempFolder::getTempFolder(BASE_PATH), 'sitemap');
            if (file_exists($tmpfile)) {
                $this->tempFile = $tmpfile;
            }
            $restart = true;
        }

        if ($restart) {
            $this->pagesToProcess = DB::query('SELECT ID FROM SiteTree_Live WHERE ShowInSearch=1')->column();
        }
    }

Note that this is duplicated for backwards compatibility purposes...

Note that this is duplicated for backwards compatibility purposes ...


public function prepareForRestart()
    {
        parent::prepareForRestart();
        // if the file we've been building is missing, lets fix it up
        if (!$this->tempFile || !file_exists($this->tempFile)) {
            $tmpfile = tempnam(TempFolder::getTempFolder(BASE_PATH), 'sitemap');
            if (file_exists($tmpfile)) {
                $this->tempFile = $tmpfile;
            }
            $this->currentStep = 0;
            $this->pagesToProcess = DB::query('SELECT ID FROM SiteTree_Live WHERE ShowInSearch=1')->column();
        }
    }

On any restart, make sure to check that our temporary file is being created still.

On any restart make sure to check that our temporary file is being created still .


protected function completeJob()
    {
        $content = '<?xml version="1.0" encoding="UTF-8"?>' .
                    '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
        $content .= file_get_contents($this->tempFile);
        $content .= '</urlset>';

        $sitemap = Director::baseFolder() . '/sitemap.xml';

        file_put_contents($sitemap, $content);

        if (file_exists($this->tempFile)) {
            unlink($this->tempFile);
        }

        $nextgeneration = Injector::inst()->create(GenerateGoogleSitemapJob::class);
        QueuedJobService::singleton()->queueJob(
            $nextgeneration,
            DBDatetime::create()->setValue(
                DBDatetime::now()->getTimestamp() + $this->config()->get('regenerate_time')
            )->Rfc2822()
        );
    }

Outputs the completed file to the site's webroot

Outputs the completed file to the site s webroot


public function runQueue($queue)
    {
        // split jobs out into multiple tasks...

        /** @var ProcessManager $manager */
        $manager = Injector::inst()->create(ProcessManager::class);
        $manager->setWorker(
            BASE_PATH . "/vendor/silverstripe/framework/cli-script.php dev/tasks/ProcessJobQueueChildTask"
        );
        $logPath = Environment::getEnv('SS_DOORMAN_LOGPATH');
        if ($logPath) {
            $manager->setLogPath($logPath);
        }

        // Assign default rules
        $defaultRules = $this->getDefaultRules();
        if ($defaultRules) {
            foreach ($defaultRules as $rule) {
                $manager->addRule($rule);
            }
        }

        $descriptor = $this->getNextJobDescriptorWithoutMutex($queue);

        while ($manager->tick() || $descriptor) {
            $this->logDescriptorStatus($descriptor, $queue);

            if ($descriptor instanceof QueuedJobDescriptor) {
                $descriptor->JobStatus = QueuedJob::STATUS_INIT;
                $descriptor->write();

                $manager->addTask(new DoormanQueuedJobTask($descriptor));
            }

            sleep(1);

            $descriptor = $this->getNextJobDescriptorWithoutMutex($queue);
        }
    }

Run tasks on the given queue

@param string $queue


public function handleAction(GridField $gridField, $actionName, $arguments, $data)
    {
        $actions = $this->getActions(null);
        if (in_array($actionName, $actions)) {
            $item = $gridField->getList()->byID($arguments['RecordID']);
            if (!$item) {
                return;
            }
            $item->$actionName();
            Requirements::clear();
        }
    }

public function handleAction ( GridField $ gridField , $ actionName , $ arguments , $ data ) { $ actions = $ this -> getActions ( null ) ; if ( in_array ( $ actionName , $ actions ) ) { $ item = $ gridField -> getList ( ) -> byID ( $ arguments [ 'RecordID' ] ) ; if ( ! $ item ) { return ; } $ item -> $ actionName ( ) ; Requirements :: clear ( ) ; } }

Handle the actions and apply any changes to the GridField

@param GridField $gridField
@param string $actionName
@param mixed $arguments
@param array $data - form data
@return void


public function getTitle()
    {
        $title = 'Unknown';

        if ($root = $this->getRoot()) {
            $title = $root->Title;
        }

        return _t(
            __CLASS__ . '.Title',
            "Publish items beneath {title}",
            ['title' => $title]
        );
    }

public
function
getTitle
(
)
{
$
title
=
'Unknown'
;
if
(
$
root
=
$
this
->
getRoot
(
)
)
{
$
title
=
$
root
->
Title
;
}
return
_t
(
__CLASS__
.
'.Title'
,
"Publish items beneath {title}"
,
[
'title'
=>
$
title
]
)
;
}

Defines the title of the job

@return string


public function setup()
    {
        if (!$this->getRoot()) {
            // we're missing for some reason!
            $this->isComplete = true;
            $this->remainingChildren = array();
            return;
        }
        $remainingChildren = array();
        $remainingChildren[] = $this->getRoot()->ID;
        $this->remainingChildren = $remainingChildren;

        // we reset this to 1; this is because we only know for sure about 1 item remaining
        // as time goes by, this will increase as we discover more items that need processing
        $this->totalSteps = 1;
    }

This is called immediately before a job begins - it gives you a chance
to initialise job data and make sure everything's good to go

What we're doing in our case is to queue up the list of items we know we need to
process still (it's not everything - just the ones we know at the moment)

When we go through, we'll constantly add and remove from this queue, meaning
we never overload it with content

This is called immediately before a job begins - it gives you a chance to initialise job data and make sure everything s good to go


public function process()
    {
        $remainingChildren = $this->remainingChildren;

        // if there's no more, we're done!
        if (!count($remainingChildren)) {
            $this->isComplete = true;
            return;
        }

        // we need to always increment! This is important, because if we don't then our container
        // that executes around us thinks that the job has died, and will stop it running.
        $this->currentStep++;

        // lets process our first item - note that we take it off the list of things left to do
        $ID = array_shift($remainingChildren);

        // get the page
        $page = DataObject::get_by_id(Page::class, $ID);
        if ($page) {
            // publish it
            $page->doPublish();

            // and add its children to the list to be published
            foreach ($page->Children() as $child) {
                $remainingChildren[] = $child->ID;
                // we increase how many steps we need to do - this means our total steps constantly rises,
                // but it gives users an idea of exactly how many more we know about
                $this->totalSteps++;
            }
            $page->destroy();
            unset($page);
        }

        // and now we store the new list of remaining children
        $this->remainingChildren = $remainingChildren;

        if (!count($remainingChildren)) {
            $this->isComplete = true;
            return;
        }
    }

Lets process a single node, and publish it if necessary

Lets process a single node and publish it if necessary


protected function getQueue($request)
    {
        $queue = $request->getVar('queue');

        if (!$queue) {
            $queue = 'Queued';
        }

        switch (strtolower($queue)) {
            case 'immediate':
                $queue = QueuedJob::IMMEDIATE;
                break;
            case 'queued':
                $queue = QueuedJob::QUEUED;
                break;
            case 'large':
                $queue = QueuedJob::LARGE;
                break;
            default:
                break;
        }

        return $queue;
    }

protected function getQueue ( $ request ) { $ queue = $ request -> getVar ( 'queue' ) ; if ( ! $ queue ) { $ queue = 'Queued' ; } switch ( strtolower ( $ queue ) ) { case 'immediate' : $ queue = QueuedJob :: IMMEDIATE ; break ; case 'queued' : $ queue = QueuedJob :: QUEUED ; break ; case 'large' : $ queue = QueuedJob :: LARGE ; break ; default : break ; } return $ queue ; }

Resolves the queue name to one of a few aliases.

@todo Solve the "Queued"/"queued" mystery!

@param HTTPRequest $request
@return string

Resolves the queue name to one of a few aliases .


public static function castModel(ActiveRecord $model)
    {
        $attributes = array_merge(
            $model->getAttributes(), $model->getRelatedRecords()
        );
        $results = [];
        foreach ($attributes as $key => $value) {
            $results[Caster::PREFIX_VIRTUAL.$key] = $value;
        }
        return $results;
    }

public static function castModel ( ActiveRecord $ model ) { $ attributes = array_merge ( $ model -> getAttributes ( ) , $ model -> getRelatedRecords ( ) ) ; $ results = [ ] ; foreach ( $ attributes as $ key => $ value ) { $ results [ Caster :: PREFIX_VIRTUAL . $ key ] = $ value ; } return $ results ; }

Get an array representing the properties of a model.

@param \yii\db\ActiveRecord $model
@return array

Get an array representing the properties of a model .


public function actionIndex()
    {
        $config = new Configuration;
        $config->getPresenter()->addCasters(
            $this->getCasters()
        );
        $shell = new Shell($config);
        $shell->setIncludes($this->include);
        $shell->run();
    }

public function actionIndex ( ) { $ config = new Configuration ; $ config -> getPresenter ( ) -> addCasters ( $ this -> getCasters ( ) ) ; $ shell = new Shell ( $ config ) ; $ shell -> setIncludes ( $ this -> include ) ; $ shell -> run ( ) ; }


public function saveMap($content)
    {
        $asset_handler = Requirements::backend()->getAssetHandler();

        $css_file = $this->options['sourceMapWriteTo'];
        $asset_handler->setContent($css_file, $content);
        $url = $asset_handler->getContentURL($css_file);

        $this->options['sourceMapURL'] = $url;
        return $this->options['sourceMapURL'];
    }

public function saveMap ( $ content ) { $ asset_handler = Requirements :: backend ( ) -> getAssetHandler ( ) ; $ css_file = $ this -> options [ 'sourceMapWriteTo' ] ; $ asset_handler -> setContent ( $ css_file , $ content ) ; $ url = $ asset_handler -> getContentURL ( $ css_file ) ; $ this -> options [ 'sourceMapURL' ] = $ url ; return $ this -> options [ 'sourceMapURL' ] ; }

Saves the source map to a file

@param string $content The content to write

@return string URL to saved file


public function write($data)
    {
        $size = mb_strlen($data, 'UTF-8');

        if($this->exists($this->id)) {
            shmop_delete($this->shmid);
            shmop_close($this->shmid);
            $this->shmid = shmop_open($this->id, "c", $this->perms, $size);
            shmop_write($this->shmid, $data, 0);
        } else {
            $this->shmid = shmop_open($this->id, "c", $this->perms, $size);
            shmop_write($this->shmid, $data, 0);
        }
    }

public function write ( $ data ) { $ size = mb_strlen ( $ data , 'UTF-8' ) ; if ( $ this -> exists ( $ this -> id ) ) { shmop_delete ( $ this -> shmid ) ; shmop_close ( $ this -> shmid ) ; $ this -> shmid = shmop_open ( $ this -> id , "c" , $ this -> perms , $ size ) ; shmop_write ( $ this -> shmid , $ data , 0 ) ; } else { $ this -> shmid = shmop_open ( $ this -> id , "c" , $ this -> perms , $ size ) ; shmop_write ( $ this -> shmid , $ data , 0 ) ; } }

Writes on a shared memory block

First we check for the block existance, and if it doesn't, we'll create it. Now, if the
block already exists, we need to delete it and create it again with a new byte allocation that
matches the size of the data that we want to write there. We mark for deletion,  close the semaphore
and create it again.

@access public
@param string $data The data that you wan't to write into the shared memory block


public function read()
    {
        $size = shmop_size($this->shmid);
        $data = shmop_read($this->shmid, 0, $size);

        return $data;
    }

public function read ( ) { $ size = shmop_size ( $ this -> shmid ) ; $ data = shmop_read ( $ this -> shmid , 0 , $ size ) ; return $ data ; }

Reads from a shared memory block

@access public
@return string The data read from the shared memory block


public function list_( $args, $assoc_args ) {
		global $wp_registered_sidebars;

		Utils\wp_register_unused_sidebar();

		if ( ! empty( $assoc_args['format'] ) && 'ids' === $assoc_args['format'] ) {
			$sidebars = wp_list_pluck( $wp_registered_sidebars, 'id' );
		} else {
			$sidebars = $wp_registered_sidebars;
		}

		$formatter = new Formatter( $assoc_args, $this->fields );
		$formatter->display_items( $sidebars );
	}

public function list_ ( $ args , $ assoc_args ) { global $ wp_registered_sidebars ; Utils \ wp_register_unused_sidebar ( ) ; if ( ! empty ( $ assoc_args [ 'format' ] ) && 'ids' === $ assoc_args [ 'format' ] ) { $ sidebars = wp_list_pluck ( $ wp_registered_sidebars , 'id' ) ; } else { $ sidebars = $ wp_registered_sidebars ; } $ formatter = new Formatter ( $ assoc_args , $ this -> fields ) ; $ formatter -> display_items ( $ sidebars ) ; }

Lists registered sidebars.

## OPTIONS

[--fields=<fields>]
: Limit the output to specific object fields.

[--format=<format>]
: Render output in a particular format.
---
default: table
options:
- table
- csv
- json
- ids
- count
- yaml
---

## AVAILABLE FIELDS

These fields will be displayed by default for each sidebar:

* name
* id
* description

These fields are optionally available:

* class
* before_widget
* after_widget
* before_title
* after_title

## EXAMPLES

$ wp sidebar list --fields=name,id --format=csv
name,id
"Widget Area",sidebar-1
"Inactive Widgets",wp_inactive_widgets

@subcommand list


public function list_( $args, $assoc_args ) {

		list( $sidebar_id ) = $args;

		$this->validate_sidebar( $sidebar_id );

		$output_widgets = $this->get_sidebar_widgets( $sidebar_id );

		if ( ! empty( $assoc_args['format'] ) && 'ids' === $assoc_args['format'] ) {
			$output_widgets = wp_list_pluck( $output_widgets, 'id' );
		}

		$formatter = new Formatter( $assoc_args, $this->fields );
		$formatter->display_items( $output_widgets );

	}

public function list_ ( $ args , $ assoc_args ) { list ( $ sidebar_id ) = $ args ; $ this -> validate_sidebar ( $ sidebar_id ) ; $ output_widgets = $ this -> get_sidebar_widgets ( $ sidebar_id ) ; if ( ! empty ( $ assoc_args [ 'format' ] ) && 'ids' === $ assoc_args [ 'format' ] ) { $ output_widgets = wp_list_pluck ( $ output_widgets , 'id' ) ; } $ formatter = new Formatter ( $ assoc_args , $ this -> fields ) ; $ formatter -> display_items ( $ output_widgets ) ; }

Lists widgets associated with a sidebar.

## OPTIONS

<sidebar-id>
: ID for the corresponding sidebar.

[--fields=<fields>]
: Limit the output to specific object fields.

[--format=<format>]
: Render output in a particular format.
---
default: table
options:
- table
- csv
- ids
- json
- count
- yaml
---

## AVAILABLE FIELDS

These fields will be displayed by default for each widget:

* name
* id
* position
* options

There are no optionally available fields.

## EXAMPLES

$ wp widget list sidebar-1 --fields=name,id --format=csv
name,id
meta,meta-5
search,search-3

@subcommand list

Lists widgets associated with a sidebar .


public function add( $args, $assoc_args ) {

		list( $name, $sidebar_id ) = $args;
		$position                  = Utils\get_flag_value( $args, 2, 1 ) - 1;
		$this->validate_sidebar( $sidebar_id );

		$widget = $this->get_widget_obj( $name );
		if ( false === $widget ) {
			WP_CLI::error( 'Invalid widget type.' );
		}

		/*
		 * Adding a widget is as easy as:
		 * 1. Creating a new widget option
		 * 2. Adding the widget to the sidebar
		 * 3. Positioning appropriately
		 */
		$widget_options = $this->get_widget_options( $name );
		$option_keys    = $widget_options;
		if ( ! isset( $widget_options['_multiwidget'] ) ) {
			$widget_options['_multiwidget'] = 1;
		}
		unset( $option_keys['_multiwidget'] );
		$option_keys                     = array_keys( $option_keys );
		$last_key                        = array_pop( $option_keys );
		$option_index                    = $last_key + 1;
		$widget_options[ $option_index ] = $this->sanitize_widget_options( $name, $assoc_args, array() );
		$this->update_widget_options( $name, $widget_options );

		$widget_id = $name . '-' . $option_index;
		$this->move_sidebar_widget( $widget_id, null, $sidebar_id, null, $position );

		WP_CLI::success( 'Added widget to sidebar.' );

	}

Adds a widget to a sidebar.

Creates a new widget entry in the database, and associates it with the
sidebar.

## OPTIONS

<name>
: Widget name.

<sidebar-id>
: ID for the corresponding sidebar.

[<position>]
: Widget's current position within the sidebar. Defaults to last

[--<field>=<value>]
: Widget option to add, with its new value

## EXAMPLES

# Add a new calendar widget to sidebar-1 with title "Calendar"
$ wp widget add calendar sidebar-1 2 --title="Calendar"
Success: Added widget to sidebar.

@subcommand add


public function update( $args, $assoc_args ) {

		list( $widget_id ) = $args;
		if ( ! $this->validate_sidebar_widget( $widget_id ) ) {
			WP_CLI::error( "Widget doesn't exist." );
		}

		if ( empty( $assoc_args ) ) {
			WP_CLI::error( 'No options specified to update.' );
		}

		list( $name, $option_index ) = $this->get_widget_data( $widget_id );

		$widget_options                  = $this->get_widget_options( $name );
		$clean_options                   = $this->sanitize_widget_options( $name, $assoc_args, $widget_options[ $option_index ] );
		$widget_options[ $option_index ] = array_merge( (array) $widget_options[ $option_index ], $clean_options );
		$this->update_widget_options( $name, $widget_options );

		WP_CLI::success( 'Widget updated.' );

	}

public
function
update
(
$
args
,
$
assoc_args
)
{
list
(
$
widget_id
)
=
$
args
;
if
(
!
$
this
->
validate_sidebar_widget
(
$
widget_id
)
)
{
WP_CLI
::
error
(
"Widget doesn't exist."
)
;
}
if
(
empty
(
$
assoc_args
)
)
{
WP_CLI
::
error
(
'No options specified to update.'
)
;
}
list
(
$
name
,
$
option_index
)
=
$
this
->
get_widget_data
(
$
widget_id
)
;
$
widget_options
=
$
this
->
get_widget_options
(
$
name
)
;
$
clean_options
=
$
this
->
sanitize_widget_options
(
$
name
,
$
assoc_args
,
$
widget_options
[
$
option_index
]
)
;
$
widget_options
[
$
option_index
]
=
array_merge
(
(
array
)
$
widget_options
[
$
option_index
]
,
$
clean_options
)
;
$
this
->
update_widget_options
(
$
name
,
$
widget_options
)
;
WP_CLI
::
success
(
'Widget updated.'
)
;
}

Updates options for an existing widget.

## OPTIONS

<widget-id>
: Unique ID for the widget

[--<field>=<value>]
: Field to update, with its new value

## EXAMPLES

# Change calendar-1 widget title to "Our Calendar"
$ wp widget update calendar-1 --title="Our Calendar"
Success: Widget updated.

@subcommand update

Updates options for an existing widget .


public function move( $args, $assoc_args ) {

		list( $widget_id ) = $args;
		if ( ! $this->validate_sidebar_widget( $widget_id ) ) {
			WP_CLI::error( "Widget doesn't exist." );
		}

		if ( empty( $assoc_args['position'] ) && empty( $assoc_args['sidebar-id'] ) ) {
			WP_CLI::error( 'A new position or new sidebar must be specified.' );
		}

		list( $name, $option_index, $current_sidebar_id, $current_sidebar_index ) = $this->get_widget_data( $widget_id );

		$new_sidebar_id = ! empty( $assoc_args['sidebar-id'] ) ? $assoc_args['sidebar-id'] : $current_sidebar_id;
		$this->validate_sidebar( $new_sidebar_id );

		$new_sidebar_index = ! empty( $assoc_args['position'] ) ? $assoc_args['position'] - 1 : $current_sidebar_index;
		// Moving between sidebars adds to the top
		if ( $new_sidebar_id !== $current_sidebar_id && $new_sidebar_index === $current_sidebar_index ) {
			// Human-readable positions are different than numerically indexed array
			$new_sidebar_index = 0;
		}

		$this->move_sidebar_widget( $widget_id, $current_sidebar_id, $new_sidebar_id, $current_sidebar_index, $new_sidebar_index );

		WP_CLI::success( 'Widget moved.' );

	}

Moves the position of a widget.

Changes the order of a widget in its existing sidebar, or moves it to a
new sidebar.

## OPTIONS

<widget-id>
: Unique ID for the widget

[--position=<position>]
: Assign the widget to a new position.

[--sidebar-id=<sidebar-id>]
: Assign the widget to a new sidebar

## EXAMPLES

# Change position of widget
$ wp widget move recent-comments-2 --position=2
Success: Widget moved.

# Move widget to Inactive Widgets
$ wp widget move recent-comments-2 --sidebar-id=wp_inactive_widgets
Success: Widget moved.

@subcommand move


public function deactivate( $args, $assoc_args ) {

		$count  = 0;
		$errors = 0;

		foreach ( $args as $widget_id ) {
			if ( ! $this->validate_sidebar_widget( $widget_id ) ) {
				WP_CLI::warning( "Widget '{$widget_id}' doesn't exist." );
				$errors++;
				continue;
			}

			list( $name, $option_index, $sidebar_id, $sidebar_index ) = $this->get_widget_data( $widget_id );
			if ( 'wp_inactive_widgets' === $sidebar_id ) {
				WP_CLI::warning( sprintf( "'%s' is already deactivated.", $widget_id ) );
				continue;
			}

			$this->move_sidebar_widget( $widget_id, $sidebar_id, 'wp_inactive_widgets', $sidebar_index, 0 );

			$count++;

		}

		Utils\report_batch_operation_results( 'widget', 'deactivate', count( $args ), $count, $errors );
	}

public
function
deactivate
(
$
args
,
$
assoc_args
)
{
$
count
=
0
;
$
errors
=
0
;
foreach
(
$
args
as
$
widget_id
)
{
if
(
!
$
this
->
validate_sidebar_widget
(
$
widget_id
)
)
{
WP_CLI
::
warning
(
"Widget '{$widget_id}' doesn't exist."
)
;
$
errors
++
;
continue
;
}
list
(
$
name
,
$
option_index
,
$
sidebar_id
,
$
sidebar_index
)
=
$
this
->
get_widget_data
(
$
widget_id
)
;
if
(
'wp_inactive_widgets'
===
$
sidebar_id
)
{
WP_CLI
::
warning
(
sprintf
(
"'%s' is already deactivated."
,
$
widget_id
)
)
;
continue
;
}
$
this
->
move_sidebar_widget
(
$
widget_id
,
$
sidebar_id
,
'wp_inactive_widgets'
,
$
sidebar_index
,
0
)
;
$
count
++
;
}
Utils
\
report_batch_operation_results
(
'widget'
,
'deactivate'
,
count
(
$
args
)
,
$
count
,
$
errors
)
;
}

Deactivates one or more widgets from an active sidebar.

Moves widgets to Inactive Widgets.

## OPTIONS

<widget-id>...
: Unique ID for the widget(s)

## EXAMPLES

# Deactivate the recent-comments-2 widget.
$ wp widget deactivate recent-comments-2
Success: 1 widget deactivated.

@subcommand deactivate

Deactivates one or more widgets from an active sidebar .


public function delete( $args, $assoc_args ) {

		$count  = 0;
		$errors = 0;

		foreach ( $args as $widget_id ) {
			if ( ! $this->validate_sidebar_widget( $widget_id ) ) {
				WP_CLI::warning( "Widget '{$widget_id}' doesn't exist." );
				$errors++;
				continue;
			}

			// Remove the widget's settings.
			list( $name, $option_index, $sidebar_id, $sidebar_index ) = $this->get_widget_data( $widget_id );
			$widget_options = $this->get_widget_options( $name );
			unset( $widget_options[ $option_index ] );
			$this->update_widget_options( $name, $widget_options );

			// Remove the widget from the sidebar.
			$all_widgets = $this->wp_get_sidebars_widgets();
			unset( $all_widgets[ $sidebar_id ][ $sidebar_index ] );
			$all_widgets[ $sidebar_id ] = array_values( $all_widgets[ $sidebar_id ] );
			update_option( 'sidebars_widgets', $all_widgets );

			$count++;
		}

		Utils\report_batch_operation_results( 'widget', 'delete', count( $args ), $count, $errors );
	}

public
function
delete
(
$
args
,
$
assoc_args
)
{
$
count
=
0
;
$
errors
=
0
;
foreach
(
$
args
as
$
widget_id
)
{
if
(
!
$
this
->
validate_sidebar_widget
(
$
widget_id
)
)
{
WP_CLI
::
warning
(
"Widget '{$widget_id}' doesn't exist."
)
;
$
errors
++
;
continue
;
}
// Remove the widget's settings.
list
(
$
name
,
$
option_index
,
$
sidebar_id
,
$
sidebar_index
)
=
$
this
->
get_widget_data
(
$
widget_id
)
;
$
widget_options
=
$
this
->
get_widget_options
(
$
name
)
;
unset
(
$
widget_options
[
$
option_index
]
)
;
$
this
->
update_widget_options
(
$
name
,
$
widget_options
)
;
// Remove the widget from the sidebar.
$
all_widgets
=
$
this
->
wp_get_sidebars_widgets
(
)
;
unset
(
$
all_widgets
[
$
sidebar_id
]
[
$
sidebar_index
]
)
;
$
all_widgets
[
$
sidebar_id
]
=
array_values
(
$
all_widgets
[
$
sidebar_id
]
)
;
update_option
(
'sidebars_widgets'
,
$
all_widgets
)
;
$
count
++
;
}
Utils
\
report_batch_operation_results
(
'widget'
,
'delete'
,
count
(
$
args
)
,
$
count
,
$
errors
)
;
}

Deletes one or more widgets from a sidebar.

## OPTIONS

<widget-id>...
: Unique ID for the widget(s)

## EXAMPLES

# Delete the recent-comments-2 widget from its sidebar.
$ wp widget delete recent-comments-2
Success: Deleted 1 of 1 widgets.

@subcommand delete

Deletes one or more widgets from a sidebar .


public function reset( $args, $assoc_args ) {

		global $wp_registered_sidebars;

		$all = Utils\get_flag_value( $assoc_args, 'all', false );

		// Bail if no arguments and no all flag.
		if ( ! $all && empty( $args ) ) {
			WP_CLI::error( 'Please specify one or more sidebars, or use --all.' );
		}

		// Fetch all sidebars if all flag is set.
		if ( $all ) {
			$args = array_keys( $wp_registered_sidebars );
		}

		// Sidebar ID wp_inactive_widgets is reserved by WP core for inactive widgets.
		if ( isset( $args['wp_inactive_widgets'] ) ) {
			unset( $args['wp_inactive_widgets'] );
		}

		// Check if no registered sidebar.
		if ( empty( $args ) ) {
			WP_CLI::error( 'No sidebar registered.' );
		}

		$count  = 0;
		$errors = 0;
		foreach ( $args as $sidebar_id ) {
			if ( ! array_key_exists( $sidebar_id, $wp_registered_sidebars ) ) {
				WP_CLI::warning( sprintf( 'Invalid sidebar: %s', $sidebar_id ) );
				$errors++;
				continue;
			}

			$widgets = $this->get_sidebar_widgets( $sidebar_id );
			if ( empty( $widgets ) ) {
				WP_CLI::warning( sprintf( "Sidebar '%s' is already empty.", $sidebar_id ) );
			} else {
				foreach ( $widgets as $widget ) {
					$widget_id = $widget->id;
					list( $name, $option_index, $new_sidebar_id, $sidebar_index ) = $this->get_widget_data( $widget_id );
					$this->move_sidebar_widget( $widget_id, $new_sidebar_id, 'wp_inactive_widgets', $sidebar_index, 0 );
				}
				WP_CLI::log( sprintf( "Sidebar '%s' reset.", $sidebar_id ) );
				$count++;
			}
		}

		Utils\report_batch_operation_results( 'sidebar', 'reset', count( $args ), $count, $errors );
	}

Resets sidebar.

Removes all widgets from the sidebar and places them in Inactive Widgets.

## OPTIONS

[<sidebar-id>...]
: One or more sidebars to reset.

[--all]
: If set, all sidebars will be reset.

## EXAMPLES

# Reset a sidebar
$ wp widget reset sidebar-1
Success: Sidebar 'sidebar-1' reset.

# Reset multiple sidebars
$ wp widget reset sidebar-1 sidebar-2
Success: Sidebar 'sidebar-1' reset.
Success: Sidebar 'sidebar-2' reset.

# Reset all sidebars
$ wp widget reset --all
Success: Sidebar 'sidebar-1' reset.
Success: Sidebar 'sidebar-2' reset.
Success: Sidebar 'sidebar-3' reset.


private function validate_sidebar( $sidebar_id ) {
		global $wp_registered_sidebars;

		Utils\wp_register_unused_sidebar();

		if ( ! array_key_exists( $sidebar_id, $wp_registered_sidebars ) ) {
			WP_CLI::error( 'Invalid sidebar.' );
		}
	}

private
function
validate_sidebar
(
$
sidebar_id
)
{
global
$
wp_registered_sidebars
;
Utils
\
wp_register_unused_sidebar
(
)
;
if
(
!
array_key_exists
(
$
sidebar_id
,
$
wp_registered_sidebars
)
)
{
WP_CLI
::
error
(
'Invalid sidebar.'
)
;
}
}

Checks whether a sidebar is a valid sidebar

@param string $sidebar_id


private function validate_sidebar_widget( $widget_id ) {

		$sidebars_widgets = $this->wp_get_sidebars_widgets();

		$widget_exists = false;
		foreach ( $sidebars_widgets as $sidebar_id => $widgets ) {

			if ( in_array( $widget_id, $widgets, true ) ) {
				$widget_exists = true;
				break;
			}
		}
		return $widget_exists;
	}

private function validate_sidebar_widget ( $ widget_id ) { $ sidebars_widgets = $ this -> wp_get_sidebars_widgets ( ) ; $ widget_exists = false ; foreach ( $ sidebars_widgets as $ sidebar_id => $ widgets ) { if ( in_array ( $ widget_id , $ widgets , true ) ) { $ widget_exists = true ; break ; } } return $ widget_exists ; }

Checks whether the specified widget is on the sidebar

@param string $widget_id


private function get_widget_data( $widget_id ) {

		$parts        = explode( '-', $widget_id );
		$option_index = array_pop( $parts );
		$name         = implode( '-', $parts );

		$sidebar_id    = false;
		$sidebar_index = false;
		$all_widgets   = $this->wp_get_sidebars_widgets();
		foreach ( $all_widgets as $s_id => &$widgets ) {

			$key = array_search( $widget_id, $widgets, true );
			if ( false !== $key ) {
				$sidebar_id    = $s_id;
				$sidebar_index = $key;
				break;
			}
		}

		return array( $name, $option_index, $sidebar_id, $sidebar_index );
	}

private function get_widget_data ( $ widget_id ) { $ parts = explode ( '-' , $ widget_id ) ; $ option_index = array_pop ( $ parts ) ; $ name = implode ( '-' , $ parts ) ; $ sidebar_id = false ; $ sidebar_index = false ; $ all_widgets = $ this -> wp_get_sidebars_widgets ( ) ; foreach ( $ all_widgets as $ s_id => & $ widgets ) { $ key = array_search ( $ widget_id , $ widgets , true ) ; if ( false !== $ key ) { $ sidebar_id = $ s_id ; $ sidebar_index = $ key ; break ; } } return array ( $ name , $ option_index , $ sidebar_id , $ sidebar_index ) ; }

Gets the widget's name, option index, sidebar, and sidebar index from its ID

@param string $widget_id
@return array

Gets the widget s name option index sidebar and sidebar index from its ID


private function move_sidebar_widget( $widget_id, $current_sidebar_id, $new_sidebar_id, $current_index, $new_index ) {

		$all_widgets     = $this->wp_get_sidebars_widgets();
		$needs_placement = true;
		// Existing widget
		if ( $current_sidebar_id && ! is_null( $current_index ) ) {

			$widgets = $all_widgets[ $current_sidebar_id ];
			if ( $current_sidebar_id !== $new_sidebar_id ) {

				unset( $widgets[ $current_index ] );

			} else {

				$part = array_splice( $widgets, $current_index, 1 );
				array_splice( $widgets, $new_index, 0, $part );

				$needs_placement = false;

			}

			$all_widgets[ $current_sidebar_id ] = array_values( $widgets );

		}

		if ( $needs_placement ) {
			$widgets                        = ! empty( $all_widgets[ $new_sidebar_id ] ) ? $all_widgets[ $new_sidebar_id ] : array();
			$before                         = array_slice( $widgets, 0, $new_index, true );
			$after                          = array_slice( $widgets, $new_index, count( $widgets ), true );
			$widgets                        = array_merge( $before, array( $widget_id ), $after );
			$all_widgets[ $new_sidebar_id ] = array_values( $widgets );
		}

		update_option( 'sidebars_widgets', $all_widgets );

	}

private
function
move_sidebar_widget
(
$
widget_id
,
$
current_sidebar_id
,
$
new_sidebar_id
,
$
current_index
,
$
new_index
)
{
$
all_widgets
=
$
this
->
wp_get_sidebars_widgets
(
)
;
$
needs_placement
=
true
;
// Existing widget
if
(
$
current_sidebar_id
&&
!
is_null
(
$
current_index
)
)
{
$
widgets
=
$
all_widgets
[
$
current_sidebar_id
]
;
if
(
$
current_sidebar_id
!==
$
new_sidebar_id
)
{
unset
(
$
widgets
[
$
current_index
]
)
;
}
else
{
$
part
=
array_splice
(
$
widgets
,
$
current_index
,
1
)
;
array_splice
(
$
widgets
,
$
new_index
,
0
,
$
part
)
;
$
needs_placement
=
false
;
}
$
all_widgets
[
$
current_sidebar_id
]
=
array_values
(
$
widgets
)
;
}
if
(
$
needs_placement
)
{
$
widgets
=
!
empty
(
$
all_widgets
[
$
new_sidebar_id
]
)
$
all_widgets
[
$
new_sidebar_id
]
:
array
(
)
;
$
before
=
array_slice
(
$
widgets
,
0
,
$
new_index
,
true
)
;
$
after
=
array_slice
(
$
widgets
,
$
new_index
,
count
(
$
widgets
)
,
true
)
;
$
widgets
=
array_merge
(
$
before
,
array
(
$
widget_id
)
,
$
after
)
;
$
all_widgets
[
$
new_sidebar_id
]
=
array_values
(
$
widgets
)
;
}
update_option
(
'sidebars_widgets'
,
$
all_widgets
)
;
}

Repositions a widget within a sidebar or move to another sidebar.

@param string $widget_id
@param string|null $current_sidebar_id
@param string $new_sidebar_id
@param int|null $current_index
@param int $new_index

Repositions a widget within a sidebar or move to another sidebar .


private function get_widget_obj( $id_base ) {
		global $wp_widget_factory;

		$widget = wp_filter_object_list( $wp_widget_factory->widgets, array( 'id_base' => $id_base ) );
		if ( empty( $widget ) ) {
			return false;
		}

		return array_pop( $widget );
	}

private function get_widget_obj ( $ id_base ) { global $ wp_widget_factory ; $ widget = wp_filter_object_list ( $ wp_widget_factory -> widgets , array ( 'id_base' => $ id_base ) ) ; if ( empty ( $ widget ) ) { return false ; } return array_pop ( $ widget ) ; }

Gets a widget's instantiated object based on its name

@param string $id_base Name of the widget
@return WP_Widget|false

Gets a widget s instantiated object based on its name


private function sanitize_widget_options( $id_base, $dirty_options, $old_options ) {

		$widget = $this->get_widget_obj( $id_base );
		if ( empty( $widget ) ) {
			return array();
		}

		// No easy way to determine expected array keys for $dirty_options
		// because Widget API dependent on the form fields
		// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- Whitelisting due to above reason.
		return @$widget->update( $dirty_options, $old_options );

	}

Cleans up a widget's options based on its update callback

@param string $id_base Name of the widget
@param mixed $dirty_options
@param mixed $old_options
@return mixed

Cleans up a widget s options based on its update callback


public function getRandomInteger($min = 0, $max = PHP_INT_MAX)
    {
        $min = (int) $min;
        $max = (int) $max;
        $range = $max - $min;

        $bits  = $this->getBitsInInteger($range);
        $bytes = $this->getBytesInBits($bits);
        $mask  = (int) ((1 << $bits) - 1);

        do {
            $byteString = $this->generator->generate($bytes);
            $result = hexdec(bin2hex($byteString)) & $mask;
        } while ($result > $range);

        return (int) $result + $min;
    }

public function getRandomInteger ( $ min = 0 , $ max = PHP_INT_MAX ) { $ min = ( int ) $ min ; $ max = ( int ) $ max ; $ range = $ max - $ min ; $ bits = $ this -> getBitsInInteger ( $ range ) ; $ bytes = $ this -> getBytesInBits ( $ bits ) ; $ mask = ( int ) ( ( 1 << $ bits ) - 1 ) ; do { $ byteString = $ this -> generator -> generate ( $ bytes ) ; $ result = hexdec ( bin2hex ( $ byteString ) ) & $ mask ; } while ( $ result > $ range ) ; return ( int ) $ result + $ min ; }

Get a random integer within the specified range.

@param  integer $min The minimum expected value. Defaults to 0.
@param  integer $max The maximum expected value. Defaults to PHP_INT_MAX.
@return integer A random integer between the specified values, inclusive.

Get a random integer within the specified range .


public function getRandomString($length, $charset = null)
    {
        $length = (int) $length;
        if (!$charset) {
            $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./';
        }

        $charsetLength = strlen($charset);
        $neededBytes = $this->getBytesInBits($length * ($this->getBitsInInteger($charsetLength) + 1));

        $string = '';
        do {
            $byteString = $this->generator->generate($neededBytes);
            for ($i = 0; $i < $neededBytes; ++$i) {
                if (ord($byteString[$i]) > (255 - (255 % $charsetLength))) {
                    continue;
                }
                $string .= $charset[ord($byteString[$i]) % $charsetLength];
            }
        } while (strlen($string) < $length);

        return substr($string, 0, $length);
    }

Get a random string of the specified length.

@param  integer $length The length of the requested string.
@return string  A random string of the specified length, consisting of
characters from the base64 character set.

Get a random string of the specified length .


public function encode($string)
    {
        $encoded = '';

        if ($string) {
            $binString = '';

            // Build a binary string representation of the input string
            foreach (str_split($string) as $char) {
                $binString .= str_pad(decbin(ord($char)), 8, 0, STR_PAD_LEFT);
            }

            // Encode the data in 5-bit chunks
            foreach (str_split($binString, 5) as $chunk) {
                $chunk = str_pad($chunk, 5, 0, STR_PAD_RIGHT);
                $encoded .= $this->charset[bindec($chunk)];
            }

            // Add padding to the encoded string as required
            if (strlen($encoded) % 8) {
                $encoded .= str_repeat($this->charset[32], 8 - (strlen($encoded) % 8));
            }
        }

        return $encoded;
    }

Encode a string of raw bytes.

@param  string $string
@return string


public function decode($string)
    {
        $decoded = '';
        $string = preg_replace("/[^{$this->charset}]/", '', rtrim(strtoupper($string), $this->charset[32]));

        if ($string) {
            $binString = '';
            foreach (str_split($string) as $char) {
                $binString .= str_pad(decbin(strpos($this->charset, $char)), 5, 0, STR_PAD_LEFT);
            }
            $binString = substr($binString, 0, (floor(strlen($binString) / 8) * 8));

            foreach (str_split($binString, 8) as $chunk) {
                $chunk = str_pad($chunk, 8, 0, STR_PAD_RIGHT);
                $decoded .= chr(bindec($chunk));
            }
        }

        return $decoded;
    }

public function decode ( $ string ) { $ decoded = '' ; $ string = preg_replace ( "/[^{$this->charset}]/" , '' , rtrim ( strtoupper ( $ string ) , $ this -> charset [ 32 ] ) ) ; if ( $ string ) { $ binString = '' ; foreach ( str_split ( $ string ) as $ char ) { $ binString .= str_pad ( decbin ( strpos ( $ this -> charset , $ char ) ) , 5 , 0 , STR_PAD_LEFT ) ; } $ binString = substr ( $ binString , 0 , ( floor ( strlen ( $ binString ) / 8 ) * 8 ) ) ; foreach ( str_split ( $ binString , 8 ) as $ chunk ) { $ chunk = str_pad ( $ chunk , 8 , 0 , STR_PAD_RIGHT ) ; $ decoded .= chr ( bindec ( $ chunk ) ) ; } } return $ decoded ; }

Decode an encoded string.

@param  string $string
@return string


public function addGeneratorPath($prefix, $path)
    {
        $path = realpath($path);
        $success = false;
        if ($path && !array_key_exists($prefix, $this->generatorPaths)) {
            $this->generatorPaths[$prefix] = $path;
            $success = true;
        }

        return $success;
    }

public function addGeneratorPath ( $ prefix , $ path ) { $ path = realpath ( $ path ) ; $ success = false ; if ( $ path && ! array_key_exists ( $ prefix , $ this -> generatorPaths ) ) { $ this -> generatorPaths [ $ prefix ] = $ path ; $ success = true ; } return $ success ; }

Add a search path for custom generators.

@param string $prefix Namespace prefix used by generator classes
in this path.
@param  string  $path Directory path containing generator classes.
@return boolean Returns true on success, false otherwise.

Add a search path for custom generators .


public function removeGeneratorPath($prefixOrPath)
    {
        if (array_key_exists($prefixOrPath, $this->generatorPaths)) {
            $prefix = $prefixOrPath;
            unset($this->generatorPaths[$prefix]);
        } elseif (($prefixOrPath = realpath($prefixOrPath)) && in_array($prefixOrPath, $this->generatorPaths)) {
            $path = $prefixOrPath;
            $generatorPaths = $this->generatorPaths;
            $this->generatorPaths = array_filter($generatorPaths, function ($value) use ($path) {
                return ($value != $path);
            });
        }

        return $this;
    }

public function removeGeneratorPath ( $ prefixOrPath ) { if ( array_key_exists ( $ prefixOrPath , $ this -> generatorPaths ) ) { $ prefix = $ prefixOrPath ; unset ( $ this -> generatorPaths [ $ prefix ] ) ; } elseif ( ( $ prefixOrPath = realpath ( $ prefixOrPath ) ) && in_array ( $ prefixOrPath , $ this -> generatorPaths ) ) { $ path = $ prefixOrPath ; $ generatorPaths = $ this -> generatorPaths ; $ this -> generatorPaths = array_filter ( $ generatorPaths , function ( $ value ) use ( $ path ) { return ( $ value != $ path ) ; } ) ; } return $ this ; }

Remove a registered generator path.

@param  type $prefixOrPath The prefix or directory path to remove.
@return self Returns an instance of self for method chaining.


public function getGenerator()
    {
        if (!$this->generators) {
            $this->loadGenerators();
        }

        $generators = $this->generators;
        usort($generators, function ($a, $b) {
            return ($b::getPriority() - $a::getPriority());
        });

        $generator = null;
        if (isset ($generators[0])) {
            $class = $generators[0];
            $generator = new $class;
        }

        return $generator;
    }

public function getGenerator ( ) { if ( ! $ this -> generators ) { $ this -> loadGenerators ( ) ; } $ generators = $ this -> generators ; usort ( $ generators , function ( $ a , $ b ) { return ( $ b :: getPriority ( ) - $ a :: getPriority ( ) ) ; } ) ; $ generator = null ; if ( isset ( $ generators [ 0 ] ) ) { $ class = $ generators [ 0 ] ; $ generator = new $ class ; } return $ generator ; }

Get a supported random data generator.

Checks the available supported generators and attempts to return the best
possible generator.

@return \Rych\Random\Generator\GeneratorInterface
@uses \Rych\Random\Generator\GeneratorInterface::getPriority() Used to
sort the available generators from lowest to highest priority.


public function loadGenerators()
    {
        // Reset the list of loaded classes
        $this->generators = array ();

        // Loop through each registered path
        foreach ($this->generatorPaths as $generatorPrefix => $generatorPath) {
            // Build the iterators to search the path
            $dirIterator = new \RecursiveDirectoryIterator($generatorPath);
            $iteratorIterator = new \RecursiveIteratorIterator($dirIterator);
            $generatorIterator = new \RegexIterator($iteratorIterator, '/^.+\.php$/i');

            // Loop through the iterator looking for GeneratorInterface
            // instances
            foreach ($generatorIterator as $generatorFile) {
                // Determine additional namespace from file path
                $pathName = $generatorFile->getPath();
                if (substr($pathName, 0, strlen($generatorPath)) == $generatorPath) {
                    $pathName = substr($pathName, strlen($generatorPath), strlen($pathName));
                }

                // Best guess for class name is Prefix\Path\File
                $class = $generatorPrefix . '\\' . $pathName . $generatorFile->getBasename('.php');
                $class = str_replace('/', '\\', $class);

                // If the class doesn't exists and isn't autoloaded, try to
                // load it from the discovered file.
                if (!class_exists($class, true) && !in_array($generatorFile->getPathName(), get_included_files())) {
                    include $generatorFile->getPathname();
                }

                // If the class exists and implements GeneratorInterface,
                // append it to our list.
                if (class_exists($class, false) && in_array(__NAMESPACE__ . '\\GeneratorInterface', class_implements($class))) {
                    if ($class::isSupported()) {
                        $this->generators[] = $class;
                    }
                }
            }
        }
    }

Populate the available generators array.

@return void
@uses \Rych\Random\Generator\GeneratorInterface Used to determine if a
generator is supported by the system.

Populate the available generators array .


public function showAsRow($rowConfig = 'default')
    {
        $rowConfig = app('config')->get("bs4.form_rows.$rowConfig", null);
        if ($rowConfig === null)
        {
            throw new \InvalidArgumentException("Unknown configuration entry: bs4.form_rows.$rowConfig");
        }

        $element = clone $this;

        // Add a class to ourselves, to the control wrapper and to the label
        $element = $element->addClass('row');

        $element->controlWrapper = $element->controlWrapper ?? Div::create();
        $element->controlWrapper = $element->controlWrapper->addClass($rowConfig['control_wrapper'] ?? []);

        $element->label = $element->label ?? $element->label('', true)->label;
        $element->label = $element->label->addClass($rowConfig['label'] ?? []);

        return $element;
    }

Show the group as an horizontal row, using the specified configuration. This method should be called last
when the label and controls are all initialized.

The row configuration will be taken from config("bs4.form_rows.$rowConfig")

@param string $rowConfig The reference to the configuration (without prefix).

@return \MarvinLabs\Html\Bootstrap\Elements\FormGroup|static

@throws \InvalidArgumentException When the configuration does not exist
@throws \Psr\Container\NotFoundExceptionInterface Problem in dependency retrieval for 'config'
@throws \Psr\Container\ContainerExceptionInterface Problem in dependency retrieval for 'config'

Show the group as an horizontal row using the specified configuration . This method should be called last when the label and controls are all initialized .


public function readOnly($showAsPlainText = false)
    {
        $element = clone $this;
        $element->plainText = $showAsPlainText;

        return $element->attribute('readonly', 'readonly');
    }

public function readOnly ( $ showAsPlainText = false ) { $ element = clone $ this ; $ element -> plainText = $ showAsPlainText ; return $ element -> attribute ( 'readonly' , 'readonly' ) ; }

Make the input read only

@param bool $showAsPlainText Style the input just like plain text (no border, padding, etc.)

@return static


public function openForm($method, $action, array $options = []): Htmlable
    {
        // Initialize the form state
        if ($this->formState !== null || $this->currentForm !== null)
        {
            throw new RuntimeException('You cannot open another form before closing the previous one');
        }
        $this->formState = app()->make(FormState::class);
        $this->formState->setModel($options['model'] ?? null);
        $this->formState->setHideErrors($options['hideErrors'] ?? false);

        // Create a form element with sane defaults
        $this->currentForm = Form::create();

        // Handle form method consequences (token / hidden method field)
        //
        // If Laravel needs to spoof the form's method, we'll append a hidden
        // field containing the actual method
        //
        // On any other method that get, the form needs a CSRF token
        $method = strtoupper($method);

        if (\in_array($method, ['DELETE', 'PATCH', 'PUT'], true))
        {
            $this->currentForm = $this->currentForm->addChild($this->hidden('_method', $method));
            $method = 'POST';
        }

        if ($method !== 'GET')
        {
            $this->currentForm = $this->currentForm->addChild($this->token());
        }

        if ($options['files'] ?? false)
        {
            $this->currentForm = $this->currentForm->acceptsFiles();
        }

        return $this->currentForm
            ->method($method)
            ->action($action)
            ->addClassIf($options['inline'] ?? false, 'form-inline')
            ->attributesIf($options['attributes'] ?? false, $options['attributes'] ?? [])
            ->open();
    }

Open a form

@param string $method  The method to target for form action
@param string $action  The form action
@param array  $options A set of options for the form

Valid options are:

- files      => bool    Does the form accept files
- inline     => bool    Shall we render an inline form (Bootstrap specific)
- model      => mixed   The model to bind to the form
- hideErrors => bool    Hide field errors
- attributes => array   Attributes to set on the form element

@return \Illuminate\Contracts\Support\Htmlable
@throws \Exception When trying to open a form before closing the previous one


public function prefix($prefix, $isPlainText = true)
    {
        if ($prefix === null)
        {
            return $this;
        }

        $element = clone $this;
        $element->prefixes[] = [
            'content'   => $prefix,
            'plaintext' => $isPlainText,
        ];

        return $element;
    }

public function prefix ( $ prefix , $ isPlainText = true ) { if ( $ prefix === null ) { return $ this ; } $ element = clone $ this ; $ element -> prefixes [ ] = [ 'content' => $ prefix , 'plaintext' => $ isPlainText , ] ; return $ element ; }

Add an addon before the field

@param string|\Spatie\Html\BaseElement $prefix      One or more prefixes to append before the field
@param bool                            $isPlainText Is the prefix to be handled like a button, dropdown, etc.

@return static


public function suffix($suffix, $isPlainText = true)
    {
        if ($suffix === null)
        {
            return $this;
        }

        $element = clone $this;
        $element->suffixes[] = [
            'content'   => $suffix,
            'plaintext' => $isPlainText,
        ];

        return $element;
    }

public function suffix ( $ suffix , $ isPlainText = true ) { if ( $ suffix === null ) { return $ this ; } $ element = clone $ this ; $ element -> suffixes [ ] = [ 'content' => $ suffix , 'plaintext' => $ isPlainText , ] ; return $ element ; }

Add an addon after the field

@param string|\Spatie\Html\BaseElement $suffix      One or more suffixes to append before the field
@param bool                            $isPlainText Is the prefix to be handled like a button, dropdown, etc.

@return static


private function assembleAddons($addons, $addonContainerClass)
    {
        if (0 === \count($addons))
        {
            return $this;
        }

        $div = Div::create()
                  ->addClass($addonContainerClass)
                  ->addChildren($addons,
                      function ($token) {
                          $content = $token['content'] ?? '';
                          $plainText = $token['plaintext'] ?? true;

                          // When not instructed to treat as plain text, use it directly
                          if (!$plainText)
                          {
                              return $content;
                          }

                          // When instructed to use as plain text, we wrap inside a span
                          $span = Span::create()->addClass('input-group-text');

                          return \is_string($content)
                              ? $span->text($content)
                              : $span->addChild($content);
                      });

        return $this->addChild($div);
    }

Add the child elements corresponding to the given addons

@param array  $addons
@param string $addonContainerClass

@return static


public function size($size)
    {
        if (!property_exists($this, 'sizableClass'))
        {
            throw new RuntimeException('You must specify the sizable CSS class');
        }

        $size = strtolower($size);
        if (!\in_array($size, ['lg', 'sm'], true))
        {
            throw new RuntimeException('Invalid size');
        }

        return $this->addClass("{$this->sizableClass}-$size");
    }

Set the control size

@param string $size

@return static
@throws \RuntimeException When $size does not have a valid value


private function unscrub($msg) {
        $args = $msg->arguments;
        $session = $this;
        foreach ($msg->callbacks as $id => $path) {
            if (!isset($this->wrapped[$id])) {
                $this->wrapped[$id] = function() use ($session, $id) {
                    $session->request((int) $id, func_get_args());
                };
            }
            $location =& $args;
            foreach ($path as $part) {
                if (is_array($location)) {
                    $location =& $location[$part];
                    continue;
                }
                $location =& $location->$part;
            }
            $location = $this->wrapped[$id];
        }
        return $args;
    }

private function unscrub ( $ msg ) { $ args = $ msg -> arguments ; $ session = $ this ; foreach ( $ msg -> callbacks as $ id => $ path ) { if ( ! isset ( $ this -> wrapped [ $ id ] ) ) { $ this -> wrapped [ $ id ] = function ( ) use ( $ session , $ id ) { $ session -> request ( ( int ) $ id , func_get_args ( ) ) ; } ; } $ location = & $ args ; foreach ( $ path as $ part ) { if ( is_array ( $ location ) ) { $ location = & $ location [ $ part ] ; continue ; } $ location = & $ location -> $ part ; } $ location = $ this -> wrapped [ $ id ] ; } return $ args ; }

Replace callbacks. The supplied function should take a callback
id and return a callback of its own.

Replace callbacks . The supplied function should take a callback id and return a callback of its own .


public function clientTempF($cb)
    {
        $this->remote->temperature(function($degC) use ($cb) {
            $degF = round($degC * 9 / 5 + 32);
            $cb($degF);
        });
    }

public function clientTempF ( $ cb ) { $ this -> remote -> temperature ( function ( $ degC ) use ( $ cb ) { $ degF = round ( $ degC * 9 / 5 + 32 ) ; $ cb ( $ degF ) ; } ) ; }


public function getNodes($sessionName, $path, $cb)
    {
        if (!$this->validateSessionName($sessionName, $cb))
            return false;

        $exception = null;
        $msg = null;
        $names = array ();

        try {
            $parent = $this->sessions[$sessionName]->getNode($path);
            $nodes = $parent->getNodes();
            $names = array_keys ($nodes->getArrayCopy());
        } catch (\Exception $e) {
            $exception = get_class($e);
            $msg = $e->getMessage();
        }

        $cb($names, $exception, $msg);
    }

public function getNodes ( $ sessionName , $ path , $ cb ) { if ( ! $ this -> validateSessionName ( $ sessionName , $ cb ) ) return false ; $ exception = null ; $ msg = null ; $ names = array ( ) ; try { $ parent = $ this -> sessions [ $ sessionName ] -> getNode ( $ path ) ; $ nodes = $ parent -> getNodes ( ) ; $ names = array_keys ( $ nodes -> getArrayCopy ( ) ) ; } catch ( \ Exception $ e ) { $ exception = get_class ( $ e ) ; $ msg = $ e -> getMessage ( ) ; } $ cb ( $ names , $ exception , $ msg ) ; }

Get the names of children nodes

@param $sessionName - name of the session
@param $path - absolute path of the parent node
@param $cb - callback function

@return void


public function getProperties($sessionName, $path, $cb)
    {
        if (!$this->validateSessionName($sessionName, $cb))
            return false;

        $exception = null;
        $msg = null;
        $names = array ();

        try {
            $parent = $this->sessions[$sessionName]->getNode($path);
            $properties = $parent->getProperties ();
            $names = array_keys ($properties);
        } catch (\Exception $e) {
            $exception = get_class($e);
            $msg = $e->getMessage();
        }

        $cb($names, $exception, $msg);
    }

public function getProperties ( $ sessionName , $ path , $ cb ) { if ( ! $ this -> validateSessionName ( $ sessionName , $ cb ) ) return false ; $ exception = null ; $ msg = null ; $ names = array ( ) ; try { $ parent = $ this -> sessions [ $ sessionName ] -> getNode ( $ path ) ; $ properties = $ parent -> getProperties ( ) ; $ names = array_keys ( $ properties ) ; } catch ( \ Exception $ e ) { $ exception = get_class ( $ e ) ; $ msg = $ e -> getMessage ( ) ; } $ cb ( $ names , $ exception , $ msg ) ; }

Get the names of all properties

@param $sessionName - name of the session
@param $path - absolute path of the node
@param $cb - callback function

@return void


protected function evaluatePath(string $path, array $data = [])
    {
        extract($data, EXTR_SKIP);
        try {
            if (!$this->smarty->isCached($path)) {
                foreach ($data as $var => $val) {
                    $this->smarty->assign($var, $val);
                }
            }
            // render
            $cacheId = isset($data['smarty.cache_id']) ? $data['smarty.cache_id'] : null;
            $compileId = isset($data['smarty.compile_id']) ? $data['smarty.compile_id'] : null;

            return $this->smarty->fetch($path, $cacheId, $compileId);
            // @codeCoverageIgnoreStart
        } catch (\SmartyException $e) {
            $this->handleViewException($e);
        } catch (Throwable $e) {
            $this->handleViewException(new FatalThrowableError($e));
        }
    }

protected
function
evaluatePath
(
string
$
path
,
array
$
data
=
[
]
)
{
extract
(
$
data
,
EXTR_SKIP
)
;
try
{
if
(
!
$
this
->
smarty
->
isCached
(
$
path
)
)
{
foreach
(
$
data
as
$
var
=>
$
val
)
{
$
this
->
smarty
->
assign
(
$
var
,
$
val
)
;
}
}
// render
$
cacheId
=
isset
(
$
data
[
'smarty.cache_id'
]
)
$
data
[
'smarty.cache_id'
]
:
;
$
compileId
=
isset
(
$
data
[
'smarty.compile_id'
]
)
$
data
[
'smarty.compile_id'
]
:
;
return
$
this
->
smarty
->
fetch
(
$
path
,
$
cacheId
,
$
compileId
)
;
// @codeCoverageIgnoreStart
}
catch
(
\
SmartyException
$
e
)
{
$
this
->
handleViewException
(
$
e
)
;
}
catch
(
Throwable
$
e
)
{
$
this
->
handleViewException
(
new
FatalThrowableError
(
$
e
)
)
;
}
}

Get the evaluated contents of the view at the given path.

@param string $path
@param array  $data

@throws \Exception
@return string

Get the evaluated contents of the view at the given path .


protected function write(array $keys, $expire = 1)
    {
        foreach ($keys as $k => $v) {
            $k = sha1($k);
            $this->redis->setex($k, $expire, $v);
        }
        return true;
    }

protected function write ( array $ keys , $ expire = 1 ) { foreach ( $ keys as $ k => $ v ) { $ k = sha1 ( $ k ) ; $ this -> redis -> setex ( $ k , $ expire , $ v ) ; } return true ; }

Save values for a set of keys to cache

@param  array $keys list of values to save
@param  int   $expire expiration time

@return bool true on success, false on failure


public function getWebdriver()
	{
		$browser = $this->browser;
		$config = parse_ini_file(__DIR__ . '/config.dist.ini', true);

		if (file_exists(__DIR__ . '/config.ini'))
		{
			$config = parse_ini_file(__DIR__ . '/config.ini', true);
		}

		if ($browser == 'chrome')
		{
			$driver['type'] = 'webdriver.chrome.driver';
		}
		elseif ($browser == 'firefox')
		{
			$driver['type'] = 'webdriver.gecko.driver';
		}
		elseif ($browser == 'MicrosoftEdge')
		{
			$driver['type'] = 'webdriver.edge.driver';
		}
		elseif ($browser == 'edg')
		{
			$driver['type'] = 'webdriver.edg.driver';
		}
		elseif ($browser == 'internet explorer')
		{
			$driver['type'] = 'webdriver.ie.driver';
		}

		// All the exceptions in the world...
		if (isset($config[$browser][$this->getOs()]))
		{
			$driver['path'] = __DIR__ . '/' . $config[$browser][$this->getOs()];
		}
		else
		{
			print('No driver for your browser. Check your browser configuration in config.ini');

			// We can't do anything without a driver, exit
			exit(1);
		}

		return '-D' . implode('=', $driver);
	}

Detect the correct driver for selenium

@return  string the webdriver string to use with selenium

@since version


public function getAperture()
    {
        if (!isset($this->data[self::APERTURE])) {
            return false;
        }

        return $this->data[self::APERTURE];
    }

public function getAperture ( ) { if ( ! isset ( $ this -> data [ self :: APERTURE ] ) ) { return false ; } return $ this -> data [ self :: APERTURE ] ; }

Returns the Aperture F-number

@return string|boolean


public function getAuthor()
    {
        if (!isset($this->data[self::AUTHOR])) {
            return false;
        }

        return $this->data[self::AUTHOR];
    }

public function getAuthor ( ) { if ( ! isset ( $ this -> data [ self :: AUTHOR ] ) ) { return false ; } return $ this -> data [ self :: AUTHOR ] ; }

Returns the Author

@return string|boolean


public function getHeadline()
    {
        if (!isset($this->data[self::HEADLINE])) {
            return false;
        }

        return $this->data[self::HEADLINE];
    }

public function getHeadline ( ) { if ( ! isset ( $ this -> data [ self :: HEADLINE ] ) ) { return false ; } return $ this -> data [ self :: HEADLINE ] ; }

Returns the Headline

@return string|boolean


public function getCredit()
    {
        if (!isset($this->data[self::CREDIT])) {
            return false;
        }

        return $this->data[self::CREDIT];
    }

public function getCredit ( ) { if ( ! isset ( $ this -> data [ self :: CREDIT ] ) ) { return false ; } return $ this -> data [ self :: CREDIT ] ; }

Returns the Credit

@return string|boolean


public function getSource()
    {
        if (!isset($this->data[self::SOURCE])) {
            return false;
        }

        return $this->data[self::SOURCE];
    }

public function getSource ( ) { if ( ! isset ( $ this -> data [ self :: SOURCE ] ) ) { return false ; } return $ this -> data [ self :: SOURCE ] ; }

Returns the source

@return string|boolean


public function getJobtitle()
    {
        if (!isset($this->data[self::JOB_TITLE])) {
            return false;
        }

        return $this->data[self::JOB_TITLE];
    }

public function getJobtitle ( ) { if ( ! isset ( $ this -> data [ self :: JOB_TITLE ] ) ) { return false ; } return $ this -> data [ self :: JOB_TITLE ] ; }

Returns the Jobtitle

@return string|boolean


public function getIso()
    {
        if (!isset($this->data[self::ISO])) {
            return false;
        }

        return $this->data[self::ISO];
    }

public function getIso ( ) { if ( ! isset ( $ this -> data [ self :: ISO ] ) ) { return false ; } return $ this -> data [ self :: ISO ] ; }

Returns the ISO speed

@return int|boolean


public function getFocusDistance()
    {
        if (!isset($this->data[self::FOCAL_DISTANCE])) {
            return false;
        }

        return $this->data[self::FOCAL_DISTANCE];
    }

public function getFocusDistance ( ) { if ( ! isset ( $ this -> data [ self :: FOCAL_DISTANCE ] ) ) { return false ; } return $ this -> data [ self :: FOCAL_DISTANCE ] ; }

Returns the focus distance, if it exists

@return string|boolean


public function getWidth()
    {
        if (!isset($this->data[self::WIDTH])) {
            return false;
        }

        return $this->data[self::WIDTH];
    }

public function getWidth ( ) { if ( ! isset ( $ this -> data [ self :: WIDTH ] ) ) { return false ; } return $ this -> data [ self :: WIDTH ] ; }

Returns the width in pixels, if it exists

@return int|boolean

Returns the width in pixels if it exists


public function getHeight()
    {
        if (!isset($this->data[self::HEIGHT])) {
            return false;
        }

        return $this->data[self::HEIGHT];
    }

public function getHeight ( ) { if ( ! isset ( $ this -> data [ self :: HEIGHT ] ) ) { return false ; } return $ this -> data [ self :: HEIGHT ] ; }

Returns the height in pixels, if it exists

@return int|boolean

Returns the height in pixels if it exists


public function getTitle()
    {
        if (!isset($this->data[self::TITLE])) {
            return false;
        }

        return $this->data[self::TITLE];
    }

public function getTitle ( ) { if ( ! isset ( $ this -> data [ self :: TITLE ] ) ) { return false ; } return $ this -> data [ self :: TITLE ] ; }

Returns the title, if it exists

@return string|boolean


public function getCaption()
    {
        if (!isset($this->data[self::CAPTION])) {
            return false;
        }

        return $this->data[self::CAPTION];
    }

public function getCaption ( ) { if ( ! isset ( $ this -> data [ self :: CAPTION ] ) ) { return false ; } return $ this -> data [ self :: CAPTION ] ; }

Returns the caption, if it exists

@return string|boolean


public function getCopyright()
    {
        if (!isset($this->data[self::COPYRIGHT])) {
            return false;
        }

        return $this->data[self::COPYRIGHT];
    }

public function getCopyright ( ) { if ( ! isset ( $ this -> data [ self :: COPYRIGHT ] ) ) { return false ; } return $ this -> data [ self :: COPYRIGHT ] ; }

Returns the copyright, if it exists

@return string|boolean


public function getKeywords()
    {
        if (!isset($this->data[self::KEYWORDS])) {
            return false;
        }

        return $this->data[self::KEYWORDS];
    }

public function getKeywords ( ) { if ( ! isset ( $ this -> data [ self :: KEYWORDS ] ) ) { return false ; } return $ this -> data [ self :: KEYWORDS ] ; }

Returns the keywords, if they exists

@return array|boolean


public function getCamera()
    {
        if (!isset($this->data[self::CAMERA])) {
            return false;
        }

        return $this->data[self::CAMERA];
    }

public function getCamera ( ) { if ( ! isset ( $ this -> data [ self :: CAMERA ] ) ) { return false ; } return $ this -> data [ self :: CAMERA ] ; }

Returns the camera, if it exists

@return string|boolean


public function getHorizontalResolution()
    {
        if (!isset($this->data[self::HORIZONTAL_RESOLUTION])) {
            return false;
        }

        return $this->data[self::HORIZONTAL_RESOLUTION];
    }

public function getHorizontalResolution ( ) { if ( ! isset ( $ this -> data [ self :: HORIZONTAL_RESOLUTION ] ) ) { return false ; } return $ this -> data [ self :: HORIZONTAL_RESOLUTION ] ; }

Returns the horizontal resolution in DPI, if it exists

@return int|boolean

Returns the horizontal resolution in DPI if it exists


public function getVerticalResolution()
    {
        if (!isset($this->data[self::VERTICAL_RESOLUTION])) {
            return false;
        }

        return $this->data[self::VERTICAL_RESOLUTION];
    }

public function getVerticalResolution ( ) { if ( ! isset ( $ this -> data [ self :: VERTICAL_RESOLUTION ] ) ) { return false ; } return $ this -> data [ self :: VERTICAL_RESOLUTION ] ; }

Returns the vertical resolution in DPI, if it exists

@return int|boolean

Returns the vertical resolution in DPI if it exists


public function getSoftware()
    {
        if (!isset($this->data[self::SOFTWARE])) {
            return false;
        }

        return $this->data[self::SOFTWARE];
    }

public function getSoftware ( ) { if ( ! isset ( $ this -> data [ self :: SOFTWARE ] ) ) { return false ; } return $ this -> data [ self :: SOFTWARE ] ; }

Returns the software, if it exists

@return string|boolean


public function getFocalLength()
    {
        if (!isset($this->data[self::FOCAL_LENGTH])) {
            return false;
        }

        return $this->data[self::FOCAL_LENGTH];
    }

public function getFocalLength ( ) { if ( ! isset ( $ this -> data [ self :: FOCAL_LENGTH ] ) ) { return false ; } return $ this -> data [ self :: FOCAL_LENGTH ] ; }

Returns the focal length in mm, if it exists

@return float|boolean

Returns the focal length in mm if it exists


public function getCreationDate()
    {
        if (!isset($this->data[self::CREATION_DATE])) {
            return false;
        }

        return $this->data[self::CREATION_DATE];
    }

public function getCreationDate ( ) { if ( ! isset ( $ this -> data [ self :: CREATION_DATE ] ) ) { return false ; } return $ this -> data [ self :: CREATION_DATE ] ; }

Returns the creation datetime, if it exists

@return \DateTime|boolean

Returns the creation datetime if it exists


public function getColorSpace()
    {
        if (!isset($this->data[self::COLORSPACE])) {
            return false;
        }

        return $this->data[self::COLORSPACE];
    }

public function getColorSpace ( ) { if ( ! isset ( $ this -> data [ self :: COLORSPACE ] ) ) { return false ; } return $ this -> data [ self :: COLORSPACE ] ; }

Returns the colorspace, if it exists

@return string|boolean


public function getMimeType()
    {
        if (!isset($this->data[self::MIMETYPE])) {
            return false;
        }

        return $this->data[self::MIMETYPE];
    }

public function getMimeType ( ) { if ( ! isset ( $ this -> data [ self :: MIMETYPE ] ) ) { return false ; } return $ this -> data [ self :: MIMETYPE ] ; }

Returns the mimetype, if it exists

@return string|boolean


public function getFileSize()
    {
        if (!isset($this->data[self::FILESIZE])) {
            return false;
        }

        return $this->data[self::FILESIZE];
    }

public function getFileSize ( ) { if ( ! isset ( $ this -> data [ self :: FILESIZE ] ) ) { return false ; } return $ this -> data [ self :: FILESIZE ] ; }

Returns the filesize, if it exists

@return int|boolean


public function getOrientation()
    {
        if (!isset($this->data[self::ORIENTATION])) {
            return false;
        }

        return $this->data[self::ORIENTATION];
    }

public function getOrientation ( ) { if ( ! isset ( $ this -> data [ self :: ORIENTATION ] ) ) { return false ; } return $ this -> data [ self :: ORIENTATION ] ; }

Returns the orientation, if it exists

@return int|boolean


public function getGPS()
    {
        if (!isset($this->data[self::GPS])) {
            return false;
        }

        return $this->data[self::GPS];
    }

public function getGPS ( ) { if ( ! isset ( $ this -> data [ self :: GPS ] ) ) { return false ; } return $ this -> data [ self :: GPS ] ; }

Returns GPS coordinates, if it exists

@return array|boolean


protected function extractGPSCoordinate($components)
    {
        if (!is_array($components)) {
            $components = array($components);
        }
        $components = array_map(array($this, 'normalizeComponent'), $components);

        if (count($components) > 2) {
            return floatval($components[0]) + (floatval($components[1]) / 60) + (floatval($components[2]) / 3600);
        }

        return reset($components);
    }

protected function extractGPSCoordinate ( $ components ) { if ( ! is_array ( $ components ) ) { $ components = array ( $ components ) ; } $ components = array_map ( array ( $ this , 'normalizeComponent' ) , $ components ) ; if ( count ( $ components ) > 2 ) { return floatval ( $ components [ 0 ] ) + ( floatval ( $ components [ 1 ] ) / 60 ) + ( floatval ( $ components [ 2 ] ) / 3600 ) ; } return reset ( $ components ) ; }

Extract GPS coordinates from components array

@param array|string $components
@return float


public function setToolPath($path)
    {
        if (!file_exists($path)) {
            throw new InvalidArgumentException(
                sprintf(
                    'Given path (%1$s) to the exiftool binary is invalid',
                    $path
                )
            );
        }

        $this->toolPath = $path;

        return $this;
    }

Setter for the exiftool binary path

@param string $path The path to the exiftool binary
@return \PHPExif\Adapter\Exiftool Current instance
@throws \InvalidArgumentException When path is invalid


public function getToolPath()
    {
        if (empty($this->toolPath)) {
            $path = exec('which ' . self::TOOL_NAME);
            $this->setToolPath($path);
        }

        return $this->toolPath;
    }

public
function
getToolPath
(
)
{
if
(
empty
(
$
this
->
toolPath
)
)
{
$
path
=
exec
(
'which '
.
self
::
TOOL_NAME
)
;
$
this
->
setToolPath
(
$
path
)
;
}
return
$
this
->
toolPath
;
}

Getter for the exiftool binary path
Lazy loads the "default" path

@return string

Getter for the exiftool binary path Lazy loads the default path


protected function getCliOutput($command)
    {
        $descriptorspec = array(
            0 => array('pipe', 'r'),
            1 => array('pipe', 'w'),
            2 => array('pipe', 'a')
        );

        $process = proc_open($command, $descriptorspec, $pipes);

        if (!is_resource($process)) {
            throw new RuntimeException(
                'Could not open a resource to the exiftool binary'
            );
        }

        $result = stream_get_contents($pipes[1]);
        fclose($pipes[0]);
        fclose($pipes[1]);
        fclose($pipes[2]);

        proc_close($process);

        return $result;
    }

Returns the output from given cli command

@param string $command
@return mixed
@throws RuntimeException If the command can't be executed


public function hydrate($object, array $data)
    {
        foreach ($data as $property => $value) {

            $mutator = $this->determineMutator($property);

            if (method_exists($object, $mutator)) {
                $object->$mutator($value);
            }
        }
    }

public function hydrate ( $ object , array $ data ) { foreach ( $ data as $ property => $ value ) { $ mutator = $ this -> determineMutator ( $ property ) ; if ( method_exists ( $ object , $ mutator ) ) { $ object -> $ mutator ( $ value ) ; } } }

Hydrates given array of data into the given Exif object

@param object $object
@param array $data
@return void


protected function extractGPSCoordinates($coordinates)
    {
        if ($this->numeric === true) {
            return abs((float) $coordinates);
        } else {
            if (!preg_match('!^([0-9.]+) deg ([0-9.]+)\' ([0-9.]+)"!', $coordinates, $matches)) {
                return false;
            }

            return floatval($matches[1]) + (floatval($matches[2]) / 60) + (floatval($matches[3]) / 3600);
        }
    }

Extract GPS coordinates from formatted string

@param string $coordinates
@return array


public function getMapper()
    {
        if (null === $this->mapper) {
            // lazy load one
            $mapper = new $this->mapperClass;

            $this->setMapper($mapper);
        }

        return $this->mapper;
    }

public
function
getMapper
(
)
{
if
(
===
$
this
->
mapper
)
{
// lazy load one
$
mapper
=
new
$
this
->
mapperClass
;
$
this
->
setMapper
(
$
mapper
)
;
}
return
$
this
->
mapper
;
}

Accessor for the data mapper

@return \PHPExif\Mapper\MapperInterface


public function getHydrator()
    {
        if (null === $this->hydrator) {
            // lazy load one
            $hydrator = new $this->hydratorClass;

            $this->setHydrator($hydrator);
        }

        return $this->hydrator;
    }

public
function
getHydrator
(
)
{
if
(
===
$
this
->
hydrator
)
{
// lazy load one
$
hydrator
=
new
$
this
->
hydratorClass
;
$
this
->
setHydrator
(
$
hydrator
)
;
}
return
$
this
->
hydrator
;
}

Accessor for the data hydrator

@return \PHPExif\Hydrator\HydratorInterface


public function setOptions(array $options)
    {
        $hydrator = $this->getHydrator();
        $hydrator->hydrate($this, $options);

        return $this;
    }

public function setOptions ( array $ options ) { $ hydrator = $ this -> getHydrator ( ) ; $ hydrator -> hydrate ( $ this , $ options ) ; return $ this ; }

Set array of options in the current object

@param array $options
@return \PHPExif\Reader\AdapterAbstract


public function addRequiredSection($section)
    {
        if (!in_array($section, $this->requiredSections)) {
            array_push($this->requiredSections, $section);
        }

        return $this;
    }

public function addRequiredSection ( $ section ) { if ( ! in_array ( $ section , $ this -> requiredSections ) ) { array_push ( $ this -> requiredSections , $ section ) ; } return $ this ; }

Adds an EXIF section to the list

@param string $section
@return \PHPExif\Adapter\Native Current instance for chaining


public function getIptcData($file)
    {
        getimagesize($file, $info);
        $arrData = array();
        if (isset($info['APP13'])) {
            $iptc = iptcparse($info['APP13']);

            foreach ($this->iptcMapping as $name => $field) {
                if (!isset($iptc[$field])) {
                    continue;
                }

                if (count($iptc[$field]) === 1) {
                    $arrData[$name] = reset($iptc[$field]);
                } else {
                    $arrData[$name] = $iptc[$field];
                }
            }
        }

        return $arrData;
    }

public function getIptcData ( $ file ) { getimagesize ( $ file , $ info ) ; $ arrData = array ( ) ; if ( isset ( $ info [ 'APP13' ] ) ) { $ iptc = iptcparse ( $ info [ 'APP13' ] ) ; foreach ( $ this -> iptcMapping as $ name => $ field ) { if ( ! isset ( $ iptc [ $ field ] ) ) { continue ; } if ( count ( $ iptc [ $ field ] ) === 1 ) { $ arrData [ $ name ] = reset ( $ iptc [ $ field ] ) ; } else { $ arrData [ $ name ] = $ iptc [ $ field ] ; } } } return $ arrData ; }

Returns an array of IPTC data

@param string $file The file to read the IPTC data from
@return array


public static function factory($type)
    {
        $classname = get_called_class();
        switch ($type) {
            case self::TYPE_NATIVE:
                $adapter = new NativeAdapter();
                break;
            case self::TYPE_EXIFTOOL:
                $adapter = new ExiftoolAdapter();
                break;
            default:
                throw new \InvalidArgumentException(
                    sprintf('Unknown type "%1$s"', $type)
                );
        }
        return new $classname($adapter);
    }

public
static
function
factory
(
$
type
)
{
$
classname
=
get_called_class
(
)
;
switch
(
$
type
)
{
case
self
::
TYPE_NATIVE
:
$
adapter
=
new
NativeAdapter
(
)
;
break
;
case
self
::
TYPE_EXIFTOOL
:
$
adapter
=
new
ExiftoolAdapter
(
)
;
break
;
default
:
throw
new
\
InvalidArgumentException
(
sprintf
(
'Unknown type "%1$s"'
,
$
type
)
)
;
}
return
new
$
classname
(
$
adapter
)
;
}

Factory for the reader

@param string $type
@return $this
@throws \InvalidArgumentException When given type is invalid


protected function _generatePrefix()
    {
        $prefix = Inflector::underscore(Inflector::singularize($this->_table->getAlias()));
        $prefix .= '_' . $this->_alias;

        return strtoupper($prefix);
    }

protected function _generatePrefix ( ) { $ prefix = Inflector :: underscore ( Inflector :: singularize ( $ this -> _table -> getAlias ( ) ) ) ; $ prefix .= '_' . $ this -> _alias ; return strtoupper ( $ prefix ) ; }

Generates default prefix for strategy.

@return string


protected function _normalizeConfig()
    {
        $classMap = $this->getConfig('classMap');
        $this->_classMap = array_merge($this->_classMap, $classMap);

        $lists = $this->getConfig('lists');
        $defaultStrategy = $this->getConfig('defaultStrategy');

        foreach ($lists as $alias => $config) {
            if (is_numeric($alias)) {
                unset($lists[$alias]);
                $alias = $config;
                $config = [];
                $lists[$alias] = $config;
            }

            if (is_string($config)) {
                $config = ['prefix' => strtoupper($config)];
            }

            if (empty($config['strategy'])) {
                $config['strategy'] = $defaultStrategy;
            }

            $lists[$alias] = $this->strategy($alias, $config['strategy'])
                ->initialize($config)
                ->getConfig();
        }

        $this->setConfig('lists', $lists, false);
    }

protected function _normalizeConfig ( ) { $ classMap = $ this -> getConfig ( 'classMap' ) ; $ this -> _classMap = array_merge ( $ this -> _classMap , $ classMap ) ; $ lists = $ this -> getConfig ( 'lists' ) ; $ defaultStrategy = $ this -> getConfig ( 'defaultStrategy' ) ; foreach ( $ lists as $ alias => $ config ) { if ( is_numeric ( $ alias ) ) { unset ( $ lists [ $ alias ] ) ; $ alias = $ config ; $ config = [ ] ; $ lists [ $ alias ] = $ config ; } if ( is_string ( $ config ) ) { $ config = [ 'prefix' => strtoupper ( $ config ) ] ; } if ( empty ( $ config [ 'strategy' ] ) ) { $ config [ 'strategy' ] = $ defaultStrategy ; } $ lists [ $ alias ] = $ this -> strategy ( $ alias , $ config [ 'strategy' ] ) -> initialize ( $ config ) -> getConfig ( ) ; } $ this -> setConfig ( 'lists' , $ lists , false ) ; }

Normalizes the strategies configuration and initializes the strategies.

@return void

Normalizes the strategies configuration and initializes the strategies .


protected function _translate(array $list)
    {
        $domain = $this->getConfig('translationDomain');

        return array_map(function ($value) use ($domain) {
            return __d($domain, $value);
        }, $list);
    }

protected function _translate ( array $ list ) { $ domain = $ this -> getConfig ( 'translationDomain' ) ; return array_map ( function ( $ value ) use ( $ domain ) { return __d ( $ domain , $ value ) ; } , $ list ) ; }

Translate list values.

@param array $list List.
@return array


public function run($request)
    {
        $job = new StaticCacheFullBuildJob();
        $signature = $job->getSignature();

        // see if we already have this job in a queue
        $filter = [
            'Signature' => $signature,
            'JobStatus' => [
                QueuedJob::STATUS_NEW,
                QueuedJob::STATUS_INIT,
            ]
        ];

        $existing = DataList::create(QueuedJobDescriptor::class)->filter($filter)->first();

        if ($existing && $existing->ID) {
            $this->log(sprintf(
                'There is already a %s in the queue, added %s %s',
                StaticCacheFullBuildJob::class,
                $existing->Created,
                $existing->StartAfter ? 'and set to start after ' . $existing->StartAfter : ''
            ));
            return false;
        }

        if ($request->getVar('startAfter')) {
            $now = DBDatetime::now();
            $today = $now->Date();
            $startTime = $request->getVar('startAfter');

            // move to tomorrow if the starttime has passed today
            if ($now->Time24() > $startTime) {
                $timestamp = strtotime($today . ' ' . $startTime . ' +1 day');
                $dayWord = 'tomorrow';
            } else {
                $timestamp = strtotime($today . ' ' . $startTime);
                $dayWord = 'today';
            }
            $startAfter = (new \DateTime())->setTimestamp($timestamp);
            $thisTimeTomorrow = (new \DateTime())->setTimestamp(strtotime($now . ' +1 day'))->getTimestamp();

            // sanity check that we are in the next 24 hours - prevents some weird stuff sneaking through
            if ($startAfter->getTimestamp() > $thisTimeTomorrow || $startAfter->getTimestamp() < $now->getTimestamp()) {
                $this->log('Invalid startAfter parameter passed. Please ensure the time format is HHmm e.g. 1300');
                return false;
            }

            $this->log(sprintf(
                '%s queued for %s %s.',
                StaticCacheFullBuildJob::class,
                $startAfter->format('H:m'),
                $dayWord
            ));
        } else {
            $startAfter = null;
            $this->log(StaticCacheFullBuildJob::class . ' added to the queue for immediate processing');
        }

        $job->setJobData(0, 0, false, new \stdClass(), [
            'Building static cache for full site'
        ]);
        QueuedJobService::singleton()->queueJob($job, $startAfter ? $startAfter->format('Y-m-d H:i:s') : null);

        return true;
    }

Queue up a StaticCacheFullBuildJob
Check for startAfter param and do some sanity checking

@param HTTPRequest $request
@return


public function process()
    {
        $chunkSize = self::config()->get('chunk_size');
        $count = 0;

        // Remove any URLs which have already been processed
        if ($this->jobData->ProcessedURLs) {
            $this->jobData->URLsToProcess = array_diff_key(
                $this->jobData->URLsToProcess,
                $this->jobData->ProcessedURLs
            );
        }

        foreach ($this->jobData->URLsToProcess as $url => $priority) {
            if (++$count > $chunkSize) {
                break;
            }
            $meta = Publisher::singleton()->publishURL($url, true);
            if (!empty($meta['success'])) {
                $this->jobData->ProcessedURLs[$url] = $url;
                unset($this->jobData->URLsToProcess[$url]);
            }
        }
        if (empty($this->jobData->URLsToProcess)) {
            $trimSlashes = function ($value) {
                return trim($value, '/');
            };
            $this->jobData->publishedURLs = array_map($trimSlashes, Publisher::singleton()->getPublishedURLs());
            $this->jobData->ProcessedURLs = array_map($trimSlashes, $this->jobData->ProcessedURLs);
            $this->jobData->URLsToCleanUp = array_diff($this->jobData->publishedURLs, $this->jobData->ProcessedURLs);

            foreach ($this->jobData->URLsToCleanUp as $staleURL) {
                $purgeMeta = Publisher::singleton()->purgeURL($staleURL);
                if (!empty($purgeMeta['success'])) {
                    unset($this->jobData->URLsToCleanUp[$staleURL]);
                }
            }
        };
        $this->isComplete = empty($this->jobData->URLsToProcess) && empty($this->jobData->URLsToCleanUp);
    }


public function collectChanges($context)
    {
        Environment::increaseMemoryLimitTo();
        Environment::increaseTimeLimitTo();

        if ($this->getOwner()->hasExtension(PublishableSiteTree::class)
            || $this->getOwner() instanceof StaticPublishingTrigger
        ) {
            $toUpdate = $this->getOwner()->objectsToUpdate($context);
            $this->setToUpdate($toUpdate);

            $toDelete = $this->getOwner()->objectsToDelete($context);
            $this->setToDelete($toDelete);
        }
    }

public function collectChanges ( $ context ) { Environment :: increaseMemoryLimitTo ( ) ; Environment :: increaseTimeLimitTo ( ) ; if ( $ this -> getOwner ( ) -> hasExtension ( PublishableSiteTree :: class ) || $ this -> getOwner ( ) instanceof StaticPublishingTrigger ) { $ toUpdate = $ this -> getOwner ( ) -> objectsToUpdate ( $ context ) ; $ this -> setToUpdate ( $ toUpdate ) ; $ toDelete = $ this -> getOwner ( ) -> objectsToDelete ( $ context ) ; $ this -> setToDelete ( $ toDelete ) ; } }

Collect all changes for the given context.

@param array $context

Collect all changes for the given context .