NotionCommotion Posted June 27, 2019 Share Posted June 27, 2019 (edited) 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 June 27, 2019 by NotionCommotion Nevermind => Maybe I should start my constants with high bit set such as const SANITIZE = 0b1000; Quote Link to comment Share on other sites More sharing options...
kicken Posted June 27, 2019 Share Posted June 27, 2019 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted June 27, 2019 Author Share Posted June 27, 2019 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? Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted June 27, 2019 Author Share Posted June 27, 2019 (edited) 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 June 27, 2019 by NotionCommotion Quote Link to comment Share on other sites More sharing options...
kicken Posted June 27, 2019 Share Posted June 27, 2019 (edited) 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 June 27, 2019 by kicken Quote Link to comment Share on other sites More sharing options...
gizmola Posted June 27, 2019 Share Posted June 27, 2019 It's always good to look at how the major frameworks solve these types of problems. For example take a look at Symfony's OptionsResolver component. Quote Link to comment Share on other sites More sharing options...
requinix Posted June 28, 2019 Share Posted June 28, 2019 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 } Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.