Reschedule Events 05

Testing the destruction option.

Destruct method

The PHP language allows for something to happen when an object instance is destroyed or, in low level terms, the memory keeping record of its existence is freed or made available again.
This “garbage collection” will happen as soon as nothing in the running PHP thread is holding a reference to the object.
In simple terms the object created here will have nothing referring to it and will be destroyed immediately:

function createAndDestroy(){

    new myClass();
    // <<< here the engine will call myClass::__destruct()

}

while here a variable will hold a reference to the object and until that variable is in its scope the object will not be destroyed

function createAndStore(){

    $var = new myClass();

    $var->set('some value', 23);
    $var->doSomething();

    // <<< here the engine will call myClass::__destruct()
    // since the scope of `var` ends and with it any reference
    // to the myClass instance
}

What good is destruction to me?

Resuming the considerations I’ve done while developing the reschedule utility plugin in my earlier post I’d like not to add a chain tail method to the tad_reschedule function call; in coding terms I’d like this to be enough to make the function chain do its job:

tad_reschedule('my_hook')
    ->until( get_option('my_condition', false) )
    ->each( 600 )
    ->with_args( array( 'one', 'two', 'three') );

What is not happening is that the instance of the class created by the function is not being stored anywhere and should be destroyed as soon as the last method in the chain, with_args in the case above, is called.

Testing the idea

I’ve created a test for the idea relying on function-mocker short syntax to verify that the Dummy1122::callback method will be called once the chain is ended and the class instance is in zombie state (right before __destruct method is called)

<?php

use tad\FunctionMocker\FunctionMocker as Test;

class Dummy1122 {

    public function callback() {

    }
}


class tad_RescheduleDestructTest extends \WP_UnitTestCase {

    protected $backupGlobals = false;

    public function setUp() {
        // before
        parent::setUp();
        Test::setUp();
        // your set up methods here
    }

    public function tearDown() {
        // your tear down methods here

        // then
        Test::tearDown();
        parent::tearDown();
    }

    /**
     * @test
     * it should call the function when destructing
     */
    public function it_should_call_the_function_when_destructing() {
        $object = Test::replace( 'Dummy1122' )->method( 'callback' )->get();

        tad_reschedule( 'my_hook' )->until( [ $object, 'callback' ] )->each( 60 )->with_args( [ 'foo' ] );

        $object->wasCalledOnce( 'callback' );
    }
}

to prove the theory I’ve modified the tad_Reschedule class as little as possible to call the callback method passed into the until method upon destruction

<?php
/**
 * Plugin Name: Reschedule Utility
 * Plugin URI: http://theAverageDev.com
 * Description: Easy cron event rescheduling.
 * Version: 1.0
 * Author: theAverageDev
 * Author URI: http://theAverageDev.com
 * License: GPL 2.0
 */


if ( ! function_exists( 'tad_reschedule' ) ) {
    function tad_reschedule( $hook ) {
        if ( ! is_string( $hook ) ) {
            throw new InvalidArgumentException( 'Hook name must be a string' );
        }

        return new tad_Reschedule();
    }
}


class tad_Reschedule {

    /**
     * @var string|callable A function name or a callable the result of which will be used to assert the until
     *      condition.
     */
    public $until_callback;

    public function until( $condition ) {
        if ( is_callable( $condition ) ) {
            $this->until_callback = $condition;
        }

        return $this;
    }

    public function each( $interval ) {
        if ( ! ( is_callable( $interval ) || is_numeric( $interval ) ) ) {
            throw new \InvalidArgumentException( 'Interval must be an int value or a callable.' );
        }

        return $this;
    }

    public function with_args( $args ) {
        if ( ! ( is_callable( $args ) || is_array( $args ) || is_null( $args ) ) ) {
            throw new \InvalidArgumentException( 'Arguments must be an array or a callable.' );
        }

        return $this;
    }

    function __destruct() {
        if ( $this->until_callback ) {
            call_user_func( $this->until_callback );
        }
    }
}

The code is just draft and will be subject to change but passes the test:

codecept run tests/functional/tad_RescheduleDestructTest.php

The __destruct method really seems a viable way to accomplish the task.
[test pass]

My past fiddling in the destruction yard had the issue of the __destruct method being called more than once; in my case, a call to the wp_schedule_single_event being the action scope, that should not happen.
The function-mocker test tool is making an exact times match (no more, no less times) when using the wasCalledOnce method and that addresses my concerns.

Next

I’ve decided my chain end will not be leaving the __destruct method the task to act on the chain behalf.
This post code is on GitHub.

Probably related

I appreciate your input