Jump to content

Specifying default values for bitmask constants


NotionCommotion

Recommended Posts

I sometime pass configuration settings as an associate array... 

public function __construct(JsonValidatorMethods $methods, array $options) {
    foreach($options as $option) {
        //validate whether valid and if so set $this->$option.  Options not provided will default to their private $strictMode=true value
    }
}
$validator=new JsonValidator($methods, ['sanitize'=>true, 'strictMode'=>true, 'setDefault'=>false]);

And other times pass them as individual arguments...

public function __construct(JsonValidatorMethods $methods, bool $sanitize=false, bool $strictMode=false, bool $setDefault=true) {}
$validator=new JsonValidator($methods, true, true, false);

I have a class which I was doing the later and also which I wish to allow them to be first set in the constructor but also specified when calling some of the public methods.  It became a pain to pass all the individual flags to each private method, so I thought I would give bitmask constants a try as shown below.

I recognize that this approach only allows booleans to be passed, but I am okay with this.  The part I am not sure about is how best have default values.  For instance, maybe the default is $this->options=0b0110, 0b1100 is passed to the constructor, and 0b1 (aka 0b0001) is passed is passed to the public method.  The high three bits in 0b0001 just are not provided, but how can I not interpret them as explicitly false?

Also, a little off topic, but am I implementing this bitmask approach correctly, and any strong options whether one of these three approaches or some totally other approach is best?

Thanks

class JsonValidator
{
    //Use provided options
    const SANITIZE      =   0b0001;   //Whether to sanitize (i.e. 'false' is changed to false)
    const STRICTMODE    =   0b0010;   //Currently only enfources that boolean is true/false (instead of 1/0 or "true"/"false")
    const SETDEFAULT    =   0b0100;   //If true, type double and value "nan" will be converted to 0, etc.

    private $methods, $options;

    static public function create(int $options=0):self {
        return new self(new JsonValidatorMethods, $options);
    }

    static public function getOption(array $options):int {
        $o=0;
        foreach($options as $option) {
            if(!defined("self::$option")){
                throw new JsonValidatorErrorException((is_string($option)?$option:'given constant').' is not valid');
            }
            $o=$o|constant("self::$option");
        }
        return $o;
    }

    public function __construct(JsonValidatorMethods $methods, int $options=0) {
        $this->methods=$methods;
        $this->options=$options;
    }

    public function validate(array $input, array $rules, int $options=0){

        $options=$options|$this->options;

        if($options & self::SANITIZE) {
            $value=$this->sanitize($input, $rules, $options);
        }
    }

    private function sanitize(array $input, array $rules, int $options=0){}
}

$validator=new JsonValidator($methods, JsonValidator::SANITIZE|JsonValidator::SETDEFAULT);

 

Edited by NotionCommotion
Nevermind => Maybe I should start my constants with high bit set such as const SANITIZE     =  0b1000;
Link to comment
Share on other sites

46 minutes ago, NotionCommotion said:

The high three bits in 0b0001 just are not provided, but how can I not interpret them as explicitly false?

You can't.  There's no such thing as "those bits were not provided" really.  A number is always 32/64 bits in length.  All bits start as 0 then you explicitly turn on the ones you want to make a given number.

Bit masks don't work well in this type of situation.  A better solution is just to use an array of options.

 

Link to comment
Share on other sites

7 minutes ago, kicken said:

You can't.  There's no such thing as "those bits were not provided" really.  A number is always 32/64 bits in length.  All bits start as 0 then you explicitly turn on the ones you want to make a given number.

Bit masks don't work well in this type of situation.  A better solution is just to use an array of options.

But this approach is used all the time for core PHP functions.  What is different with using them here versus with core PHP functions?

Link to comment
Share on other sites

3 hours ago, kicken said:

Bit masks don't work well in this type of situation.  A better solution is just to use an array of options.

Regarding "in this type of situation", I assume you mean setting the values both in the constructor and in the method.  Never really thought about it but I suppose the default value for all core PHP predefined constants used this way must be false.  So, my example here will work just fine if I too make the default values false and just allow them to be set in either the constructor or method but not both as doing so voids this assumption of default false values.  If it is okay that they can be enabled in both the constructor or method or both, but not disabled, should work as well. 

Edited by NotionCommotion
Link to comment
Share on other sites

In the cases where PHP uses bit masks, they are used for just one particular call or as part of the constructor as you noticed.  There's no trying to merge defaults with call specific options because that does't work.

It seems like in your original post the goal was to be able to do something like:

$o = JsonValidator::create(JsonValidator::STRICTMODE);
$o->validate($input, $rules, JsonValidator::SETDEFAULT);  //Keep strict mode and add additional SETDEFAULT option.

//Same as above but this time disable strict mode?  Can't be done
$o->validate($input, $rules, JsonValidator::SETDEFAULT);

That's simply not possible with bit mask values because as noted, there's no way to distinguish between 'strict mode not specified' and 'strict mode disabled'

The only way to really have your flags be specified in both the constructor and in specific methods would be if you design it such that the flags specified in the constructor are always on and cannot be disabled later in a specific method call.  Then in your call you could just do

public function validate($input, $rules, $options = 0){
    //Merge in constructor defaults.
    $options = $this->defaultOptions | $options;

    //...
}

 

Edited by kicken
Link to comment
Share on other sites

Simple answer with an options array:

public function __construct(array $defaults = []) {
	$this->defaults = $defaults;
}
public function whatever(array $options = []) {
	$options += $this->defaults + [
		"foo" => "bar"
	];

	// $options["foo"] comes from $options if set, or else $this->defaults if set, or else is your own default
}

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.