Etsy sync WordPress plugin 10

Too many parameter types.

Further refactoring

Approaching the development of the ELH_ListingsRetriever class I’ve took another look at the code of the previously developed ELH_ShopRetriever class and was able to reduce its code moving it to the parent both classes share; the class itself is slimmer now

class ELH_ShopRetriever extends ELH_RequestingSyncStep {

    public static function instance( ELH_KeychainInterface $keychain, ELH_ApiInterface $api, ELH_ApiRequestInterface $request, ELH_RequestCompilerInterface $request_compiler ) {
        $instance                   = new self();
        $instance->keychain         = $keychain;
        $instance->api              = $api;
        $instance->request          = $request;
        $instance->request_compiler = $request_compiler;

        return $instance;
    }

    /**
     * @param $data
     *
     * @throws ELH_SyncException
     */
    protected function ensure_request_specifics( $data ) {
        if ( $data['response']['code'] != '200' ) {
            $message = sprintf( 'Shop retrieving failed with code %d and message "%s"', $data['response']['code'], $data['response']['message'] );
            throw new ELH_SyncException( $message );
        }
    }

    /**
     * @return array
     */
    protected function get_request_data() {
        $data = array(
            'user_id' => get_option( ELH_Main::USER_ID_OPTION ),
            'api_key' => $this->api->get_api_key()
        );

        return $data;
    }

}

and would be even further if I had eschewed the code duplication in the instance method in favor of late static binding throwing PHP 5.2 compatibility out of the window; I’ve decided to develop for WordPress current minimum requirements and will get along.

Why all this refactoring?

In a world: laziness and adherence to some self-imposed and commonly accepted TDD rules: I want to cover all the code I write so any additional code I write will force me to pay in new tests (code nonetheless). So keep code down to write less tests.

New types

A quick look at the Etsy documentation about the method reveals that the request to fetch a shop listings will take more parameters and of a more complex nature then the ones that are required to fetch the list of a user shops.
Due to this I need to get back to the class responsible for the validation of the request parameters and add the missing types.
Some TDDing later all the needed types are added and I will be able to begin working on the class responsible for the retrieval of a shop listings.

class ELH_TypeUtils {

    public static function is_a( $value, $type ) {
        if ( ! is_scalar( $value ) ) {
            return false;
        }
        $check = false;
        try {
            $types = strpos( $type, '|' ) ? explode( '|', $type ) : array( $type );
            foreach ( $types as $_type ) {
                switch ( $_type ) {
                    case 'string':
                        $check = $check || self::is_string( $value );
                        break;
                    case 'int':
                        $check = $check || self::is_int( $value );
                        break;
                    case 'float':
                        $check = $check || self::is_float( $value );
                        break;
                    case 'bool':
                        $check = true;
                        break;
                    case 'hsv';
                        $check = $check || self::is_hsv( $value );
                        break;
                    case 'rgb';
                        $check = $check || self::is_rgb( $value );
                        break;
                    default:
                        $check = $check || self::is_in_enum( $value, $_type ) || self::is_in_interval( $value, $_type ) || self::is_in_list( $value, $_type );
                        break;
                }
            }
        } catch ( Exception $e ) {
        }

        return $check;
    }

    public static function is_string( $value ) {
        return is_string( $value );
    }

    public static function is_int( $value ) {
        return ( is_numeric( $value ) && intval( $value ) == floatval( $value ) );
    }

    public static function is_float( $value ) {
        return is_numeric( $value ) && ( is_float( floatval( $value ) ) || is_int( intval( $value ) ) );
    }

    public static function is_hsv( $value ) {
        $m = array();
        if ( preg_match( '/^(\\d+);(\\d+);(\\d+)$/', $value, $m ) ) {
            return $m[1] >= '0' && $m[2] >= '0' && $m[3] >= '0' && $m[1] <= '360' && $m[2] <= '100' && $m[3] <= '100';
        }

        return false;
    }

    public static function is_rgb( $value ) {
        return preg_match( '/^#*(?:[0-9a-fA-F]{3}){1,2}$/', $value );
    }

    public static function is_in_enum( $value, $type ) {
        $m = array();
        if ( preg_match( '/^\\{(.+)\\}$/', $type, $m ) ) {
            $legit = $m[1];

            return in_array( $value, explode( ',', $legit ) );
        }

        return false;
    }

    public static function is_in_interval( $value, $_type ) {
        $m = array();
        if ( preg_match( '/^\\[(.+)-(.+)\\]$/', $_type, $m ) ) {
            return in_array( $value, range( $m[1], $m[2] ) );
        }

        return false;
    }

    public static function is_in_list( $value, $_type ) {
        $m = array();
        if ( preg_match( '/^\\[(\w+)\\]$/', $_type, $m ) ) {
            if ( is_string( $value ) ) {
                if ( ! strpos( $value, ',' ) ) {
                    return true;
                }
                foreach ( explode( ',', $value ) as $v ) {
                    if ( ! self::is_a( $v, $m[1] ) ) {
                        return false;
                    }

                    return true;
                }
            }

            return false;
        }

        return false;
    }
}

I’ve also modified the ELH_ShopListingsApiRequest to use the new fields

class ELH_ShopListingsApiRequest implements ELH_ApiRequestInterface {

    private $parameters;

    public function __construct() {
        $this->parameters = array(
            new ELH_ApiRequestParameter( 'shop_id', true, false, 'string|int' ),
            new ELH_ApiRequestParameter( 'limit', false, 25, 'int' ),
            new ELH_ApiRequestParameter( 'offset', false, 0, 'int' ),
            new ELH_ApiRequestParameter( 'page', false, false, 'int' ),
            new ELH_ApiRequestParameter( 'keywords', false, false, 'string' ),
            new ELH_ApiRequestParameter( 'sort_on', false, 'created', '{created,price,score}' ),
            new ELH_ApiRequestParameter( 'sort_order', false, 'down', '{up,down}' ),
            new ELH_ApiRequestParameter( 'min_price', false, false, 'float' ),
            new ELH_ApiRequestParameter( 'max_price', false, false, 'float' ),
            new ELH_ApiRequestParameter( 'color', false, false, 'hsv|rgb' ),
            new ELH_ApiRequestParameter( 'color_accuracy', false, 0, '[0-30]' ),
            new ELH_ApiRequestParameter( 'tags', false, false, '[string]' ),
            new ELH_ApiRequestParameter( 'category', false, false, 'string' ),
            new ELH_ApiRequestParameter( 'translate_keywords', false, 0, 'bool' ),
            new ELH_ApiRequestParameter( 'include_private', false, 0, 'bool' ),
        );
    }

    /**
     * @return string
     */
    public function uri() {
        return '/users/:shop_id/listings/active';
    }

    /**
     * @return string
     */
    public function method() {
        return 'findAllShopListingsActive';
    }

    /**
     * @return bool
     */
    public function requires_OAuth() {
        return false;
    }

    /**
     * @return string
     */
    public function permission_scope() {
        return 'none';
    }

    public function http_method() {
        return 'GET';
    }

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

Next

I will finally code the ELH_ShopRetriever class and begin a complete thought about what the sync chain is supposed to do.