Ik ben nu bezig met de output van verschillende functies in binairy terug te laten geven zodat ik in 1 keer meer fouten kan terug geven als er iets aan de hand is.
Nu werkt het wel, maar mijn vraag is of het te vergemakkelijken met (korter te maken) is.
<?php
class User{
const R_INVALID_FORMAT_EMAIL = 1;
const R_INVALID_FORMAT_PASSWORD = 2;
const R_UNKNOWN_CREDENTIALS = 4;
const R_CAPATCHA_INVALID = 8;
const R_ATTEMPT_TIMEOUT = 16;
// etc.
public function login($email, $password){
$result = 0;
if(!Validate::email($email)){
$result += self::R_INVALID_FORMAT_EMAIL;
}
if(!Validate::password($password)){
$result += self::R_INVALID_FORMAT_PASSWORD;
}
// Met name, deze lijn met code.
if(~$result & self::R_INVALID_FORMAT_EMAIL && ~$result & self::R_INVALID_FORMAT_PASSWORD){
// since we now must query the database, only do this when both email and password format is correct.
}
return $result;
}
}
?>
Neem deze code, stel dat de gebruiker inlogd met "ozzie" en als wachtwoord "test".
Validate::email("ozzie") -> false (geen email aderes).
Validate::password("test") -> false (omdat minimal string length 6 moet zijn)
De waarde die word terug gekeerd is in dit geval 1 + 2 = 3.
Deze login request word door een ajax request gedaan en stuur 3 door via json.
Nu weet ik door 1 getal dat zowel de username en password fout zijn door de binaire representatie: "00000011" en kan dit direct aanpassen in het login form.
Tweede voorbeeld:
Neem deze code, stel dat de gebruiker inlogd met "[email protected]" en als wachtwoord "test".
De waarde die word terug gekeerd is in dit geval 2 in binair "00000010"
Nu weet ik dat het wachtwoord foutief is, en de email correct.
Dus wat je er eventueel met de "~" mee zou kunnen doen is kijken of alle "flags" goed zijn, behalve het wachtwoord. Het is een not operator op meerdere booleans. In mijn geval, draai ik "0" de standaard waarde van result om in alles. Ik kan ook de result de waarde geven van (16*2) - 1 om alle flags als 1 te zetten.
Beetje hetzelfde als de E_ALL constant.
Ik weet niet of bitmaskers heel erg toepasselijk zijn voor deze manier van valideren. Waarom doe je dit niet gewoon zonder? Als je dit gebruikt om bij te houden wat valideert, houd dit dan gewoon per veld bij, in plaats van deze opzet?
Ik weet niet of bitmaskers heel erg toepasselijk zijn voor deze manier van valideren. Waarom doe je dit niet gewoon zonder? Als je dit gebruikt om bij te houden wat valideert, houd dit dan gewoon per veld bij, in plaats van deze opzet?
Mijn opzet is dat alle code wat in het systeem draait taal onafhankelijk is, dus strings terug geven met daarin de fout is dus een no-no. Als ik booleans ga terug keren dan is de vraag, wat is er fout? Bitmasks past perfect in dit plaatje en het is nog erg snel ook.
Als ik eerlijk mag zijn heb ik vrij weinig met bitwise operators gewerkt, daarom mijn vraag ook. Ik neem aan dat ik iets van if($result & (FLAG_A & FLAG_B)){} kan doen maar wat ik ook probeer het resulteerd in of allebij true of false.
Het leent zich gewoon niet echt voor formulierdata in het algemeen, het is uiteindelijk toch de bedoeling dat alle velden die op een of andere manier een validatie hebben succesvol moeten valideren. De introductie van een bitmasker die zoveel doet als "alles moet valideren" lijkt mij een overcomplificering. Dit zou je ook kunnen reduceren tot één boolean.
Als je formulier ook wat uitgebreider wordt of wanneer je velden van volgorde gaat veranderen is het overzicht al snel weg denk ik (de constanten die machten van 2 zijn worden al snel vrij groot). Daarom stelde ik voor dat je gewoon per veld (en bij het veld zelf, en niet met allerlei hardcoded numerieke waarden) bijhoudt of deze geldige inhoud bevat of niet.
Ook snap ik niet helemaal wat dit in een user Class doet. Wat zou de login() methode moeten teruggeven, en wat betekent dat? Ik zou verwachten dat een login() methode van een User class iets doet met "inloggen", maar dit controleert enkel (het format van?) invoervelden, en verder niets?
Bovenstaande syntax ziet er al vrij complex uit. Als er iets helder en transparant moet zijn is het wel je loginroutine... Booleans debuggen lijkt mij nog altijd makkelijker dan zoiets terugkrijgen: "010101100101111010111101011111010111110001111" en dat je dan zegt "Ooooh, er staat een 1 op positie 28 verkeerd, dat ik dat niet eerder zag".
Ook snap ik niet helemaal wat dit in een user Class doet. Wat zou de login() methode moeten teruggeven, en wat betekent dat? Ik zou verwachten dat een login() methode van een User class iets doet met "inloggen", maar dit controleert enkel (het format van?) invoervelden, en verder niets?
Bovenstaande syntax ziet er al vrij complex uit. Als er iets helder en transparant moet zijn is het wel je loginroutine... Booleans debuggen lijkt mij nog altijd makkelijker dan zoiets terugkrijgen: "010101100101111010111101011111010111110001111" en dat je dan zegt "Ooooh, er staat een 1 op positie 28 verkeerd, dat ik dat niet eerder zag".
:)
Dit is ook maar een sample code, het weglaten van dingen die er niet toe doen aan de vraag en ik had voor dit voorbeeld de waardes simpelweg aangepast.
Over het debuggen, daarom werk je ook met constanten en niet echt met binaire data. Je kijkt alleen of er een constant actief is in het getal, niets gecompliceerd aan het is alleen een andere werk methode.
<?php
if(($r = User::login($user, $pass)) !== true){
if($r & User::R_INVALID_FORMAT_EMAIL){
echo 'Some javascript code to mark the email field red with an error message perhaps.';
}
if($r & User::R_INVALID_FORMAT_PASSWORD){
// Oh, what specifically went wrong with the password?
$t = Validate::password($pass);
if($t & Validate::R_PASSWORD_LENGTH_MIN){
// password is shorter then minimum length
}
if($t & Validate::R_PASSWORD_NO_SYMBOL){
// password does not contain symbols.
}
if($t & Validate::R_PASSWORD_NO_DIGIT){
// password does not contain any digits.
}
}
}
//in plaats van:
if(!User::login($user, $pass)){
if(Validate::email($user)){
if(emailExists($user)){
//etc
}
}
if(Validate::password($pass)){
// etc
}
}
?>
Je vergelijk je alleen de flags/constants en zit je niet te kloten met functies of andere input data want alles word al behandeld in User::login(). Dit resulteert alleen maar in kortere, snellere (omdat je niet twee keer dezelfde functie hoeft aan te roepen) en meer overzichtelijke code naar mijn idee.
Deze code (login) gaat waarschijnlijk nog wel een andere locatie of andere constant namen krijgen, ik zit er gewoon momenteel even mee te "spelen" wat het lekkerste werkt.
En niet alleen deze functie gaat zo lopen, bitwise operators zijn heel krachtig en kunnen op veel plekken gebruikt worden. Het is ook niet de vraag waarom, maar hoe. Ik wil er wat meer ervaring mee op doen en hoopte dat iemand hier dat ook had en of hij of zij dit iets anders zou schrijven.
Hmmm, ik had een opmerking geplaatst, maar die lijkt niet te zijn doorgekomen ... nogmaals:
>> De waarde die word terug gekeerd is in dit geval 2 in binair "00000010"
En waarom dan binair? Waarom niet gewoon 2?
>> Mijn opzet is dat alle code wat in het systeem draait taal onafhankelijk is, dus strings terug geven met daarin de fout is dus een no-no. Als ik booleans ga terug keren dan is de vraag, wat is er fout?
Je kan toch met exceptions werken en dan bijv. foutcodes meesturen?
>> En waarom dan binair? Waarom niet gewoon 2?
Als je kijkt naar het script, geef ik ook gewoon een getal terug.
Waar het om gaat is hoe bitwise operators werken, die vergelijkt de bitjes van het getal en niet direct de waarde.
<?php
$a = 1; // 0001 3 = 0011
$b = 2; // 0010 5 = 0101
$c = 4; // 0100 6 = 0110
$d = 8; // 1000 7 = 0111
echo 3 & $a; // aangezien het laatste bitje van 1 overeenkomt met het laatste bitje van 3, geeft hij de bitwaarde van 0001 terug dus in ditgeval het getal "1".
echo 6 & ($b | $a); // de waarde van 6 komt niet overeen met de bitjes van $a (1), wel van $b (2), ik vergelijk dit met $a of $b, in dit geval $b dus "2".
// In het voorbeeld kan je 3 terug krijgen van de functie (0011) als je wachtwoord en email fout zijn, nu met deze code kan je kijken wat er fout is.
if(3 & 1){
echo 'bitje van 3 (0011) zit ook in 1 (0001).';
}
if(3 & 2){
echo 'bitje van 3 (0011) zit ook in 2.(0010)';
}
if(3 & 3){
// deze is overbodig want dit word nu dubbel uitgevoerd omdat 1 & 2 dit al hebben gedaan.
echo 'bitje van 3 (0011) zit ook in 3 (0011).';
}
if(3 & 4){
echo 'bitje van 3 (0011) zit -niet- in 4 (0100)';
}
?>
>> Je kan toch met exceptions werken en dan bijv. foutcodes meesturen?
Dat kan, je kan ook met arrays werken je kan van alles doen en laten wat je zelf wilt.
Maar is het snel? Een heel object aan te maken om alleen maar een getal terug te sturen? Wat ik hier doe is op bit niveau, geen objecten gewoon getallen.
Persoonlijk gebruik ik alleen exceptions op kritische fouten en niet voor return values.
Hoewel je nu het praktische nut er misschien niet van ziet, bekijk dit scenario.
ik verstuur een mail naar de inbox van een gebruiker en heb de volgende kolommen in de database. (id, message, status) de status staat standaard op 0.
Nu print ik deze mail uit in een tabel en kijk op status.
Status: 0 = unread, 1 = read, 2 replyed, 4 trashed
Als de gebruiker het bericht opent, zet ik de status op +1.
Wanneer de gebruiker antwoord geeft op het bericht, zet ik de status op +2 (3)
Wanneer de gebruiker het verwijderd, gaat de status naar +4 (7) in dit geval.
Met bitwise operators kan je heel makkelijk kijken wat de status is.
<?php
const UNREAD = 0;
const READ = 1;
const REPLYED = 2;
const TRASHED = 4;
const READ_AND_TRASHED = TRASHED + READ;
if($status & UNREAD ){ //unread }
if($status & READ ){ //read }
if($status & REPLYED ){ //replyed }
if($status & 3 ){ //read and replyed }
if($status & (READ + REPLYED) ){ //read and replyed }
if($status & TRASHED ){ //unread and trashed }
if($status & READ_AND_TRASHED ){ //read and trashed }
if($status & 6 ){ //replyed and trashed }
if($status & 7 ){ //read, replyed and trashed }
// Je kan met 0, 1, 2, 4 een maximale waarde maken van 7.
?>