Sanitize user input to avoid XSS attacks Question

I have username and password for login and want to avoid the cross site scripting. I read that using htmlspecialchars() is the way to go but not sure what to do after that. Any ideas?
937 Replies
MD
MDOP10mo ago
current code so far
<?php

function sanitizeLogin(string $username, string $pass): string {
$xssCheckPartOne = htmlspecialchars($username);
$xssCheckPartTwo = htmlspecialchars($pass);

}
<?php

function sanitizeLogin(string $username, string $pass): string {
$xssCheckPartOne = htmlspecialchars($username);
$xssCheckPartTwo = htmlspecialchars($pass);

}
Jochem
Jochem10mo ago
use prepared statements
MD
MDOP10mo ago
how's that work?
Jochem
Jochem10mo ago
https://www.php.net/manual/en/pdo.prepare.php you feed the raw string to PDO and PDO makes sure it's safe for the database no matter what also, you don't need to sanitize the password because you're hashing it anyway (you are hashing it, right?)
MD
MDOP10mo ago
im using the password_hash() function but i know there's an argon option which I dont know how to use is what im doing not a good thing?
Jochem
Jochem10mo ago
yeah, you can use password_hash. Just hash the raw value from the user, and store the hash in the database hashing will guarantee a database safe string, but still use prepared statements to insert things. You should always use prepared statements rather than trying to sanitize your own data
MD
MDOP10mo ago
my goal was sanitize input for errors and report it so user can't proceed if they try to do anything stupid with xss
Jochem
Jochem10mo ago
best security practice is to not notify people doing stupid things with xss basically, you just silently sanitize and use the sanitized input
MD
MDOP10mo ago
hmm so me making a sanitize.php file is kinda a waste?
Jochem
Jochem10mo ago
yeah, honestly anything you do for security, you want to try to hide from the user as much as possible you technically don't even want to tell them if the username or password is the one that's wrong, that lets people try to find usernames by just flooding your login and seeing if it returns "user unknown" or "password incorrect" trust that PHP's standards are good enough to protect you, use prepared statements and password_hash
MD
MDOP10mo ago
see I made this form checker file to use functions from in register and login
<?php

function validateLoginFields($username, $pass) : bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

if(!filter_var($username, FILTER_VALIDATE_EMAIL)) {
$error = "Enter a valid username using your email";
echo $error;
return false;
}

return true;
}

function validateRegisterFields($username, $email, $pass, $validatepassword): bool {
if((!$username) || (!$email) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = "Enter a valid email address";
echo $error;
return false;
}

if(strlen($pass) > 25 || strlen($pass) < 8) {
$error = "Password must be between 8 to 25 characters";
echo $error;
return false;
}

if(strlen($validatepassword) > 25 || strlen($validatepassword) < 8) {
$error = "Validate Password must be between 8 to 25 characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}
<?php

function validateLoginFields($username, $pass) : bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

if(!filter_var($username, FILTER_VALIDATE_EMAIL)) {
$error = "Enter a valid username using your email";
echo $error;
return false;
}

return true;
}

function validateRegisterFields($username, $email, $pass, $validatepassword): bool {
if((!$username) || (!$email) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error = "Enter a valid email address";
echo $error;
return false;
}

if(strlen($pass) > 25 || strlen($pass) < 8) {
$error = "Password must be between 8 to 25 characters";
echo $error;
return false;
}

if(strlen($validatepassword) > 25 || strlen($validatepassword) < 8) {
$error = "Validate Password must be between 8 to 25 characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}
now im just trying to make sure xss crap is avoided
Jochem
Jochem10mo ago
see, that's fine. None of that will leak info (though upper character limits for passwords are pointless) Just don't manually try to sanitize your user's inputs, trust PHP. That way, when there's a vulnerability, you just update PHP and that's that
MD
MDOP10mo ago
ok so if I use prepare, how to handle htmlspecialchars?
Jochem
Jochem10mo ago
on display or, alternatively, when you store them in addition to the prepared statements, but only for things you might conceivably display. Never for the password htmlspecialchars won't take care of even the most basic SQL Injection attacks
MD
MDOP10mo ago
what does?
Jochem
Jochem10mo ago
prepared statements they're made to prevent SQL injection htmlspecialchars will help mitigate XSS but yeah, never mess with the user's password, it's just going to cause issues and you shouldn't ever store or display the user's password anyway, not unhashed
MD
MDOP10mo ago
what if they put xss attack in password field?
Jochem
Jochem10mo ago
how would that ever trigger? xss is a worry if the value is displayed to a different user once an attacker has submitted their XSS attack in the password field, your code should hash it as is and that destroys the payload.
ἔρως
ἔρως10mo ago
you're doing some things in a very very wrong way 1- you're sanitizing emails - just validate and gtfo if it doesn't pass as an email, it isn't an email and no sanitization will save it if it is an email, you should accept the input you can sanitize the email after, but it should be safe
Jochem
Jochem10mo ago
(I'd argue you should still sanitize before display though
ἔρως
ἔρως10mo ago
2- you're limiting the maximum length of your password DO NOT DO THAT
MD
MDOP10mo ago
i dumped the email for sign and changed the maxium password
ἔρως
ἔρως10mo ago
set it to 256 characters or something, if you really must 30 characters isn't safe enough, sometimes 3- you're not trimming whitespaces people are dumb and copy stuff, so, trim whitespace
Jochem
Jochem10mo ago
72 bytes is the limit for bcrypt, but there's no reason to limit the password at all
ἔρως
ἔρως10mo ago
PASSWORD_DEFAULT doesn't and the argon2 ones don't as well just bcrypt
MD
MDOP10mo ago
ok i got some stff started
<?php
require __DIR__ . '../../../vendor/autoload.php';
include "../../class/helpers/formcheck.php";
include "../../class/dbhelper/SQLConnection.php";

function postInput($htmlName) {
if(isset($_POST[$htmlName])) {
return $_POST[$htmlName];
}

return null;
}

$username = postInput('username');
$pass = postInput('password');
$validatePass = postInput('validatepassword');

if(isset($_POST['submit'])) {
$hashedpassword = password_hash($pass, PASSWORD_BCRYPT);

if(validateRegisterFields($username, $pass, $validatePass)) {

}
}
<?php
require __DIR__ . '../../../vendor/autoload.php';
include "../../class/helpers/formcheck.php";
include "../../class/dbhelper/SQLConnection.php";

function postInput($htmlName) {
if(isset($_POST[$htmlName])) {
return $_POST[$htmlName];
}

return null;
}

$username = postInput('username');
$pass = postInput('password');
$validatePass = postInput('validatepassword');

if(isset($_POST['submit'])) {
$hashedpassword = password_hash($pass, PASSWORD_BCRYPT);

if(validateRegisterFields($username, $pass, $validatePass)) {

}
}
ἔρως
ἔρως10mo ago
require __DIR__ . '../../../vendor/autoload.php'; <-- this is scary
Jochem
Jochem10mo ago
use PASSWORD_DEFAULT insteaf of specifying a specific algo
ἔρως
ἔρως10mo ago
include "../../class/helpers/formcheck.php";
include "../../class/dbhelper/SQLConnection.php";
include "../../class/helpers/formcheck.php";
include "../../class/dbhelper/SQLConnection.php";
instead of doing this, just use the composer psr4 loader you were using before
MD
MDOP10mo ago
changed
ἔρως
ἔρως10mo ago
you have a bug if(isset($_POST[$htmlName])) <-- this will give you false positives use !empty() instead wait oh boy ... i got what that function does and it's completely useless
MD
MDOP10mo ago
im very rusty at php how i include them?
ἔρως
ἔρως10mo ago
instead of using that function, use ?? null or something
MD
MDOP10mo ago
"MD<insert name here>"
ἔρως
ἔρως10mo ago
use MD/helpers/formcheck capitalization is important you have a loader, so, all you have to do is just use the classes you don't have to include the files, as they are automatically included for you
MD
MDOP10mo ago
php is complaining Undefined function 'validateRegisterFields'.intelephense(P1010)
ἔρως
ἔρως10mo ago
then you did something wrong
MD
MDOP10mo ago
<?php
include '../../../vendor/autoload.php';
include "MD/helpers/formcheck";
include "MD/dbhelper/SQLConnection";

function postInput($htmlName) {
if(isset($_POST[$htmlName])) {
return $_POST[$htmlName];
}

return null;
}

$username = postInput('username');
$pass = postInput('password');
$validatePass = postInput('validatepassword');

if(isset($_POST['submit'])) {
$hashedpassword = password_hash($pass, PASSWORD_DEFAULT);

if(validateRegisterFields($username, $pass, $validatePass)) {

}
}


?>
<?php
include '../../../vendor/autoload.php';
include "MD/helpers/formcheck";
include "MD/dbhelper/SQLConnection";

function postInput($htmlName) {
if(isset($_POST[$htmlName])) {
return $_POST[$htmlName];
}

return null;
}

$username = postInput('username');
$pass = postInput('password');
$validatePass = postInput('validatepassword');

if(isset($_POST['submit'])) {
$hashedpassword = password_hash($pass, PASSWORD_DEFAULT);

if(validateRegisterFields($username, $pass, $validatePass)) {

}
}


?>
ἔρως
ἔρως10mo ago
not include use the psr4 loader does all the work for you
MD
MDOP10mo ago
include '../../../vendor/autoload.php';
use MD/helpers/formcheck;
use MD/dbhelper/SQLConnection;
include '../../../vendor/autoload.php';
use MD/helpers/formcheck;
use MD/dbhelper/SQLConnection;
complains syntax error
ἔρως
ἔρως10mo ago
what does it say? probably wrong slashes
MD
MDOP10mo ago
syntax error, unexpected token "/", expecting "," or ";"
ἔρως
ἔρως10mo ago
yeah, told you the wrong slashes change then for the use
MD
MDOP10mo ago
ah it complains that I setup wrong because I dont have class in formcheck.php
<?php

namespace MD\helpers;

function validateLoginFields($username, $pass) : bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}
<?php

namespace MD\helpers;

function validateLoginFields($username, $pass) : bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}
ἔρως
ἔρως10mo ago
then do it make it all into a class make it a generic validation class and make one for sanitization
MD
MDOP10mo ago
<?php

namespace MD\helpers;

class Checker {



function validateLoginFields($username, $pass) : bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}

}
<?php

namespace MD\helpers;

class Checker {



function validateLoginFields($username, $pass) : bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}

}
` just complains I dont have a defined type this is getting confusing
ἔρως
ἔρως10mo ago
that's because you're doing too much here's what i would do: 1- make a class to validate strings 2- validate strings 3- get the errors, if there's any now, you do you, but, i would put those 2 functions as 2 static methods in a class
MD
MDOP10mo ago
im just struggling been a while since I had to think this way ive never done oop php just php before oop
ἔρως
ἔρως10mo ago
this isn't even the hardest part but why don't you make a generic class to validate stuff?
MD
MDOP10mo ago
because I never thought about it
ἔρως
ἔρως10mo ago
the idea of classes is to make stuff re-usable and separate responsibilities
MD
MDOP10mo ago
i could make a class have an array of input i guess since there's different fields between both pages
ἔρως
ἔρως10mo ago
or, make something generic then you can validate anything
MD
MDOP10mo ago
yeah im not sure how to do that in php
ἔρως
ἔρως10mo ago
and since it is a custom class for you, you can add specific validations how about a class that you can pass a value, and it validates if the value is something and if that something is what you expect
MD
MDOP10mo ago
class Checker {

public static function validateLoginFields($username, $pass): bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

public static function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}

}
class Checker {

public static function validateLoginFields($username, $pass): bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

public static function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}

}
ἔρως
ἔρως10mo ago
that's really not generic at all, but it's a move forward
MD
MDOP10mo ago
how would you do it? i could just split it up into validating things separately
ἔρως
ἔρως10mo ago
a class that has a function that accepts a value and an array of what you want to validate and that array could be like ['email', 'required'] or ['string', 'maxlen' => 50, 'minlen' => 10, 'regex' => '@^[a-z]+$@i'] then the function validates if it is a string, the length, and the regex, or the email and if it exists or just a method for everything, and you use chaining to get you the result something like Validate::post('key')->email()->required()->valid(), and it returns either true or an array with errors
MD
MDOP10mo ago
PHP
PHP Tutorial
PHP sanitize() Input Function
In this tutorial, you'll learn to develop a reusable PHP sanitize() function to sanitize inputs.
ἔρως
ἔρως10mo ago
or just sprinkle that all over that's valid as well but don't use sanitize, use filter_var
MD
MDOP10mo ago
ok im just lost this all I got so far
ἔρως
ἔρως10mo ago
too many decisions, i know
MD
MDOP10mo ago
<?php

namespace MD\helpers;

class Checker {

private $input;
private $validateArray;

public function __construct($input)
{
$this->input = $input;
}



public static function validateLoginFields($username, $pass): bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

public static function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}

}
<?php

namespace MD\helpers;

class Checker {

private $input;
private $validateArray;

public function __construct($input)
{
$this->input = $input;
}



public static function validateLoginFields($username, $pass): bool {
if((!$username) || (!$pass))
{
$error = "Please fill in all fields for processing the login";
echo $error;
return false;
}

return true;
}

public static function validateRegisterFields($username, $pass, $validatepassword): bool {
if((!$username) || (!$pass) || (!$validatepassword)) {
$error = "Please leave no fields blank";
echo $error;
return false;
}

if(strlen($pass) < 8) {
$error = "Password must be 8 characters or more";
echo $error;
return false;
}

if(strlen($validatepassword) < 8) {
$error = "Validate Password must be 8 or more characters";
echo $error;
return false;
}

if($pass != $validatepassword) {
$error = "Passwords must match";
echo $error;
return false;
}

return true;
}

}
ἔρως
ἔρως10mo ago
it's fine, you can leave as it was and just use it it's not the best, or optimal, but if it works then it works
MD
MDOP10mo ago
well if you want to you can share better code so I can learn
ἔρως
ἔρως10mo ago
i wouldn't say i could write the best, but different i have my style, and i don't think my style is the best for you
MD
MDOP10mo ago
well if your idea is more optimal I should learn it too so next project I can improve i mean php isnt the same as it was in 2012 so im having to relearn it
ἔρως
ἔρως10mo ago
that's alright, i will come up with something
MD
MDOP10mo ago
Thanks Tbh my day is now a mess so any more confusion will get me nowhere
ἔρως
ἔρως10mo ago
i have a simpler idea for you write a class to validate the email, the password and the username (if you have one) should be a lot easier i was thinking too overcomplicated
MD
MDOP10mo ago
a class for each? or just what i already have?
ἔρως
ἔρως10mo ago
no, for all
MD
MDOP10mo ago
i made separate files for each validateusername.php and validatepassword.php
ἔρως
ἔρως10mo ago
class Validate {
static function email($input, $required = true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Email is required';
}

if($input && !$errors && !filter_var($input, ...)) {
$errors[] = 'Email is invalid';
}

return $errors ?: true;
}

...
}
class Validate {
static function email($input, $required = true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Email is required';
}

if($input && !$errors && !filter_var($input, ...)) {
$errors[] = 'Email is invalid';
}

return $errors ?: true;
}

...
}
something like this
MD
MDOP10mo ago
I dropped email
ἔρως
ἔρως10mo ago
you get the gist
MD
MDOP10mo ago
2nd if has an error ... says "expression expected"
ἔρως
ἔρως10mo ago
yeah, because it's pseudocode
MD
MDOP10mo ago
since FILTER_SANITIZE_STRING is deprecated what are my options for a username?
ἔρως
ἔρως10mo ago
none, use htmlentities with an encoding or leave it null to use utf8 (default)
MD
MDOP10mo ago
for password what about ENT stuff? ENT_IGNORE? DISALLOWED, ?
ἔρως
ἔρως10mo ago
try it
MD
MDOP10mo ago
i just did whatever i came up with this
<?php

namespace MD\helpers;

class Validate {
static function username($input, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}

static function password($input, $required=true) {
$errors = [];
if(!$input && $required) {
$errors[] = 'Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Password must 8 or more characters';
}
}

static function valdiatePassword($input, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Validated Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Validated Password must be 8 or more characters';
}
}
}
<?php

namespace MD\helpers;

class Validate {
static function username($input, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}

static function password($input, $required=true) {
$errors = [];
if(!$input && $required) {
$errors[] = 'Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Password must 8 or more characters';
}
}

static function valdiatePassword($input, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Validated Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Validated Password must be 8 or more characters';
}
}
}
here's register.php
<?php
include '../../../vendor/autoload.php';
use MD\dbhelper\SQLConnection;
use MD\helpers\Validate;

$username = $_POST['username'];
$pass = $_POST['password'];
$validatePass = $_POST['validatepassword'];
$required = true;

if(isset($_POST['submit'])) {
// check the user input before database connection



}


?>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
<?php
include '../../../vendor/autoload.php';
use MD\dbhelper\SQLConnection;
use MD\helpers\Validate;

$username = $_POST['username'];
$pass = $_POST['password'];
$validatePass = $_POST['validatepassword'];
$required = true;

if(isset($_POST['submit'])) {
// check the user input before database connection



}


?>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
im thinking the validate class is brought in and since it has two return types... maybe I need two
MD
MDOP10mo ago
MD
MDOP10mo ago
updated register.php
ἔρως
ἔρως10mo ago
does this work?
MD
MDOP10mo ago
So tomorrow how should I display these $errors[]
ἔρως
ἔρως10mo ago
that's the fun part: any way you want if you use ajax, send it over ajax if you only have php, make sure the errors are accessible where you render the form
MD
MDOP10mo ago
That's the part I'm not sure about Since it's an array, I'm not sure how to display the proper messages in my html
MD
MDOP10mo ago
GitHub
PHPTodoApp/app/views/register/register.php at main · MD-2016/PHPTod...
practice project to get back into php. Contribute to MD-2016/PHPTodoApp development by creating an account on GitHub.
MD
MDOP10mo ago
That's what I got so far
ἔρως
ἔρως10mo ago
how are you passing the into to the file?
MD
MDOP10mo ago
Well the validation happens when the user hits submit When isset is triggered for submit button
ἔρως
ἔρως10mo ago
does javascript send it? or is it a normal post?
MD
MDOP10mo ago
Normal Post I didn't know how to do Js sending it
ἔρως
ἔρως10mo ago
then, where the form is, just render the messages how you render, varies based on the contents of the errors variable you can output it on the top of the form you can store the errors on an array with the key based on the name, and then for each field you output a message it's, almost literally, "loop and print"
MD
MDOP10mo ago
So an associate array
ἔρως
ἔρως10mo ago
if you want you're asking about something that is very "do what you want" because there's no "right" way to do it just better or worse
MD
MDOP10mo ago
Then for each loop in the html above the fields and top of form
ἔρως
ἔρως10mo ago
something like that, yes
MD
MDOP10mo ago
Ok I'll have to try that tomorrow
ἔρως
ἔρως10mo ago
i can't give you a definitive answer because there isn't one but what you said is a good way to do it it isn't the best because there isn't one you code could use TONS of improvements
MD
MDOP10mo ago
Yeah since I haven't used any js it made me wonder if it was more appropriate for form checking and only having php handle the requests
ἔρως
ἔρως10mo ago
it's fine, both are fine, both work
MD
MDOP10mo ago
Yeah my code is sloppy 😦
ἔρως
ἔρως10mo ago
it's not sloppy just can be a lot better
MD
MDOP10mo ago
What improvements are you suggesting?
ἔρως
ἔρως10mo ago
re-doing everything from line 6 to 30
$username = $_POST['username'];
$pass = $_POST['password'];
$validatePass = $_POST['validatepassword'];
$username = $_POST['username'];
$pass = $_POST['password'];
$validatePass = $_POST['validatepassword'];
^ this doesn't do anything and may actually cause issues because it throws warnings
$validatedUsername = false;
$validatedPassword = false;
$validatedValidPass = false;
$validatedUsername = false;
$validatedPassword = false;
$validatedValidPass = false;
^ you don't need this: if $errors is empty it means a success
$validatedUsername | $errors = Validate::username($username, $required);
$validatedPassword | $errors = Validate::password($pass, $required);
$validatedValidPass | $errors = Validate::valdiatePassword($validatePass, $pass, $required);
$validatedUsername | $errors = Validate::username($username, $required);
$validatedPassword | $errors = Validate::password($pass, $required);
$validatedValidPass | $errors = Validate::valdiatePassword($validatePass, $pass, $required);
^ this is a loop but you're doing it manually
MD
MDOP10mo ago
I wasn't sure how to handle the $errors ?: true situation with this code
ἔρως
ἔρως10mo ago
the what???
MD
MDOP10mo ago
GitHub
PHPTodoApp/app/class/helpers/formcheck.php at main · MD-2016/PHPTod...
practice project to get back into php. Contribute to MD-2016/PHPTodoApp development by creating an account on GitHub.
ἔρως
ἔρως10mo ago
oh, that im just saying that you're doing what a loop does, but you're doing it manually
MD
MDOP10mo ago
How do I do it in a loop since the return was ?:
ἔρως
ἔρως10mo ago
what does that have to do with anything?
MD
MDOP10mo ago
I'm bringing those functions From formcheck
ἔρως
ἔρως10mo ago
what's the problem with ?:?
MD
MDOP10mo ago
That's a ternary return meaning it could be a string or boolean?
ἔρως
ἔρως10mo ago
yes, and? what's confusing about that?
MD
MDOP10mo ago
Never seen it before so I wasn't sure how to handle it with using it in my register page
ἔρως
ἔρως10mo ago
you handle it is pretending it doesn't exist it's an implementation detail of the function just set the correct return types and you're good to go
MD
MDOP10mo ago
So the correct return type is a boolean?
ἔρως
ἔρως10mo ago
no the correct type is an array OR a boolean array|bool 8.x lets you define multiple types
MD
MDOP10mo ago
Ok that's kinda new
ἔρως
ἔρως10mo ago
it's from 8.0
MD
MDOP10mo ago
Yeah I'm reading
ἔρως
ἔρως10mo ago
you were overthinking
MD
MDOP10mo ago
so i deleted the code from line 6 just kept the errors string array and required boolean
ἔρως
ἔρως10mo ago
does it work?
MD
MDOP10mo ago
i havent done anything in if(isset($_POST['submit'])) { // check the user input before database connection // code here for success and start database connection and prepared statement }
ἔρως
ἔρως10mo ago
that's 80% of your work done planning
MD
MDOP10mo ago
yeah just thinking about how to do that loop since I need to check each input, display the proper error message, and call the right functions to validate from the formcheck.php
ἔρως
ἔρως10mo ago
associative arrays help a lot
MD
MDOP10mo ago
maybe an asosciate array with the form element name mapping to the error message? thing is there is more than one for each
ἔρως
ἔρως10mo ago
yup there exist this one amazing thing called "array" where you can just shove stuff into it, automatically, in numeric order
MD
MDOP10mo ago
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}
ἔρως
ἔρως10mo ago
yeah, you may have 2 errors that's fine
MD
MDOP10mo ago
but my form name can only map to one of them in an array
ἔρως
ἔρως10mo ago
you know you can put arrays in arrays?
MD
MDOP10mo ago
so a form element name pointing to an array of error messages?
ἔρως
ἔρως10mo ago
im trying really hard not to tell you what to do, but hinting on a way to do it yes
MD
MDOP10mo ago
$inputArray = array("username" => ['Username is required', 'Username is invalid'], "password" => ['Password is required', 'Password must 8 or more characters'], "validatepassword" => ['Validated Password is required', 'Validated Password must be 8 or more characters', 'Passwords dont match']);
$inputArray = array("username" => ['Username is required', 'Username is invalid'], "password" => ['Password is required', 'Password must 8 or more characters'], "validatepassword" => ['Validated Password is required', 'Validated Password must be 8 or more characters', 'Passwords dont match']);
ἔρως
ἔρως10mo ago
that would be thje final result, if there's errors
MD
MDOP10mo ago
im just a little confused on putting this all together I know there's the validation functions I need to call from formcheck.php and they return array | boolean, populate the $errors[] as needed, process the POST stuff with these 3 variables
$required = true;
$errors = [];
$inputArray = array("username" => ['Username is required', 'Username is invalid'], "password" => ['Password is required', 'Password must 8 or more characters'], "validatepassword" => ['Validated Password is required', 'Validated Password must be 8 or more characters', 'Passwords dont match']);
$required = true;
$errors = [];
$inputArray = array("username" => ['Username is required', 'Username is invalid'], "password" => ['Password is required', 'Password must 8 or more characters'], "validatepassword" => ['Validated Password is required', 'Validated Password must be 8 or more characters', 'Passwords dont match']);
ἔρως
ἔρως10mo ago
what's that $inputarray?
MD
MDOP10mo ago
idk i just made it based on previous discussion yes the "final result"
ἔρως
ἔρως10mo ago
why?
MD
MDOP10mo ago
$validateUsername | $errors = Validate::username($_POST['username'], $required);
$validatePass | $errors = Validate::password($_POST['password'], $required);
$validatePassBoth | $errors = Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required);
$validateUsername | $errors = Validate::username($_POST['username'], $required);
$validatePass | $errors = Validate::password($_POST['password'], $required);
$validatePassBoth | $errors = Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required);
sorry just a little lost with this setup
ἔρως
ἔρως10mo ago
im trying to understand how you are lost
MD
MDOP10mo ago
that's the above code I got
ἔρως
ἔρως10mo ago
what are you trying to do? can you describe it to me, step by step? tell me the algorithm
MD
MDOP10mo ago
- user hits submit - validate the username, password, validatepassword fields - check if their good (true) or display an appropriate error message
- stop on errors from processing above input - only authorize connection to database if input passes
ἔρως
ἔρως10mo ago
in a list form, please? im a stupid robot, and you need to explain to me, step by step, what you want the code to do define "user input"
MD
MDOP10mo ago
username, password, validatepassword
ἔρως
ἔρως10mo ago
add it to the list
MD
MDOP10mo ago
updated
ἔρως
ἔρως10mo ago
what stops on errors?
MD
MDOP10mo ago
processing input
ἔρως
ἔρως10mo ago
add it to the list
MD
MDOP10mo ago
well I got the user hitting submit check with if(isset($_POST['submit'))
ἔρως
ἔρως10mo ago
yes, but don't talk about implementation details if you aren't sure of what you're implementing first, know what you're implementing define all steps and how it will be processed then, you write the code
MD
MDOP10mo ago
i pretty much defined all the steps
ἔρως
ἔρως10mo ago
so, if the email is empty, it stops checking that the password is empty too?
MD
MDOP10mo ago
if(isset($_POST['submit'])) {
// if the validation is successful
if($validateUsername && $validatePass && $validatePassBoth)
{

}
else {
// handling errors
}
if(isset($_POST['submit'])) {
// if the validation is successful
if($validateUsername && $validatePass && $validatePassBoth)
{

}
else {
// handling errors
}
ἔρως
ἔρως10mo ago
the list is wrong then it says it stops on errors but it doesnt it validates all inputs and then decides on what it does
MD
MDOP10mo ago
well either this or I do each one individually
ἔρως
ἔρως10mo ago
as you should do you know how to make a flow diagram?
MD
MDOP10mo ago
been a long time since ive done programming logic and design that was like 2009
ἔρως
ἔρως10mo ago
you should make a flow diagram
MD
MDOP10mo ago
if($validateUsername) {

}

if($validatePass) {

}

if($validatePassBoth) {

}
if($validateUsername) {

}

if($validatePass) {

}

if($validatePassBoth) {

}
ἔρως
ἔρως10mo ago
i havent done one since 2009 either
MD
MDOP10mo ago
that means more studying and I need to get into a job by summer or it's over for me
ἔρως
ἔρως10mo ago
i know this sounds like im wasting your time, but you're implementing badly defined features, and you end up with messy undebuggable code that not even a mother would love
MD
MDOP10mo ago
maybe im just not meant to be a programmer
ἔρως
ἔρως10mo ago
open excalidraw and make a flor diagram
MD
MDOP10mo ago
my code never been that great anyway it just worked for school
ἔρως
ἔρως10mo ago
dude, listen to me your code isn't "not good" not because of lack of knowledge you're just waffling about, trying to blindly implement something but you don't have defined what you will implement
MD
MDOP10mo ago
does anyone in this server do that?
ἔρως
ἔρως10mo ago
do what?
MD
MDOP10mo ago
flow diagrams and such most just write code
ἔρως
ἔρως10mo ago
and they write amazing code and never ask for help, right?
MD
MDOP10mo ago
idk cdl and I just burned out I guess
ἔρως
ἔρως10mo ago
you think im not tired?
MD
MDOP10mo ago
idk if they write amazing code 😄
ἔρως
ἔρως10mo ago
ive slept a total of a little above 15 hours these last 3 days
MD
MDOP10mo ago
you should rest then 🙂
ἔρως
ἔρως10mo ago
i've been extremely tired all month ive been working 9-10 hours almost everyday what im telling you to do is to stop stop, organize your head, draw what you are thinking on how it will be implemented, then write the code right now, you're just doing the equivalent of throwing pasta to a wall and see if it sticks so, stop, put your ideas into something visual, then do what you need to do
MD
MDOP10mo ago
i dont like drawing those diagrams and never did them correctly i mostly just listed this stuff here's what I came up with thinking back to what I remember that's everything it should do but the setup is different so it's a bit harder to see the implementation
ἔρως
ἔρως10mo ago
step 5 is useless you don't "check for xss input" you just output what you need by making it into a safe html string
MD
MDOP10mo ago
sorry mom was bugging about adware on youtube talking about "work from home" jobs for companies that dont even hire for those roles im back now
ἔρως
ἔρως10mo ago
it's fine by the way, you forgot the part where you show the errors
MD
MDOP10mo ago
"Report error" is showing the errors
ἔρως
ἔρως10mo ago
how? where's the step where the errors will be shown? will you show the error right after validating? will you show the errors all together at the top of the form? will you show the errors by input?
MD
MDOP10mo ago
updated
ἔρως
ἔρως10mo ago
- display the error message to the user <-- how? where? when?
MD
MDOP10mo ago
ἔρως
ἔρως10mo ago
yes, i read the list, but my question is still unanswered which is an important step
MD
MDOP10mo ago
i cant edit it anymore too big for discord
ἔρως
ἔρως10mo ago
it's fine, just say it here
MD
MDOP10mo ago
- display error message from errors array to the user on top of form
ἔρως
ἔρως10mo ago
the classic way implement this for 1 input
MD
MDOP10mo ago
been a long time since ive done logic and design apparently it's killed me not keeping up with it
ἔρως
ἔρως10mo ago
i can see it, which is why i have to force you to re-think about it
MD
MDOP10mo ago
along with data structures and algorithms
ἔρως
ἔρως10mo ago
but, go ahead and implement this for the email input
MD
MDOP10mo ago
i dont use email took that out just username for this small project
ἔρως
ἔρως10mo ago
pick 1 field and validate it username, validate it AND ONLY THAT ONE
MD
MDOP10mo ago
ok I have the idea but we have an issue we have this
$validateUsername | $errors = Validate::username($_POST['username'], $required);
$validateUsername | $errors = Validate::username($_POST['username'], $required);
to start with which is boolean | array
ἔρως
ἔρως10mo ago
okay, and the issue is?
MD
MDOP10mo ago
so that function is going to check for empty and then add the error to $errors
ἔρως
ἔρως10mo ago
no, what it is doing is outputting a boolean or an array
MD
MDOP10mo ago
otherwise return true
<?php

namespace MD\helpers;

class Validate {
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}

static function password($input, $required=true) {
$errors = [];
if(empty($input) && $required) {
$errors[] = 'Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Password must 8 or more characters';
}

return $errors ?: true;
}

static function valdiatePassword($input, $pass2, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Validated Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Validated Password must be 8 or more characters';
}

if(strcmp($input, $pass2) != 0) {
$errors[] = 'Passwords dont match';
}

return $errors ?: true;
}
}
<?php

namespace MD\helpers;

class Validate {
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}

static function password($input, $required=true) {
$errors = [];
if(empty($input) && $required) {
$errors[] = 'Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Password must 8 or more characters';
}

return $errors ?: true;
}

static function valdiatePassword($input, $pass2, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Validated Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Validated Password must be 8 or more characters';
}

if(strcmp($input, $pass2) != 0) {
$errors[] = 'Passwords dont match';
}

return $errors ?: true;
}
}
ἔρως
ἔρως10mo ago
but, again, what is the issue? you have a boolean, you have an array
MD
MDOP10mo ago
I guess check if the boolean is false?
ἔρως
ἔρως10mo ago
the function outputs true or an array it will never be false
MD
MDOP10mo ago
yeah this is confusing
ἔρως
ἔρως10mo ago
it's not true = no errors, array = errors
MD
MDOP10mo ago
so what am i saying
if($validateUsername) {}
if($validateUsername) {}
or
if(strlen($errors) != 0) {}
if(strlen($errors) != 0) {}
just weird trying to tie this all together
ἔρως
ἔρως10mo ago
i see where the confusion stems from you have 2 return types, and you think you need 2 variables for it but no, you don't
MD
MDOP10mo ago
yes the confusion is how this is going into register.php since formcheck.php handles some of the checking
ἔρως
ἔρως10mo ago
$validateUsername | $errors <-- this is a bitwise or
MD
MDOP10mo ago
the logic makes sense on paper but trying to tie together with the current implemntation details isn't making sense ok but im not seeing how to handle it for each input
ἔρως
ἔρως10mo ago
focus on 1 input
MD
MDOP10mo ago
this is why I understood the code I started with all these "updated changes" made it more confusing I just know older php this new stuff is just making it into Java
ἔρως
ἔρως10mo ago
this has nothing to do with "new" or "old" php you would have to write the same code in 7.x, 5.6 or even 5.3
MD
MDOP10mo ago
the code I started with before we made a lot of changes is what I understood since you said it was "bad" ok so I need to do a bitwise or check in the if?
ἔρως
ἔρως10mo ago
no
MD
MDOP10mo ago
since the return type is bitwise or I wont know what im getting so id have to check for both somehow
ἔρως
ἔρως10mo ago
it's not bitwise you are using bitwise operations on the result value, which after testing, i verified that it doesn't work
MD
MDOP10mo ago
so do I need to change the result on formcheck.php? does this need changed then?
ἔρως
ἔρως10mo ago
you have 2 options: 1- keep as is, and check if it is a boolean or an array 2- change the name of something to indicate that it doesn't have errors, and change the return value to false
MD
MDOP10mo ago
that's the issue was checking if it's a boolean or array that's where im stuck
ἔρως
ἔρως10mo ago
return false then
MD
MDOP10mo ago
because I have to check it for each
ἔρως
ἔρως10mo ago
or just return an empty array and document that it returns an array of errors
MD
MDOP10mo ago
let's just start here
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors ?: true;
}
so we know the return is array or boolean this means we check for either an empty array or boolean? does php have functions for that?
ἔρως
ἔρως10mo ago
change it to return just the $errors variable it's easier to reason with
MD
MDOP10mo ago
there is an is_array and is_bool functions I can use
if(is_array($validateUsername)) {

}
if(is_array($validateUsername)) {

}
ἔρως
ἔρως10mo ago
just try what i said and lets see how it works
MD
MDOP10mo ago
so what I put is wrong?
ἔρως
ἔρως10mo ago
no im just telling you to simplify
MD
MDOP10mo ago
<?php

namespace MD\helpers;

class Validate {
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors;
}

static function password($input, $required=true) {
$errors = [];
if(empty($input) && $required) {
$errors[] = 'Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Password must 8 or more characters';
}

return $errors;
}

static function valdiatePassword($input, $pass2, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Validated Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Validated Password must be 8 or more characters';
}

if(strcmp($input, $pass2) != 0) {
$errors[] = 'Passwords dont match';
}

return $errors;
}
}
<?php

namespace MD\helpers;

class Validate {
static function username($input, $required=true) {
$errors = [];

if(empty($input) && $required) {
$errors[] = 'Username is required';
}

if($input && !$errors && !filter_var($input, htmlentities($input, ENT_DISALLOWED, "UTF-8"))) {
$errors[] = 'Username is invalid';
}

return $errors;
}

static function password($input, $required=true) {
$errors = [];
if(empty($input) && $required) {
$errors[] = 'Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Password must 8 or more characters';
}

return $errors;
}

static function valdiatePassword($input, $pass2, $required=true) {
$errors = [];

if(!$input && $required) {
$errors[] = 'Validated Password is required';
}

if(strlen($input) < 8) {
$errors[] = 'Validated Password must be 8 or more characters';
}

if(strcmp($input, $pass2) != 0) {
$errors[] = 'Passwords dont match';
}

return $errors;
}
}
ἔρως
ἔρως10mo ago
now it only returns an array
MD
MDOP10mo ago
so i would check if the array isnt empty
ἔρως
ἔρως10mo ago
yup if it isn't empty, it has errors
MD
MDOP10mo ago
i can use empty()
ἔρως
ἔρως10mo ago
yes and no you can, but for THIS VERY UNIQUE AND SPECIFIC EXAMPLE, it is overkill
MD
MDOP10mo ago
i guess normal would be for php
if (errors != ''){}
if (errors != ''){}
ἔρως
ἔρως10mo ago
that's checking if the constant error isn't set to an empty string
MD
MDOP10mo ago
ill check if count != 0
ἔρως
ἔρως10mo ago
or just use the variable, alone, in the if
MD
MDOP10mo ago
hmm? if($validateUsername){}
ἔρως
ἔρως10mo ago
yup but, you don't have to do it there just shove it all into an array, with the name of the field as the key
MD
MDOP10mo ago
ok i got the
if(!$validateUsername){}
if(!$validateUsername){}
started so now i need to get the message from the error array and add it to my error array on that page then display it
ἔρως
ἔρως10mo ago
if you do something like this, you can save A TON of work
MD
MDOP10mo ago
ok so I got an errors array already I just need to populate it with the apprioate stuff
ἔρως
ἔρως10mo ago
basically, yes, it's an option
MD
MDOP10mo ago
like this
if(!$validateUsername) {
$errors = ['username' => [$validateUsername]];
}
if(!$validateUsername) {
$errors = ['username' => [$validateUsername]];
}
ἔρως
ἔρως10mo ago
no but it's close
MD
MDOP10mo ago
darn
ἔρως
ἔρως10mo ago
it's really close
MD
MDOP10mo ago
$errors = ['username' => $valdiateUsername];
$errors = ['username' => $valdiateUsername];
ἔρως
ἔρως10mo ago
very very very close the variable is useless
MD
MDOP10mo ago
$errors['username'] = $validateUsername
ἔρως
ἔρως10mo ago
go back to the other version skip assigning that weird long ass variable
MD
MDOP10mo ago
idk all i see is this
MD
MDOP10mo ago
PHP
PHP Tutorial
PHP Associative Arrays
In this tutorial, you will learn about PHP associative arrays and how to use them effectively.
ἔρως
ἔρως10mo ago
dude, do literally what i said this time, im telling you what to do
MD
MDOP10mo ago
ok...
$errors = ['username' => ];
$errors = ['username' => ];
ἔρως
ἔρως10mo ago
now, what's missing there?
MD
MDOP10mo ago
the string from the $validateUsername
ἔρως
ἔρως10mo ago
and what supposed string comes from where?
MD
MDOP10mo ago
Validate::username()
ἔρως
ἔρως10mo ago
since you removed the variable, what do you have to do?
MD
MDOP10mo ago
$errors = ['username' => Validate::username($_POST['username'], $required)];
$errors = ['username' => Validate::username($_POST['username'], $required)];
ἔρως
ἔρως10mo ago
very good by the way, are you using vscode?
MD
MDOP10mo ago
yes
MD
MDOP10mo ago
so I need to delete that valdiateUsername
ἔρως
ἔρως10mo ago
that's the variable? yes, it's utterly useless
MD
MDOP10mo ago
ok so what do I put in the if
ἔρως
ἔρως10mo ago
install the extension first
MD
MDOP10mo ago
ive had that installed before you mentioned it
ἔρως
ἔρως10mo ago
alright right now, don't think about it show me the full code
MD
MDOP10mo ago
if() {
$errors = ['username' => Validate::username($_POST['username'], $required)];
}
if() {
$errors = ['username' => Validate::username($_POST['username'], $required)];
}
ἔρως
ἔρως10mo ago
what's that if?
MD
MDOP10mo ago
that's what's left removing the $validateUsername
ἔρως
ἔρως10mo ago
okay show me the full code
MD
MDOP10mo ago
wouldn't that be in the html
ἔρως
ἔρως10mo ago
the full php file
MD
MDOP10mo ago
<?php
include '../../../vendor/autoload.php';
use MD\dbhelper\SQLConnection;
use MD\helpers\Validate;

$required = true;
$errors = [];
$validatePass = Validate::password($_POST['password'], $required);
$validatePassBoth = Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required);
/*$inputArray = array("username" => ['Username is required', 'Username is invalid'], "password" => ['Password is required', 'Password must 8 or more characters'], "validatepassword" => ['Validated Password is required', 'Validated Password must be 8 or more characters', 'Passwords dont match']);*/
if(isset($_POST['submit'])) {
// if the validation is successful
if() {
$errors = ['username' => Validate::username($_POST['username'], $required)];

}

}

// deal with printing out errors


}


?>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
<?php
include '../../../vendor/autoload.php';
use MD\dbhelper\SQLConnection;
use MD\helpers\Validate;

$required = true;
$errors = [];
$validatePass = Validate::password($_POST['password'], $required);
$validatePassBoth = Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required);
/*$inputArray = array("username" => ['Username is required', 'Username is invalid'], "password" => ['Password is required', 'Password must 8 or more characters'], "validatepassword" => ['Validated Password is required', 'Validated Password must be 8 or more characters', 'Passwords dont match']);*/
if(isset($_POST['submit'])) {
// if the validation is successful
if() {
$errors = ['username' => Validate::username($_POST['username'], $required)];

}

}

// deal with printing out errors


}


?>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
`
ἔρως
ἔρως10mo ago
$validatePass = Validate::password($_POST['password'], $required);
$validatePassBoth = Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required);
$validatePass = Validate::password($_POST['password'], $required);
$validatePassBoth = Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required);
^ what are these for? same question ^
MD
MDOP10mo ago
ἔρως
ἔρως10mo ago
why are you re-creating the array 3 times, inside of leftover ifs?
MD
MDOP10mo ago
i have no clue im just following the logic of the steps i wrote out
ἔρως
ἔρως10mo ago
but now, think about this you have an array an array can have multiple keys correct?
MD
MDOP10mo ago
yes if associative
ἔρως
ἔρως10mo ago
then why don't you put it all into the same array? without the if
MD
MDOP10mo ago
so do it all in one if?
ἔρως
ἔρως10mo ago
no ifs it's empty for a reason: it's of no use now
MD
MDOP10mo ago
if(isset($_POST['submit'])) {
// if the validation is successful
$errors = ['username' => Validate::username($_POST['username'], $required), 'password' => Validate::password($_POST['password'], $required), 'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)];

// deal with printing out errors


}
if(isset($_POST['submit'])) {
// if the validation is successful
$errors = ['username' => Validate::username($_POST['username'], $required), 'password' => Validate::password($_POST['password'], $required), 'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)];

// deal with printing out errors


}
ἔρως
ἔρως10mo ago
very good now format is properly one array value per line
MD
MDOP10mo ago
// if the validation is successful
$errors = ['username' => Validate::username($_POST['username'], $required),
'password' => Validate::password($_POST['password'], $required),
'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)];

// deal with printing out errors
// if the validation is successful
$errors = ['username' => Validate::username($_POST['username'], $required),
'password' => Validate::password($_POST['password'], $required),
'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)];

// deal with printing out errors
ἔρως
ἔρως10mo ago
fix the indentation
MD
MDOP10mo ago
that doesnt happen on vscode it's discord
ἔρως
ἔρως10mo ago
alright now, you see what you've done?
MD
MDOP10mo ago
yeah no ifs shorted the code but this means my code in the html will need changed normally in java i would've used try catch for some error handling
ἔρως
ἔρως10mo ago
it was wrong in the first place that's an option, but you can only throw 1 exception per field, while each field may have multiple errors
MD
MDOP10mo ago
yeah next up is
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<?php echo '<span>${$errors}</span>'?>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<?php echo '<span>${$errors}</span>'?>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
ἔρως
ἔρως10mo ago
hold there, buster! you aren't done yet now, you need to do something when it doesn't have any errors, right?
MD
MDOP10mo ago
yeah i guess santize input and pass to database?
ἔρως
ἔρως10mo ago
before you even touch that, how do you know if you have any errors?
MD
MDOP10mo ago
check if (!$errors)
ἔρως
ἔρως10mo ago
but you have 3 values won't it always be a truthy value?
MD
MDOP10mo ago
check if each key has an empty value?
ἔρως
ἔρως10mo ago
and how do you do that in a concise way?
MD
MDOP10mo ago
map?
ἔρως
ἔρως10mo ago
how about filtering out the empty arrays? you won't need them, so, toss them out, right?
MD
MDOP10mo ago
or you could use array_key_exists and is_null
ἔρως
ἔρως10mo ago
the key will always exist and will never be null since you return an empty array
MD
MDOP10mo ago
right
ἔρως
ἔρως10mo ago
how about you wrap the array into a call to array_filter?
MD
MDOP10mo ago
ok so i need the array and a callback
ἔρως
ἔρως10mo ago
no, just the array read the notes
MD
MDOP10mo ago
ok default is empty()
ἔρως
ἔρως10mo ago
yup so, just $errors = array_filter([ ... ]) and you're done
MD
MDOP10mo ago
$errors = array_filter($errors);
$errors = array_filter($errors);
since it returns an array
ἔρως
ἔρως10mo ago
or, just save some lines and put it all together that's why functions accept values, instead of just variables but, if you want to be more expressive and explicit, that is perfectly valid
MD
MDOP10mo ago
if(isset($_POST['submit'])) {
// build an error array just in case
$errors = array_filter(['username' => Validate::username($_POST['username'], $required),
'password' => Validate::password($_POST['password'], $required),
'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)]);
}
if(isset($_POST['submit'])) {
// build an error array just in case
$errors = array_filter(['username' => Validate::username($_POST['username'], $required),
'password' => Validate::password($_POST['password'], $required),
'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)]);
}
ἔρως
ἔρως10mo ago
basically, yes now $errors is empty of has something in it
MD
MDOP10mo ago
<?php echo '<span>{$errors}</span>'?>
<?php echo '<span>{$errors}</span>'?>
ἔρως
ἔρως10mo ago
no it's an array you can't convert an array to strings
MD
MDOP10mo ago
so ill need to use a foreach loop
ἔρως
ἔρως10mo ago
yup but you know what would be even smarter?
MD
MDOP10mo ago
map?
ἔρως
ἔρως10mo ago
map, but would be better to show the message under the field that generated it
MD
MDOP10mo ago
that's the fun part what "field" shows
ἔρως
ἔρως10mo ago
what do you mean?
MD
MDOP10mo ago
because ill need to see what field is being displayed brb mom is getting frustrated with the tv
ἔρως
ἔρως10mo ago
you have the name of the field as a key this is what i would do i would check if the $errors[<field_name> is empty, then output an <ol>, loop over the errors - an <li></li> per error, then outside the loop just output a final </ol>
MD
MDOP10mo ago
so more than one error?
ἔρως
ἔρως10mo ago
they will be ordered numerically
MD
MDOP10mo ago
ok so I need to use a loop
ἔρως
ἔρως10mo ago
yes
MD
MDOP10mo ago
with ifs inside then?
ἔρως
ἔρως10mo ago
no no ifs
MD
MDOP10mo ago
ok this started
foreach($errors as $errorFound) {

}
foreach($errors as $errorFound) {

}
ἔρως
ἔρως10mo ago
no that's for all errors, not per field
MD
MDOP10mo ago
i see so I need to check if values of a key are empty otherwise loop
ἔρως
ἔρως10mo ago
yup
MD
MDOP10mo ago
so that will be 3 ifs or just one?
ἔρως
ἔρως10mo ago
3 ifs
MD
MDOP10mo ago
if($errors['username']-> "") {}
if($errors['username']-> "") {}
ἔρως
ἔρως10mo ago
no, it's an array, not an object and you can't use empty strings as strings for properties
MD
MDOP10mo ago
ok
ἔρως
ἔρως10mo ago
that should be easy
MD
MDOP10mo ago
COUNT_RECURSIVE?
ἔρως
ἔρως10mo ago
no
MD
MDOP10mo ago
hmm
ἔρως
ἔρως10mo ago
do you want to show all the errors at the top? or on each input?
MD
MDOP10mo ago
just inputs that fail
ἔρως
ἔρως10mo ago
for each input, check if the errors for that name are empty
MD
MDOP10mo ago
if($errors['username'] == '')
if($errors['username'] == '')
ἔρως
ἔρως10mo ago
that works by pure sheer luck
MD
MDOP10mo ago
what is the "proper way"
ἔρως
ἔρως10mo ago
it's not correct, it's not actually working properly empty check if it's not empty
MD
MDOP10mo ago
so
if(!empty($errors['username']))
if(!empty($errors['username']))
ἔρως
ἔρως10mo ago
not empty yes that
MD
MDOP10mo ago
ok so this means $errors has one or more
ἔρως
ἔρως10mo ago
yes
MD
MDOP10mo ago
so i need a loop that says
foreach($errors['username'] as $usernameError) {
// echo span with the error
}
foreach($errors['username'] as $usernameError) {
// echo span with the error
}
ἔρως
ἔρως10mo ago
as $error don't be so verbose you know it's an username error
MD
MDOP10mo ago
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<?php
if(!empty($errors['username'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}

if(!empty($errors['password'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}

if(!empty($errors['validatepassword'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}
}
?>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<?php
if(!empty($errors['username'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}

if(!empty($errors['password'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}

if(!empty($errors['validatepassword'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}
}
?>
<form action="" method="post">
<h1>Sign Up</h1>
<div>

<label for="username">Username:</label>
<input type="text" name="username" id="username">
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
`
ἔρως
ἔρως10mo ago
so close almost there
MD
MDOP10mo ago
i could have it do it inside the form and display each error above the respective field
ἔρως
ἔρως10mo ago
it's prefered to display under, so it doesn't interfeer with the label
MD
MDOP10mo ago
under the textfield?
ἔρως
ἔρως10mo ago
yup
MD
MDOP10mo ago
what's my goof
ἔρως
ἔρως10mo ago
you check for $errors['validatepassword'], then loop for $errors that's the goof
MD
MDOP10mo ago
i made it $error
ἔρως
ἔρως10mo ago
that doesn't fix the goof
MD
MDOP10mo ago
<?php
if(!empty($errors['username'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}

if(!empty($errors['password'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}

if(!empty($errors['validatepassword'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}
?>
<?php
if(!empty($errors['username'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}

if(!empty($errors['password'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}

if(!empty($errors['validatepassword'])) {
foreach ($errors as $error) {
echo '<span>{$error}</span>';
}
}
?>
ἔρως
ἔρως10mo ago
also, spans are inline you will see error 1error 2error 3
MD
MDOP10mo ago
can't have all three named $error
ἔρως
ἔρως10mo ago
and the goof is still there yes you can
MD
MDOP10mo ago
so the issue is with validatepassword
ἔρως
ἔρως10mo ago
no, all of them that one was an example
MD
MDOP10mo ago
oh
<?php
if(!empty($errors['username'])) {
foreach ($errors['username'] as $error) {
echo '<p>{$error}</p>';
}
}

if(!empty($errors['password'])) {
foreach ($errors['password'] as $error) {
echo '<p>{$error}</p>';
}
}

if(!empty($errors['validatepassword'])) {
foreach ($errors['validatepassword'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
<?php
if(!empty($errors['username'])) {
foreach ($errors['username'] as $error) {
echo '<p>{$error}</p>';
}
}

if(!empty($errors['password'])) {
foreach ($errors['password'] as $error) {
echo '<p>{$error}</p>';
}
}

if(!empty($errors['validatepassword'])) {
foreach ($errors['validatepassword'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
ἔρως
ἔρως10mo ago
very good but that will output it at the top
MD
MDOP10mo ago
ill just add php tags under each field and then check?
ἔρως
ἔρως10mo ago
yup
MD
MDOP10mo ago
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username">
<?php
if(!empty($errors['username'])) {
foreach ($errors['username'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
<?php
if(!empty($errors['password'])) {
foreach ($errors['password'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
<?php
if(!empty($errors['validatepassword'])) {
foreach ($errors['validatepassword'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username">
<?php
if(!empty($errors['username'])) {
foreach ($errors['username'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
<?php
if(!empty($errors['password'])) {
foreach ($errors['password'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
<?php
if(!empty($errors['validatepassword'])) {
foreach ($errors['validatepassword'] as $error) {
echo '<p>{$error}</p>';
}
}
?>
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
ἔρως
ἔρως10mo ago
very good that should be enough to show error messages
MD
MDOP10mo ago
yep I guess next would be sanitizing input then allowing what's good through to database
ἔρως
ἔρως10mo ago
however, that's absolutely horribly unsafe
MD
MDOP10mo ago
:/
ἔρως
ἔρως10mo ago
so, ALL error messages have to be made into proper html strings
MD
MDOP10mo ago
well after the unsafe is fixed ill have to call it a night
ἔρως
ἔρως10mo ago
echo '<p>{$error}</p>'; <-- also, this doesn't work single-quotes are NOT the same as double-quotes but, in this case, you should keep the single-quotes, and remove the string interpolation one important thing: echo accepts multiple arguments, like a function
MD
MDOP10mo ago
print better to use?
ἔρως
ἔρως10mo ago
no that's a different tool perfectly valid but way overkill
MD
MDOP10mo ago
ok sometimes it feels like Go is a bit easier than php
ἔρως
ἔρως10mo ago
it's newer
MD
MDOP10mo ago
echo "<p>" . $error . "</p>";
echo "<p>" . $error . "</p>";
ἔρως
ἔρως10mo ago
or echo '<p>', $error, '</p>'; this version makes some things a lot easier to handle
MD
MDOP10mo ago
why's that
ἔρως
ἔρως10mo ago
the arguments are clearly separated
MD
MDOP10mo ago
ive mainly seen . concat in the docs
ἔρως
ἔρως10mo ago
this isn't string concatenation
MD
MDOP10mo ago
yeah i know
ἔρως
ἔρως10mo ago
it's just multiple arguments to the echo construct
MD
MDOP10mo ago
i never seen commas in echo before it's kinda new for me
ἔρως
ἔρως10mo ago
which actually is a tiny tiny tiny tiny tiny tiny micro-optimization since it doesn't have to concatenate before outputting, it's a bit faster
MD
MDOP10mo ago
anything else I need to do before we end?
ἔρως
ἔρως10mo ago
yes you need to sanitize the output messages
MD
MDOP10mo ago
im not handling anything else outside of error checking at this point it's too late htmlentities or htmlspecialchars?
ἔρως
ἔρως10mo ago
i would use both pick one, stick to it but i would pick htmlentities to avoid issues with quotes
MD
MDOP10mo ago
ok so wrap my $error in it
ἔρως
ἔρως10mo ago
yes the default settings should be fine
MD
MDOP10mo ago
utf-8?
ἔρως
ἔρως10mo ago
^ don't touch anything else just the first argument
MD
MDOP10mo ago
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username">
<?php
if(!empty($errors['username'])) {
foreach ($errors['username'] as $error) {
echo '<p>', htmlentities($error), '</p>';
}
}
?>
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
<?php
if(!empty($errors['password'])) {
foreach ($errors['password'] as $error) {
echo '<p>', htmlentities($error), '</p>';
}
}
?>
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
<?php
if(!empty($errors['validatepassword'])) {
foreach ($errors['validatepassword'] as $error) {
echo '<p>', htmlentities($error), '</p>';
}
}
?>
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
</head>
<body>
<main>
<form action="" method="post">
<h1>Sign Up</h1>
<div>
<label for="username">Username:</label>
<input type="text" name="username" id="username">
<?php
if(!empty($errors['username'])) {
foreach ($errors['username'] as $error) {
echo '<p>', htmlentities($error), '</p>';
}
}
?>
</div>
<div>

<label for="password">Password:</label>
<input type="password" name="password" id="password">
<?php
if(!empty($errors['password'])) {
foreach ($errors['password'] as $error) {
echo '<p>', htmlentities($error), '</p>';
}
}
?>
</div>
<div>

<label for="validatepassword">Validate Password:</label>
<input type="password" name="validatepassword" id="validatepassword">
<?php
if(!empty($errors['validatepassword'])) {
foreach ($errors['validatepassword'] as $error) {
echo '<p>', htmlentities($error), '</p>';
}
}
?>
</div>
<div>
<button type="submit">Register</button>
<footer>Already a member? <a href="../login/login.php">Login Here</a></footer>
</div>
</form>
</main>
</body>
</html>
ἔρως
ἔρως10mo ago
yup that should work if it doesn't, it will wait for tomorrow but you understand what's being done?
MD
MDOP10mo ago
with that htmlentities escape any xss code
ἔρως
ἔρως10mo ago
everything yes and no, but yes
MD
MDOP10mo ago
sort of some of it is interesting but i have to see how to do the database stuff next
ἔρως
ἔρως10mo ago
spoiler alert: no sanitization will be done
MD
MDOP10mo ago
we basically made an array that is filtered with proper error messages then displayed them is the breakdown basic
ἔρως
ἔρως10mo ago
yup, that's right and each field is mapped by it's name
MD
MDOP10mo ago
I think I can get this down just wll take some time for it click if I can get that programming logic and design mindset back this will get a lot easier combined with my data structures and algorithms knowledge (if I didnt lose it too)
ἔρως
ἔρως10mo ago
and that means practice
MD
MDOP10mo ago
sadly one job im after they dont mention what tech they're using so it's probably asp.net with C# which means im screwed 😄
ἔρως
ἔρως10mo ago
c# isnt that hard
MD
MDOP10mo ago
as youve seen php is already hard for me going back to C# would be another long road
ἔρως
ἔρως10mo ago
60% of the syntax is the same
MD
MDOP10mo ago
pushed code to github
ἔρως
ἔρως10mo ago
nice
MD
MDOP10mo ago
The logic makes sense I just need to get back into a mindset where I break things down better and use a data structure or proper algorithm
ἔρως
ἔρως10mo ago
that's why i wanted you to make the list
MD
MDOP10mo ago
Yeah might do that again tomorrow too
ἔρως
ἔρως10mo ago
you should rest too
putuardiworks
putuardiworks10mo ago
@MD how the progress now?
MD
MDOP10mo ago
Some
putuardiworks
putuardiworks10mo ago
Already solved then?
MD
MDOP10mo ago
I'm currently on writing to the database
putuardiworks
putuardiworks10mo ago
Okay, i'm here just to answer this topic question: Sanitize user input to avoid XSS attacks - Sanitize is more like check the input for required, length, format, etc. - You don't need convert/encode/escape the input using htmlspecialchars() or htmlentities() when saving the input to database. - When saving to database, use prepared statements and parameterized queries. - When displaying the data from database to HTML, then you need to convert/encode/escape the data using htmlspecialchars().
ἔρως
ἔρως10mo ago
sanitizing is the removal of characters or sequences that aren't allowed for a specific format however, it is not validation, as sanitizing a string like a<b>@gmail.com</b> will/should result in a, which obviously isn't a valid email address
putuardiworks
putuardiworks10mo ago
yes, i mistake it for validate
ἔρως
ἔρως10mo ago
it's alright everything else you said is absolutely correct
MD
MDOP10mo ago
this is what im onto next 1. Validate the error checking works     2. If good, then work on writing to database     3. After writing to database, start a session, redirect user to their todo page     4. Check for user logging out whether it be through closing browser or logging out     5. ban access to pages without proper login Process for writing to database     6. Check if database is loaded correctly,         - else add an error variable then display as a html element to the screen     1. if database loads successfully, then             - setup a prepare statement with username, and password             - bind the username, and password as parameters to the update query for user table             - execute the update query to the database which I do know you really shouldn't display a database fail to a user
ἔρως
ἔρως10mo ago
you can't check if a user closed the browser reliably also, users close their browsers and expect some sessions to still work but other than the order being weird, i think you got the right ideas
MD
MDOP10mo ago
JS could check for a browser closer since it's local
ἔρως
ἔρως10mo ago
yes, but the server shouldn't care about it
MD
MDOP10mo ago
but you're right most users want they cookie to continue letting them in for a bit so they dont have to sign
ἔρως
ἔρως10mo ago
seriously, just don't even bother with the closing the server deletes the sessions when they are unused
MD
MDOP10mo ago
true
ἔρως
ἔρως10mo ago
so, don't worry about it
MD
MDOP10mo ago
I added a try catch for database issue
ἔρως
ἔρως10mo ago
good, now you can check if hte database failed
MD
MDOP10mo ago
for now just displays a message of failure to load database just to have something there for the meantime
ἔρως
ἔρως10mo ago
that's all you can do, if the database dies
MD
MDOP10mo ago
right!
ἔρως
ἔρως10mo ago
but you have a plan, so, follow it now way better than no plan, right?
MD
MDOP10mo ago
true right now i got this
<?php
include '../../../vendor/autoload.php';
use MD\dbhelper\SQLConnection;
use MD\helpers\Validate;

$required = true;
$errors = [];
if(isset($_POST['submit'])) {
// build an error array just in case
$errors = array_filter(['username' => Validate::username($_POST['username'], $required),
'password' => Validate::password($_POST['password'], $required),
'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)]);

$addNewUser = (new SQLConnection())->connect();

// check if the database loads
if($addNewUser) {
$username =
} else {
echo "Error with database";
}

}


?>
<?php
include '../../../vendor/autoload.php';
use MD\dbhelper\SQLConnection;
use MD\helpers\Validate;

$required = true;
$errors = [];
if(isset($_POST['submit'])) {
// build an error array just in case
$errors = array_filter(['username' => Validate::username($_POST['username'], $required),
'password' => Validate::password($_POST['password'], $required),
'validatepassword' => Validate::valdiatePassword($_POST['validatepassword'], $_POST['password'], $required)]);

$addNewUser = (new SQLConnection())->connect();

// check if the database loads
if($addNewUser) {
$username =
} else {
echo "Error with database";
}

}


?>
so im thinking if database loads then add the post variables to the username and password local variables then do a prepare statement, bind, then execute
ἔρως
ἔρως10mo ago
how about you have a class that makes it easier for you to do queries and stuff? and you can check if the database loaded in it, and throw the errors you need from that class
MD
MDOP10mo ago
i can add it to my dbhelper
ἔρως
ἔρως10mo ago
oh, right, you have SQLConnection <-- that
MD
MDOP10mo ago
yeah right now it just connects
ἔρως
ἔρως10mo ago
you can add that functionality in the class
MD
MDOP10mo ago
got this so far
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
try{
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(\PDOException $e) {
//error was caught trying to create database connection.
echo "Error with loading database: " . $e;
}

return $this->pdo;
}

/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {
$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_BCRYPT);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
}
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;


class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
try{
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(\PDOException $e) {
//error was caught trying to create database connection.
echo "Error with loading database: " . $e;
}

return $this->pdo;
}

/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {
$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_BCRYPT);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
}
kinda goofed on that $pdo variable
ἔρως
ἔρως10mo ago
//error was caught trying to create database connection. <-- please, avoid comments like these those do nothing but bloat your code $encryptedPass = password_hash($password, PASSWORD_BCRYPT); <-- DO NOT USE BCRYPT!!! use the default one by the way, test the cost argument, and check which value is best for your server aim for 350ms of delay for an answer, to make it unfeasably slow for brute force attacks to be effective within a lifetime
MD
MDOP10mo ago
public function addUserQuery($username, $password) {
$options = [
'cost' => 10
];

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, $options);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
public function addUserQuery($username, $password) {
$options = [
'cost' => 10
];

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, $options);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
ten for now since it's local hosted ill test more after I get more of a working setup
ἔρως
ἔρως10mo ago
just go for 11 localhosts usually do well with 11
MD
MDOP10mo ago
it doesn't like this $stmt = $pdo->prepare($query); $pdo being the issue
ἔρως
ἔρως10mo ago
$pdo doesn't exist
MD
MDOP10mo ago
i have it as a private global variable
ἔρως
ἔρως10mo ago
yes, but you're using $pdo how do you access a private variable in php?
MD
MDOP10mo ago
oh this isnt java it is a private global class variable
ἔρως
ἔρως10mo ago
it can't be private and global
MD
MDOP10mo ago
$this
ἔρως
ἔρως10mo ago
yup that's it
MD
MDOP10mo ago
yes it is java 😄
/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {
$options = [
'cost' => 11
];

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, $options);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {
$options = [
'cost' => 11
];

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, $options);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
ἔρως
ἔρως10mo ago
that's good however, you have a problem
MD
MDOP10mo ago
$query looks odd
ἔρως
ἔρως10mo ago
it looks odd as hell, but it is fine the $options should be in a class constant yes, i said "constant" by the way, use an heredoc with the name SQL, and vscode enables syntax highlighting into it
MD
MDOP10mo ago
this is getting better
class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

const OPTION_COST = 11;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
try{
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(\PDOException $e) {

echo "Error with loading database: " . $e;
}

return $this->pdo;
}

/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {



$options = [
'cost' => SQLConnection::OPTION_COST
];

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, $options);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
}
class SQLConnection {
/**
* PDO instance
* @var type
*/

private $pdo;

const OPTION_COST = 11;

/**
* return pdo connection to sqlite
* @return \PDO
*/
public function connect() {
try{
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(\PDOException $e) {

echo "Error with loading database: " . $e;
}

return $this->pdo;
}

/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {



$options = [
'cost' => SQLConnection::OPTION_COST
];

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, $options);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
}
ἔρως
ἔρως10mo ago
no no, the whole array has to be a constant, so you can re-use it you can make it private as well by the way, the function is missing a return type and a type on the arguments
MD
MDOP10mo ago
im getting there
private const OPTIONS = [
'cost' => SQLConnection::OPTION_COST
];
private const OPTIONS = [
'cost' => SQLConnection::OPTION_COST
];
im not seeing key value const examples
ἔρως
ἔρως10mo ago
remove that cost constant put the value into the array
MD
MDOP10mo ago
private const OPTIONS = [SQLConnection::OPTION_COST];
ἔρως
ἔρως10mo ago
no SQLConnection::OPTION_COST <-- you don't need this, just put the value directly oh, btw, you can just use self instead of typing the class name
MD
MDOP10mo ago
const OPTION_COST = 11;

private const OPTIONS = [OPTION_COST];
const OPTION_COST = 11;

private const OPTIONS = [OPTION_COST];
ἔρως
ἔρως10mo ago
no that's not what i said read this message carefully
MD
MDOP10mo ago
ok i put 11
ἔρως
ἔρως10mo ago
show me
MD
MDOP10mo ago
const OPTIONS = [11];
ἔρως
ἔρως10mo ago
you're missing the key the key is mandatory
MD
MDOP10mo ago
got it everything is fixed now still getting used to the newer php syntax but in time
ἔρως
ἔρως10mo ago
that's old syntax, supported in php 5.6
MD
MDOP10mo ago
/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, self::ENCRYPTED_COST);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, self::ENCRYPTED_COST);

$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);
}
ἔρως
ἔρως10mo ago
yes, but options is a very non-specific name i know this will sound nitpicky, because it is but you need to create good habits naming things correctly is very important
MD
MDOP10mo ago
ive had good habits before in time lets focus on task at hand so is qery and stmt ok?
ἔρως
ἔρως10mo ago
yeah, it is okay, but you lack backticks around the identifiers this prevents surprises and syntax errors
MD
MDOP10mo ago
huh i just go by the php page examples
ἔρως
ἔρως10mo ago
// less good:
$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';

// perfect
$query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';

// ultimate perfect
$query = <<<'SQL'
INSERT INTO `users` (`username`, `password`)
VALUES (:username, :password)
SQL;
// less good:
$query = 'INSERT INTO users(username,password) VALUES(:username, :password)';

// perfect
$query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';

// ultimate perfect
$query = <<<'SQL'
INSERT INTO `users` (`username`, `password`)
VALUES (:username, :password)
SQL;
indentation is also very very important specially if it is tabs: tabs are a must
MD
MDOP10mo ago
true ok so "exec" returns an int for the number of affected rows or 0 if none affected
ἔρως
ἔρως10mo ago
or false in case of an error if you inserted and there were 0 affected rows, or the result is false or there's no inserted id, then it failed
MD
MDOP10mo ago
ill check for the error
ἔρως
ἔρως10mo ago
it shouldn't be too hard
MD
MDOP10mo ago
looks pretty clean
/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, self::ENCRYPTION_COST);

$query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);

if ($stmt == false) {
return $successfullyAddedUser;
}

return $successfullyAddedUser = true;
}
/**
* add user to the database query
* @return boolean
*/
public function addUserQuery($username, $password) {

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, self::ENCRYPTION_COST);

$query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);

if ($stmt == false) {
return $successfullyAddedUser;
}

return $successfullyAddedUser = true;
}
ἔρως
ἔρως10mo ago
not yet $successfullyAddedUser = false; <-- this variable is useless
if ($stmt == false) {
return $successfullyAddedUser;
}

return $successfullyAddedUser = true;
if ($stmt == false) {
return $successfullyAddedUser;
}

return $successfullyAddedUser = true;
this doesn't do what you think it does also, you aren't checking this in the proper place anyways if ($stmt == false) <-- if this is true $stmt->execute([ ... ]) <-- this will throw an error because you can't access a falsy value as an object
MD
MDOP10mo ago
hmm ok
ἔρως
ἔρως10mo ago
if ($stmt == false) <-- this is just the same as if (!$stmt) but may be confused with if ($stmt === false) unless you want to check if the value is specifically false, it's best to avoid if ($stmt == false)
MD
MDOP10mo ago
the variable is set to false though but i see
ἔρως
ἔρως10mo ago
if it is set to false, something wrong happened
MD
MDOP10mo ago
public function addUserQuery($username, $password) {

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, self::ENCRYPTION_COST);

$query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);

if(!$stmt) {
return $successfullyAddedUser;
}

return $successfullyAddedUser = true;
}
public function addUserQuery($username, $password) {

$successfullyAddedUser = false;

$encryptedPass = password_hash($password, PASSWORD_DEFAULT, self::ENCRYPTION_COST);

$query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';
$stmt = $this->pdo->prepare($query);

$stmt->execute([
':username' => $username,
':password' => $encryptedPass
]);

if(!$stmt) {
return $successfullyAddedUser;
}

return $successfullyAddedUser = true;
}
ἔρως
ἔρως10mo ago
$successfullyAddedUser <-- remove this, it's absolutely useless it only makes the code more bloated returning the value directly is a lot better than bloating with long variables
MD
MDOP10mo ago
ok so it returns an int or false ill just return the $stmt or could just make it void
ἔρως
ἔρως10mo ago
<?php

class Database {
private PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;
private const HASH_OPTIONS = [
'cost' => 11,
];

public function __construct() {
$this->pdo = new PDO('...');
}

/**
* Adds a user to the database
*
* @param string $username The username to add
* @param string $password Unhashed password
*
* @return bool|int User ID on success, `false` on error
*/
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';

/** @var PDOStatement|bool $stmt */
$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

/** @var string $hash */
$hash = password_hash($password, self::HASH_ALGO, self::HASH_OPTIONS);

/** @var bool|int $result */
$result = $stmt->execute([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
}
}
<?php

class Database {
private PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;
private const HASH_OPTIONS = [
'cost' => 11,
];

public function __construct() {
$this->pdo = new PDO('...');
}

/**
* Adds a user to the database
*
* @param string $username The username to add
* @param string $password Unhashed password
*
* @return bool|int User ID on success, `false` on error
*/
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';

/** @var PDOStatement|bool $stmt */
$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

/** @var string $hash */
$hash = password_hash($password, self::HASH_ALGO, self::HASH_OPTIONS);

/** @var bool|int $result */
$result = $stmt->execute([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
}
}
this is how i would write it
MD
MDOP10mo ago
<?php

class Database {
private PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;
private const HASH_OPTIONS = [
'cost' => 11,
];

public function __construct() {
$this->pdo = new PDO('...');
}

/**
* Adds a user to the database
*
* @param string $username The username to add
* @param string $password Unhashed password
*
* @return bool|int User ID on success, `false` on error
*/
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';

/** @var PDOStatement|bool $stmt */
$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

/** @var string $hash */
$hash = password_hash($password, self::HASH_ALGO, self::HASH_OPTIONS);

/** @var bool|int $result */
$result = $stmt->execute([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
}
}
<?php

class Database {
private PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;
private const HASH_OPTIONS = [
'cost' => 11,
];

public function __construct() {
$this->pdo = new PDO('...');
}

/**
* Adds a user to the database
*
* @param string $username The username to add
* @param string $password Unhashed password
*
* @return bool|int User ID on success, `false` on error
*/
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `users` (`username`, `password`) VALUES(:username, :password)';

/** @var PDOStatement|bool $stmt */
$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

/** @var string $hash */
$hash = password_hash($password, self::HASH_ALGO, self::HASH_OPTIONS);

/** @var bool|int $result */
$result = $stmt->execute([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
}
}
fixed it for the highlighting
ἔρως
ἔρως10mo ago
i probably broke the language name but you wanna know something? this function doesn't belong in this class but that's an issue for another time
MD
MDOP10mo ago
which one? addNewUSER
ἔρως
ἔρως10mo ago
yes you should follow the single responsibility principle
MD
MDOP10mo ago
well where should it go
ἔρως
ἔρως10mo ago
on a class to manage users
MD
MDOP10mo ago
I just have a user class in models currently
ἔρως
ἔρως10mo ago
for now, what you have is fine
MD
MDOP10mo ago
yeah id have to remove pdo code because you shouldnt have pdo stuff in models unless you can im still kinda learning the "setup" for php code following their mvc approach
ἔρως
ἔρως10mo ago
yeah, i know what you mean
MD
MDOP10mo ago
code is updated and pushed to github
ἔρως
ἔρως10mo ago
but first, lets make it work
MD
MDOP10mo ago
ok so in register.php i removed the addNewUser stuff just got errors
ἔρως
ἔρως10mo ago
you shouldn't have moved yet you don't have any interfaces to access the database basically, the database class is a closed system
MD
MDOP10mo ago
ok guess that's next making that interface will be last thing i do for night
ἔρως
ἔρως10mo ago
then you will have to make a plan on what the class does
MD
MDOP10mo ago
well I got two models already one for user and one for task
ἔρως
ἔρως10mo ago
that doesn't say what the database class needs to do
MD
MDOP10mo ago
database should only add users, remove users, add tasks, remove tasks, update users, update tasks basically CRUD
ἔρως
ἔρως10mo ago
that's not right the database should only care about database adding users, removing users, adding tasks, removing tanks, updating users, updating tanks ... that's all stuff for the controller
MD
MDOP10mo ago
oh right i forgot about the controller 😄 yeah i need to do a little MVC studying tomorrow then get back to this
ἔρως
ἔρως10mo ago
i think that that's a good idea
MD
MDOP10mo ago
Database should really just connect
ἔρως
ἔρως10mo ago
connect and do query stuff
MD
MDOP10mo ago
so i need a database interface?
ἔρως
ἔρως10mo ago
that class is the "interface"
MD
MDOP10mo ago
wait dont we need a __destruct for database
ἔρως
ἔρως10mo ago
no, it disconnects at the end of the script
MD
MDOP10mo ago
so i guess next then is controllers?
ἔρως
ἔρως10mo ago
that's going to be a big re-write, but yes
MD
MDOP10mo ago
yeah too late for that tonight what am i missing to finish tonight
ἔρως
ἔρως10mo ago
sleep
MD
MDOP10mo ago
oh ok good idea 🙂
ἔρως
ἔρως10mo ago
i like that idea for myself too
MD
MDOP10mo ago
rest well 🙂
ἔρως
ἔρως10mo ago
thank you, i will
MD
MDOP10mo ago
I think i only need one controller because the user is interacting with it i dont think user needs a controller but I could be wrong
ἔρως
ἔρως10mo ago
lots of code will have to interact with the user
MD
MDOP10mo ago
I need to restructure my models
ἔρως
ἔρως10mo ago
go for it then
MD
MDOP10mo ago
yeah I read up on MVC last night models should handle database stuff with the model then controller acts as the api to it im not sure what roles a "user" has where as task is easy it's just CRUD
ἔρως
ἔρως10mo ago
a user can login, be created, deleted, edited, logout, be disabled
MD
MDOP10mo ago
so similar?
ἔρως
ἔρως10mo ago
depends you need to communicate with the database in all those
MD
MDOP10mo ago
so im starting with task because it seems easier
ἔρως
ἔρως10mo ago
go for it just do what sounds better to do first
MD
MDOP10mo ago
so for list tasks
public function list_tasks($user) {
$sql = "SELECT * FROM tasks WHERE "
}
public function list_tasks($user) {
$sql = "SELECT * FROM tasks WHERE "
}
it should be userid = $user since it should be the user's userid
ἔρως
ἔρως10mo ago
that depends on what you have
MD
MDOP10mo ago
just Task.php and User.php for models
CREATE TABLE users (userid INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password TEXT NOT NULL);
CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN, FOREIGN KEY(userid) REFERENCES users(userid));
CREATE TABLE users (userid INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, password TEXT NOT NULL);
CREATE TABLE tasks (taskid INTEGER PRIMARY KEY AUTOINCREMENT, userid INTEGER NOT NULL, description TEXT, done BOOLEAN, FOREIGN KEY(userid) REFERENCES users(userid));
ἔρως
ἔρως10mo ago
sounds good to me, i don't see any errors
MD
MDOP10mo ago
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;
use PDOException;

class Database {
/**
* PDO instance
* @var type
*/

private \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{
try {
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(PDOException $e) {
echo "Error with loading database: " . $e;
}

}

public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
}
}
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;
use PDOException;

class Database {
/**
* PDO instance
* @var type
*/

private \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{
try {
$this->pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(PDOException $e) {
echo "Error with loading database: " . $e;
}

}

public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
}
}
so im bringing in the pdo to the model
<?php

namespace MD\models;
use MD\dbhelper\Database;

class Task extends Database
{
public $description;
protected $done;

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

public function done() {
$this->done = true;
}

public function list_tasks($user) {
$con = $this->__construct();
$sql = "SELECT * FROM `tasks` WHERE `userid={$user}`";
$sql_exc = $this->
}
}
<?php

namespace MD\models;
use MD\dbhelper\Database;

class Task extends Database
{
public $description;
protected $done;

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

public function done() {
$this->done = true;
}

public function list_tasks($user) {
$con = $this->__construct();
$sql = "SELECT * FROM `tasks` WHERE `userid={$user}`";
$sql_exc = $this->
}
}
ἔρως
ἔρως10mo ago
NO! GOD NO, PLEASE DON'T stop, now that is bad in many levels the database should be passed to the models via dependency injection $this->__construct(); <-- ABSOLUTELY never do this in php
MD
MDOP10mo ago
removed the "extends Database" and removed the "$con = $this->__construct();"
ἔρως
ἔρως10mo ago
good
MD
MDOP10mo ago
let's do this right since my approach is garbage
ἔρως
ἔρως10mo ago
it's not about it being bad it's about it completely shattering how oop works maybe you could try to implement a trait that gives access to the database, but it is a really bad way too
MD
MDOP10mo ago
guess i poorly designed this then 😄
ἔρως
ἔρως10mo ago
mvc is always hard to design
MD
MDOP10mo ago
I used to be better at it gotta retrain myself
ἔρως
ἔρως10mo ago
you can implement it using attributes, but i think you will have to use class reflection
MD
MDOP10mo ago
so the approach you're suggesting is having the database being brought in as a parameter to the functions then called? if im understanding that right
ἔρως
ἔρως10mo ago
as a parameter of the constructor for the models, yes or all models automatically have a $pdo attribute
MD
MDOP10mo ago
hmm either could work
ἔρως
ἔρως10mo ago
i would go with the 2nd option
MD
MDOP10mo ago
right not my models dont have a constructor
ἔρως
ἔρως10mo ago
they don't need
MD
MDOP10mo ago
make it a private pdo?
ἔρως
ἔρως10mo ago
protected you cant make it private
MD
MDOP10mo ago
ok sorry just the discussion in general made me really mad had to step away a moment right now database class just has the construct for pdo
ἔρως
ἔρως10mo ago
create a model class for your models
MD
MDOP10mo ago
like an abstract class?
ἔρως
ἔρως10mo ago
yes
MD
MDOP10mo ago
just like coreModel?
ἔρως
ἔρως10mo ago
just model
MD
MDOP10mo ago
<?php

namespace MD\models;
use MD\dbhelper\Database;

abstract class Model {

}
<?php

namespace MD\models;
use MD\dbhelper\Database;

abstract class Model {

}
ἔρως
ἔρως10mo ago
yup, something like that
MD
MDOP10mo ago
ok so what are the important parts for this
ἔρως
ἔρως10mo ago
the model needs to store a single instance of whatever you want to use and pass to the models
MD
MDOP10mo ago
probably the database?
ἔρως
ἔρως10mo ago
yup, and other things
MD
MDOP10mo ago
ok so in this case we can bring in the database and use the constructor to create the pdo object and have the other models borrow it as extends to base class mode? model
ἔρως
ἔρως10mo ago
that's the idea, yes
MD
MDOP10mo ago
im kinda surprised i had like a mini flashback to oop topics in college
ἔρως
ἔρως10mo ago
that's how it usually is things will click soon
MD
MDOP10mo ago
<?php

namespace MD\models;
use MD\dbhelper\Database;

abstract class Model {
protected $pdo;

public function __construct() {
$pdo = new Database;


}
}
<?php

namespace MD\models;
use MD\dbhelper\Database;

abstract class Model {
protected $pdo;

public function __construct() {
$pdo = new Database;


}
}
so I rememebered that you can't call functions statically without them being static so I called my database class this way instead of trying to do the construct method since it wasn't static then having to change all that code around here's what I came up with since Database class handles the connection, I have this could this setup the model using it ```php <?php namespace MD\models; use MD\dbhelper\Database; use PDOException; abstract class Model { protected $pdo; public function construct() { $pdo = new Database; try { $pdo->__construct(); } catch(PDOException $e) { echo "Database could not connect: " . $e; } return $pdo; } }``` I remembered in Java that abstract classes were the base for the "Object" class and some of its methods that all classes use then classes just inherit and extend the Object class seems to be weird trying to call prepare not able to access the PDO object from the database class
ἔρως
ἔρως10mo ago
why are you calling the constructor twice?
MD
MDOP10mo ago
Wasn't sure why I can't access the pdo object through the database class
ἔρως
ἔρως10mo ago
yeah, but that's not how oop works the constructor is called automatically, and you shouldn't call it manually
MD
MDOP10mo ago
What should I change
ἔρως
ἔρως10mo ago
never ever ever call a constructor manually, inless it is the constructor of the parent class and even then, you only do it inside the constructor
MD
MDOP10mo ago
I couldn't figure out how to reach the PDO object I thought i had to call the constructor to get to it
ἔρως
ἔρως10mo ago
you call the constructor when you use new
MD
MDOP10mo ago
Yeah but when I do $pdo-> none of the PDO functions pop up
ἔρως
ἔρως10mo ago
because your Database class isn't an instance of PDO
MD
MDOP10mo ago
Hmm So how do I get to pdo since it's used in database class
ἔρως
ἔρως10mo ago
is it publicly accessible?
MD
MDOP10mo ago
Private pdo variable
ἔρως
ἔρως10mo ago
do you have a method to return it?
MD
MDOP10mo ago
I even tried returning it in the constructor It failed
ἔρως
ἔρως10mo ago
constructors' return values are ignored
MD
MDOP10mo ago
GitHub
PHPTodoApp/app/class/dbhelper/SQLConnection.php at main · MD-2016/P...
practice project to get back into php. Contribute to MD-2016/PHPTodoApp development by creating an account on GitHub.
ἔρως
ἔρως10mo ago
i mean, your class only has an empty connect method and a non-public select method you should only see the connectmethod
MD
MDOP10mo ago
Yeah I added those to work on tomorrow since I got too tired and a headache
ἔρως
ἔρως10mo ago
you should rest
MD
MDOP10mo ago
So setup basic database stuff in Database class, then have Model constructor call database then use the methods from database through Model on the User and Task models?
ἔρως
ἔρως10mo ago
basically, yes
MD
MDOP10mo ago
Hey I had a smart moment (:
ἔρως
ἔρως10mo ago
you did you just need somewhere to store your instances of the classes
MD
MDOP10mo ago
Yeah pretty much After those are done I can work on controllers which should basically just pull the model commands then act as the api to the view to display
ἔρως
ἔρως10mo ago
yup and will you write the views in php?
MD
MDOP10mo ago
Hmm Well the login and register both need php code to function and the user page will pull from the database Most likely
ἔρως
ἔρως10mo ago
makes perfect sense, and is the easiest anyways
MD
MDOP10mo ago
I guess if I made an about page or if I had pages for like 404 or something I could make them html Oh! After models and controllers are done, I need to make a router
ἔρως
ἔρως10mo ago
shouldn't the router be first?
MD
MDOP10mo ago
Technically yes I just didn't think about til just now
ἔρως
ἔρως10mo ago
yeah, you should think about it then
MD
MDOP9mo ago
so i removed the code from the construct and put it in the connect() and made the connect static
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;
use PDOException;

class Database {
/**
* PDO instance
* @var type
*/

private \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{


}

public static function connect() {
try {
$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

return $pdo;

}

protected static function select($query) {
$pdo = Database::connect();
$sql = "SELECT "
}
/*
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
} */
}
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;
use PDOException;

class Database {
/**
* PDO instance
* @var type
*/

private \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{


}

public static function connect() {
try {
$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

return $pdo;

}

protected static function select($query) {
$pdo = Database::connect();
$sql = "SELECT "
}
/*
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
} */
}
ἔρως
ἔρως9mo ago
i have a much better idea for you how about you create the $pdo in the __construct? and store it in a static private variable?
MD
MDOP9mo ago
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;
use PDOException;

class Database {
/**
* PDO instance
* @var type
*/

private static \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{
try {
$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

return $pdo;

}



protected static function select($) {
$pdo = Database::connect();
$sql = "SELECT `{$query}` FROM "
}
/*
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
} */
}
<?php

namespace MD\dbhelper;
use MD\dbhelper\config;
use PDOException;

class Database {
/**
* PDO instance
* @var type
*/

private static \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{
try {
$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

return $pdo;

}



protected static function select($) {
$pdo = Database::connect();
$sql = "SELECT `{$query}` FROM "
}
/*
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
} */
}
ἔρως
ἔρως9mo ago
if the static private variable is empty, you create a new pdo if it isn't empty, you do nothing don't return from __contruct
MD
MDOP9mo ago
fixed
ἔρως
ἔρως9mo ago
in the __construct, check if the static variable is empty before you create the pdo object
MD
MDOP9mo ago
if !$pdo?
ἔρως
ἔρως9mo ago
almost that's a local variable, not static
MD
MDOP9mo ago
it has to be instantiated before you can check if it is empty unless you declare it globally first and instantiate it
ἔρως
ἔρως9mo ago
not if you set it to null by default or check with empty
MD
MDOP9mo ago
so i would check if (!empty($pdo))
ἔρως
ἔρως9mo ago
almost that's a local variable you want to access a static variable
MD
MDOP9mo ago
public function __construct()
{

if(empty($pdo)) {
try {
$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);

} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

}
}
public function __construct()
{

if(empty($pdo)) {
try {
$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);

} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

}
}
ἔρως
ἔρως9mo ago
again local variable it won't work
MD
MDOP9mo ago
mines at top of class class variable
ἔρως
ἔρως9mo ago
if(empty($pdo)) { <-- this is a local variable
MD
MDOP9mo ago
self::
ἔρως
ἔρως9mo ago
yes
MD
MDOP9mo ago
so that $pdo declaration should be self::$pdo =
ἔρως
ἔρως9mo ago
all $pdo inside that class, yes
MD
MDOP9mo ago
public function __construct()
{

if(empty(self::$pdo)) {
try {
self::$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);

} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

}
}
public function __construct()
{

if(empty(self::$pdo)) {
try {
self::$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);

} catch(PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}

}
}
ἔρως
ἔρως9mo ago
yup, that's it
MD
MDOP9mo ago
so now there needs to be methods for select, update, create, and delete
ἔρως
ἔρως9mo ago
yeah, you can write your own ones, to make it easier for you to do what you need
MD
MDOP9mo ago
I think I should either take in an array or more variables because ill need to see what is changing
ἔρως
ἔρως9mo ago
you can accept a table, a list of rows and receive a list of values
MD
MDOP9mo ago
you mean the table construct that's mysql i think
ἔρως
ἔρως9mo ago
what table construct?
ἔρως
ἔρως9mo ago
why are you reading about mysql vendor-specific extensions when you're using pdo?
MD
MDOP9mo ago
how you make a table?
ἔρως
ἔρως9mo ago
you write the sql for it like how you did before
MD
MDOP9mo ago
good idea
ἔρως
ἔρως9mo ago
but don't forget the basics, like select, insert, update, delete
MD
MDOP9mo ago
protected function select($field: string, $table: string): {
$sql = "SELECT `{$field}` FROM `{$table}`";
self::$pdo;
$stmt = self::$pdo->prepare($sql);

}
protected function select($field: string, $table: string): {
$sql = "SELECT `{$field}` FROM `{$table}`";
self::$pdo;
$stmt = self::$pdo->prepare($sql);

}
this is a start so far sadly the intellisense must be broke because it isn't picking up on the PDO functions
ἔρως
ἔρως9mo ago
self::$pdo; <-- why this?
MD
MDOP9mo ago
just pdo?
ἔρως
ἔρως9mo ago
no, it's just there, doing nothing
MD
MDOP9mo ago
mistake was having trouble listing the PDO functions I could use sadly they dont show up
ἔρως
ἔρως9mo ago
intelephense is ass it lists ALL THE FUNCTIONS because it's ass but it's the best we have, so you will only see the list of functions if you ... type a letter
MD
MDOP9mo ago
I should make sure the $field is properly handled it could be more than one
ἔρως
ἔρως9mo ago
which means that ...?
MD
MDOP9mo ago
probably need an array or spread operator?
ἔρως
ἔρως9mo ago
an array
MD
MDOP9mo ago
protected function select($field, $table) {
$sql = "SELECT `{$field}` FROM `{$table}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = self::$pdo->exec($stmt);

return $stmt_exec;

}

protected function select($field, $table) {
$sql = "SELECT `{$field}` FROM `{$table}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = self::$pdo->exec($stmt);

return $stmt_exec;

}

a WHERE in there is possible too
ἔρως
ἔρως9mo ago
you can make it even easier instead of 1 array and a string, you can do 1 string or 1 array 'table' <-- would be equivalent to table.* ['table' => [...]] <-- would be equivalent to selecting each field from that specific table
MD
MDOP9mo ago
protected function select(array $query): string {
$sql = "SELECT `{$query}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = self::$pdo->exec($stmt);

return $stmt_exec;

}
protected function select(array $query): string {
$sql = "SELECT `{$query}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = self::$pdo->exec($stmt);

return $stmt_exec;

}
ἔρως
ἔρως9mo ago
🤣 no, i wish it was that simple
MD
MDOP9mo ago
😦 well crap
ἔρως
ἔρως9mo ago
😂
MD
MDOP9mo ago
let me guess I need to loop that $sql creation
ἔρως
ἔρως9mo ago
depends on the values you receive
MD
MDOP9mo ago
so what is wrong with that code
ἔρως
ἔρως9mo ago
well, you're trying to use an array as a string
MD
MDOP9mo ago
it should be a string array 😄
ἔρως
ἔρως9mo ago
😂 dreams js does that automatically
MD
MDOP9mo ago
or there's another way
ἔρως
ἔρως9mo ago
there's another way you can generate 2 different queries if it is a string, just the basic select * from table if it is an array, then you have to implode it in a may to make it proper sql table names
MD
MDOP9mo ago
protected function select(): string {
$args = func_get_args();
$sql = "SELECT `{$args}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = self::$pdo->exec($stmt);

return $stmt_exec;

}
protected function select(): string {
$args = func_get_args();
$sql = "SELECT `{$args}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = self::$pdo->exec($stmt);

return $stmt_exec;

}
ἔρως
ἔρως9mo ago
that's absolutely much worse
MD
MDOP9mo ago
since func_get_args() gets variable number of arguments from the function
ἔρως
ἔρως9mo ago
yes, but that's horrible, trust me just take 1 argument, and accept a string or array also, exec doesn't return a string
MD
MDOP9mo ago
protected function select(string|array $query): string {
if()
}

protected function select(string|array $query): string {
if()
}

ἔρως
ἔρως9mo ago
yup
MD
MDOP9mo ago
what's the type checker in php?
ἔρως
ἔρως9mo ago
php
MD
MDOP9mo ago
i mean function 😄
ἔρως
ἔρως9mo ago
what do you mean?
MD
MDOP9mo ago
like if query is array
ἔρως
ἔρως9mo ago
is_array
MD
MDOP9mo ago
ok
ἔρως
ἔρως9mo ago
if you need to check what type something is, just use is_<type>
MD
MDOP9mo ago
protected function select(string|array $query): string {
$type = is_string($query);
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = $stmt->exec($stmt);
}
else {

}
}

protected function select(string|array $query): string {
$type = is_string($query);
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
$stmt_exec = $stmt->exec($stmt);
}
else {

}
}

can use implode
ἔρως
ἔρως9mo ago
it's not a matter of "can", but it's a "use implode or build it yourself in a worse way" which is something common in php
MD
MDOP9mo ago
woo
protected function select(string|array $query) {
$type = is_string($query);
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
protected function select(string|array $query) {
$type = is_string($query);
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
this means loop to get the statements in the sql hmm so how does this work
ἔρως
ἔρως9mo ago
how about you stop and write a list of what you want the function to do? like how you did before?
MD
MDOP9mo ago
i got it 🙂
protected function select(string|array $query) {
$type = is_string($query);
if(empty($query)) {
echo "Please have select statement";
}
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
}
protected function select(string|array $query) {
$type = is_string($query);
if(empty($query)) {
echo "Please have select statement";
}
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
}
ἔρως
ἔρως9mo ago
no, not yet just make the list, trust me
MD
MDOP9mo ago
What the select statement should do

1. user passes in a string or array for the proper query
2. check if the string or array is empty
- print an error message
3. else check if the type is string
- it is string
- select all items from the table being sent as the string
- execute the statment to the database
- return the results as a string
- display it to the user
4. else it is an array
- get all the select query statement variables
- create the sql query for the database
- execute the sql statement
- return the results as a string
- display it to the user
What the select statement should do

1. user passes in a string or array for the proper query
2. check if the string or array is empty
- print an error message
3. else check if the type is string
- it is string
- select all items from the table being sent as the string
- execute the statment to the database
- return the results as a string
- display it to the user
4. else it is an array
- get all the select query statement variables
- create the sql query for the database
- execute the sql statement
- return the results as a string
- display it to the user
ἔρως
ἔρως9mo ago
point 2, the print part that's absolutely horrible don't do that throw an exception point 3, 2nd line is literally impossiible impossible as well as 4
MD
MDOP9mo ago
list didnt help me then
ἔρως
ἔρως9mo ago
yes it did trust me, it always does
MD
MDOP9mo ago
point 2 is fixed
protected function select(string|array $query) {
$type = is_string($query);
if(empty($query)) {
throw new ErrorException("Select statement must not be empty");
}
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
}

protected function select(string|array $query) {
$type = is_string($query);
if(empty($query)) {
throw new ErrorException("Select statement must not be empty");
}
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
}

ἔρως
ἔρως9mo ago
why an error exception when an argument count (https://www.php.net/manual/en/class.argumentcounterror.php) would probably more appropriate?
MD
MDOP9mo ago
i was going by this
MD
MDOP9mo ago
remember im not this well versed in php i barely ever used php in life java is probably the only language ive used extensively
ἔρως
ἔρως9mo ago
i know, but im asking this so you stop and think
MD
MDOP9mo ago
okay changed to value error so point 2 is fixed
ἔρως
ἔρως9mo ago
and the other one?
MD
MDOP9mo ago
im not sure
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->exec($stmt);
}
}
that's this code
ἔρως
ἔρως9mo ago
return an array databases dont return a single string
MD
MDOP9mo ago
how
ἔρως
ἔρως9mo ago
by just fetching or a generator
MD
MDOP9mo ago
ok
ἔρως
ἔρως9mo ago
by the way, exec doesn't exist https://www.php.net/manual/en/pdostatement.execute.php <-- it's execute look at the return values as well
MD
MDOP9mo ago
here's some progress
protected function select(string|array $query) {
$type = is_string($query);
if(empty($query)) {
throw new ValueError("Select statement must not be empty");
}
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
$stmt->execute($stmt);
$results = $stmt->fetchAll();
return $results;
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->execute($stmt);
}
}
}
protected function select(string|array $query) {
$type = is_string($query);
if(empty($query)) {
throw new ValueError("Select statement must not be empty");
}
else {
if($type) {
$sql = "SELECT * FROM `{$query}`";
$stmt = self::$pdo->prepare($sql);
$stmt->execute($stmt);
$results = $stmt->fetchAll();
return $results;
}
else {
$statements = implode(",", $query);
$sql = "SELECT `{$statements}`";
$stmt = self::$pdo->prepare($sql);
return $stmt->execute($stmt);
}
}
}
ἔρως
ἔρως9mo ago
it is some progress, but that's still not going to work first, you're nesting too much and your indentation needs serious work, asap fix those 2 things then, worry about making the string type work completely ignore arrays for now
MD
MDOP9mo ago
im not really concerned about indentation that can be fixed after code is working right now this needs to work
ἔρως
ἔρως9mo ago
well, you have to have good habits well indented code is mandatory
MD
MDOP9mo ago
amazon didnt care they were just happy it worked
ἔρως
ἔρως9mo ago
it helps you catch bugs, it helps others to read the code it highlights the conditions a lot more visibly you can quickly glance at the code and understand how many levels deep you're getting into so, please, fix your indentation
MD
MDOP9mo ago
any fast way in vscode i dont wanna do this forever
ἔρως
ἔρως9mo ago
yes: use tabs you can convert the indentation to tabs and it fixes everything for you
MD
MDOP9mo ago
<?php

namespace MD\dbhelper;

use ErrorException;
use Exception;
use MD\dbhelper\config;
use PDOException;
use ValueError;

class Database
{
/**
* PDO instance
* @var type
*/

private static \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{

if (empty(self::$pdo)) {
try {
self::$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch (PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}
}
}

protected function select(string|array $query)
{
$type = is_string($query);
if (empty($type)) {
throw new ValueError("Select statement must not be empty");
}
}




/*
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
} */
}
<?php

namespace MD\dbhelper;

use ErrorException;
use Exception;
use MD\dbhelper\config;
use PDOException;
use ValueError;

class Database
{
/**
* PDO instance
* @var type
*/

private static \PDO $pdo;

private const HASH_ALGO = PASSWORD_DEFAULT;

private const ENCRYPTION_COST = ['cost' => 11];

public function __construct()
{

if (empty(self::$pdo)) {
try {
self::$pdo = new \PDO("sqlite:" . Config::PATH_TO_SQLITE_FILE);
} catch (PDOException $e) {
echo "Error with loading database: " . $e->getMessage();
}
}
}

protected function select(string|array $query)
{
$type = is_string($query);
if (empty($type)) {
throw new ValueError("Select statement must not be empty");
}
}




/*
public function addUserQuery(string $username, string $password): bool|int {
static $query = 'INSERT INTO `user` (`username`, `password`) VALUES(:username, :password)';

$stmt = $this->pdo->prepare($query);

if(!$stmt) {
return false;
}

$hash = password_hash($password, self::HASH_ALGO, self::ENCRYPTION_COST);

$result = $stmt->exec([
':username' => $username,
':password' => $hash
]);

if(!$result) {
return false;
}

return $this->pdo->lastInsertId();
} */
}
better?
ἔρως
ἔρως9mo ago
much better
MD
MDOP9mo ago
ok back to more important matters
ἔρως
ἔρως9mo ago
$type = is_string($query);
if (empty($type)) {
throw new ValueError("Select statement must not be empty");
}
$type = is_string($query);
if (empty($type)) {
throw new ValueError("Select statement must not be empty");
}
^ you have a bug
MD
MDOP9mo ago
fixed
ἔρως
ἔρως9mo ago
can you show the fixed code?
MD
MDOP9mo ago
$type = is_string($query);
if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}
$type = is_string($query);
if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}
ἔρως
ἔρως9mo ago
yup but delete the $type variable
MD
MDOP9mo ago
ok whenever I tried to use is_string in an if vscode complained it was syntax errored well oddly it didnt this time weird this next
if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
$results = $stmt->fetchAll();
return $results;

} else {

}
if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
$results = $stmt->fetchAll();
return $results;

} else {

}
ἔρως
ἔρως9mo ago
instead of fetchall, try a generator basically, loop and yield
MD
MDOP9mo ago
ok
ἔρως
ἔρως9mo ago
try it, see how it feels for you
MD
MDOP9mo ago
sorry cat was frustrating me generator looks too complicated
ἔρως
ἔρως9mo ago
dude, it's literally a fake array you get the values with a foreach loop you replace the return with yield
MD
MDOP9mo ago
ok so how would this work after my execute
ἔρως
ἔρως9mo ago
while($row = $stmt->fetch()) {
yield $row;
}
while($row = $stmt->fetch()) {
yield $row;
}
that's literally it
MD
MDOP9mo ago
no way their examples are just foreach and for loops lol
ἔρως
ἔρως9mo ago
that's all you need this way, the select method returns a generator and you just use a foreach on it
MD
MDOP9mo ago
protected function select(string|array $query)
{

if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {

}
}
protected function select(string|array $query)
{

if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {

}
}
dinner break
ἔρως
ἔρως9mo ago
looks good to mne me
MD
MDOP9mo ago
what do I do with the yield since we were returning an array?
ἔρως
ἔρως9mo ago
yield yields a value, and waits for you to use the generator to actually give you the value so, imagine you return 500 rows
MD
MDOP9mo ago
so add the value to an array?
ἔρως
ἔρως9mo ago
instead of giving you a massive array of 500 rows, it gives you 1 row at a time and then you do whatever you want you can loop it and show it imediately
MD
MDOP9mo ago
oh since this isn't the right class for it where should it go?
ἔρως
ἔρως9mo ago
i never said it wasn't the right class im just saying that a yield is like a return, but waits for you to use the value instead of an array, it gives a generator
MD
MDOP9mo ago
protected function select(string|array $query)
{

if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {
$sql = "";
$stmt = self::$pdo->prepare($sql);

}
}
protected function select(string|array $query)
{

if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {
$sql = "";
$stmt = self::$pdo->prepare($sql);

}
}
that's what I got so far
MD
MDOP9mo ago
so I can return the yield or function?
ἔρως
ἔρως9mo ago
it's not a return, it's not a function it's a yield
MD
MDOP9mo ago
oh ok so id just call the function to use it? then use a for loop to use it later?
ἔρως
ἔρως9mo ago
check line 13 of the code
MD
MDOP9mo ago
i see it
foreach(xyz() as $value){
echo json_encode($value), PHP_EOL;
}
foreach(xyz() as $value){
echo json_encode($value), PHP_EOL;
}
ἔρως
ἔρως9mo ago
that's extremely useful because it doesn't "return" a value until you iterate the generator so, it will wait until the value is needed
MD
MDOP9mo ago
so far that while loop Id do just what you did
ἔρως
ἔρως9mo ago
yup
MD
MDOP9mo ago
protected function select(string|array $query)
{

if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

foreach()

} else {
$sql = "";
$stmt = self::$pdo->prepare($sql);

}
}
protected function select(string|array $query)
{

if (empty($query)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($query)) {
$sql = "SELECT * FROM {$query}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

foreach()

} else {
$sql = "";
$stmt = self::$pdo->prepare($sql);

}
}
do i say $row as $value?
ἔρως
ἔρως9mo ago
you actually don't do anything else once you yield, the function stops
MD
MDOP9mo ago
ok since i got a meetup with a friend in a few lets aim to finish select for tonight
ἔρως
ἔρως9mo ago
remove the for each, and you have 70% done
MD
MDOP9mo ago
you see where im at currently for the array it's rmeoved removed
ἔρως
ἔρως9mo ago
by the way, instead of calling it $query, i would give it a descriptive name, like $table
MD
MDOP9mo ago
fixed
protected function select(string|array $table)
{

if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {
$sql = "";
$stmt = self::$pdo->prepare($sql);

}
}
protected function select(string|array $table)
{

if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {
$sql = "";
$stmt = self::$pdo->prepare($sql);

}
}
` so we know select is the first word ok so my guess is you would implode the array first then add it to the sql statement? we would implode based on " " space beng the separator
ἔρως
ἔρως9mo ago
backtick, comma, backtick
MD
MDOP9mo ago
else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
}
else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
}
ἔρως
ἔρως9mo ago
you have to have backticks on the value as well
MD
MDOP9mo ago
updated then do the generator? updated function
protected function select(string|array $table)
{

if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}
}
}
protected function select(string|array $table)
{

if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}

} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
$stmt = self::$pdo->prepare($sql);
$stmt->execute();

while($row = $stmt->fetch()) {
yield $row;
}
}
}
ἔρως
ἔρως9mo ago
it's still missing something from the array one, but it's fine for now now, move everything that's duplicated out all of it off of the conditions
MD
MDOP9mo ago
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
this are duplicated the sql statements are a bit different
ἔρως
ἔρως9mo ago
the loop is duplicated too
MD
MDOP9mo ago
111php
protected function select(string|array $table)
{

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch()) {
yield $row;
}

if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";




} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";

}
}
protected function select(string|array $table)
{

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch()) {
yield $row;
}

if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";




} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";

}
}
moved out for now
ἔρως
ἔρως9mo ago
to the bottom of the function code is executed from top to bottom
MD
MDOP9mo ago
i just moved it for now i commented it out
ἔρως
ἔρως9mo ago
why did you comment it out?
MD
MDOP9mo ago
so i dont lose it until this part is done then delete it
ἔρως
ἔρως9mo ago
can you send what you have?
MD
MDOP9mo ago
protected function select(string|array $table)
{

/*
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch()) {
yield $row;
}
*/
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";




} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";

}
}
protected function select(string|array $table)
{

/*
$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch()) {
yield $row;
}
*/
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";




} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";

}
}
ἔρως
ἔρως9mo ago
why did you comment it out? you just have it in the wrong place that's all
MD
MDOP9mo ago
because i have no clue what were doing so i dont want to lose working code
ἔρως
ἔρως9mo ago
you see where the repeated code is, in this function?
MD
MDOP9mo ago
protected function select(string|array $table)
{



if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";




} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";

}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch()) {
yield $row;
}
}
protected function select(string|array $table)
{



if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if(is_string($table)) {
$sql = "SELECT * FROM {$table}";




} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";

}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch()) {
yield $row;
}
}
ἔρως
ἔρως9mo ago
there you go now fix up the spacing
MD
MDOP9mo ago
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
ἔρως
ἔρως9mo ago
much better
MD
MDOP9mo ago
whats next
ἔρως
ἔρως9mo ago
rest
MD
MDOP9mo ago
it's not done though array feels like it's not done
ἔρως
ἔρως9mo ago
it isn't i just don't a good way for it
MD
MDOP9mo ago
tomorrow ill tackle it next then my goal for weekend is at least have database and model done and start on controllers
ἔρως
ἔρως9mo ago
you can finish the other methods too, later on
MD
MDOP9mo ago
im gonna have to start working at a faster pace finish things sooner my turtle speed means no jobs for me
ἔρως
ἔρως9mo ago
everybody learns at their own speed i learn super quick, doing multiple things at the same time not everybody is like that
MD
MDOP9mo ago
im gonna have to get there because no one will hire me with this pace
ἔρως
ἔρως9mo ago
you will get there
MD
MDOP9mo ago
hope so is there a solution to the array thing
ἔρως
ἔρως9mo ago
yes there are 2 solutions: 1- ['table', ['col1', 'col2', ... 'colx']] 2- ['table' => ['col1', 'col2', ... 'colx']]
MD
MDOP9mo ago
oh neat! 🙂
ἔρως
ἔρως9mo ago
you pick how it will work
MD
MDOP9mo ago
ok ok so back to the array
ἔρως
ἔρως9mo ago
yes, which option will you choose?
MD
MDOP9mo ago
both are good but maybe which ever is a bit easier
ἔρως
ἔρως9mo ago
the 1st one
MD
MDOP9mo ago
so I have the word SELECT already so I know it will be "column(s)" then FROM then either WHERE or not ok let's do it
ἔρως
ἔρως9mo ago
yup, the 2nd one is columns and you know what to do with it
MD
MDOP9mo ago
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$fixedStatement = implode("`,`", $table);
$sql = "SELECT `{$fixedStatement}`";
}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
here's our starting point
ἔρως
ἔρως9mo ago
on the else is where you generate the sql for the array
MD
MDOP9mo ago
yes I have it somewhat started
ἔρως
ἔρως9mo ago
yup, you do
MD
MDOP9mo ago
so what is missing here im guessing SQL statement is wrong
ἔρως
ἔρως9mo ago
it's missing the table and the variable is wrong
MD
MDOP9mo ago
else {
$table = implode("`,`", $table);
$sql = "SELECT `{$table}`";

}
else {
$table = implode("`,`", $table);
$sql = "SELECT `{$table}`";

}
ἔρως
ἔρως9mo ago
almost there
MD
MDOP9mo ago
only other way off top of my head is to have table be broken up into $statements, and $parameters
ἔρως
ἔρως9mo ago
that naming doesn't make sense, but i know what you mean
MD
MDOP9mo ago
let's try an example say we get SELECT description FROM tasks WHERE userid=2; using w3schools run editor we get this
SELECT`,`description`,`FROM`,`tasks`,`WHERE`,`userid`,`=`,`2`,`;
SELECT`,`description`,`FROM`,`tasks`,`WHERE`,`userid`,`=`,`2`,`;
using example code...
<?php
$arr = array('SELECT','description', 'FROM', 'tasks', 'WHERE', 'userid', '=', '2', ';');
echo implode("`,`",$arr);
?>
<?php
$arr = array('SELECT','description', 'FROM', 'tasks', 'WHERE', 'userid', '=', '2', ';');
echo implode("`,`",$arr);
?>
ἔρως
ἔρως9mo ago
what do you have in mind for when you call the function?
MD
MDOP9mo ago
it should come out SELECT description FROM tasks WHERE userid=2;` at least that's the goal unfortunately discord doesn't handle it correctly because of how they handle backticks but you get the idea so this $table = implode(",", $table);` is incorrect
ἔρως
ἔρως9mo ago
i get the idea and it is incorrect, yes but im trying to understand what you think that it will look like to do a select from this
MD
MDOP9mo ago
they bring in an array of the commands words then implode it then create the query, prepare it then execute
ἔρως
ἔρως9mo ago
write in code or pseudocode
MD
MDOP9mo ago
if you just use the space you get the right idea
<?php
$arr = array('SELECT','description', 'FROM', 'tasks', 'WHERE', 'userid', '=', '2', ';');
echo implode(" ",$arr);
?>
<?php
$arr = array('SELECT','description', 'FROM', 'tasks', 'WHERE', 'userid', '=', '2', ';');
echo implode(" ",$arr);
?>
then you add the backticks and comma after
ἔρως
ἔρως9mo ago
imagine this you have to use the select method write, in pseudocode, how you would you call it (wrong or right, doesn't matter) and how you would process the result imagine it's just a list of ids from the users
MD
MDOP9mo ago
just a general select works like this must have keywords SELECT, FROM, and possibly where
ἔρως
ἔρως9mo ago
that's not what i asked
MD
MDOP9mo ago
well that approach isnt working for me i need to understand how select actually works
ἔρως
ἔρως9mo ago
im trying to understand how you think that the function is meant to work
MD
MDOP9mo ago
there are two must have keywords and one optional then check for one or more columns and report an error if 0 are present, then you check for a table name after from being present or more than one then you check for WHERE clause and if there is then check the condition otherwise either an error or no WHERE clause was needed so implode takes in an array and returns a string so then you check the string out unless you wanna loop through the array then implode you can use str_contains to check for must needed words like SELECT and FROM then WHERE is optional after that you can check the inner workings where you look at the columns and such
ἔρως
ἔρως9mo ago
can you put all that into code or pseudo-code?
MD
MDOP9mo ago
here's a start
private function queryCheck(array $queryAsArray) {
for($i = 0; $i < count($queryAsArray); $i++) {
if(str_contains($queryAsArray[$i], "SELECT")) {
return true;
}

if(str_contains($queryAsArray[$i], "FROM")) {
return true;
}
}
}
private function queryCheck(array $queryAsArray) {
for($i = 0; $i < count($queryAsArray); $i++) {
if(str_contains($queryAsArray[$i], "SELECT")) {
return true;
}

if(str_contains($queryAsArray[$i], "FROM")) {
return true;
}
}
}
but since there's a precise order they need to be in
ἔρως
ἔρως9mo ago
what's that function for?
MD
MDOP9mo ago
if($queryAsArray[0] == "SELECT") {
return true;
}
if($queryAsArray[0] == "SELECT") {
return true;
}
ἔρως
ἔρως9mo ago
yeah, but why?
MD
MDOP9mo ago
private function queryFormatter(array $queryAsArray) {

if($queryAsArray[0] == "SELECT") {
return true;
}

// then check for columns


// check for FROM

// check tables

// check if WHERE is there

// complete query with semicolon

// create final string

// return correct string


}
private function queryFormatter(array $queryAsArray) {

if($queryAsArray[0] == "SELECT") {
return true;
}

// then check for columns


// check for FROM

// check tables

// check if WHERE is there

// complete query with semicolon

// create final string

// return correct string


}
here's the correct logic thing is there could extremely high number of columns (to the point of out of memory for the database)
ἔρως
ἔρως9mo ago
so, i want to use your class to get data from the database now, what do i do?
MD
MDOP9mo ago
no clue im just spinning my wheels now it should be check for a connection, use the appropirate query method, then execute it
ἔρως
ἔρως9mo ago
yes, so, i want to use the select method now what?
MD
MDOP9mo ago
it should create a proper string then pass it into an sql statement then prepare it then execute it then yield the result for the caller
ἔρως
ἔρως9mo ago
which arguments do i pass to the function?
MD
MDOP9mo ago
key ones is SELECT, FROM with WHERE being optional
ἔρως
ἔρως9mo ago
how would it look like in code?
MD
MDOP9mo ago
well SELECT should be first element so
if($table[0] == "SELECT") {...}
if($table[0] == "SELECT") {...}
then columns after could be a ton so that's another array imploding it with comma separators then check FROM then array for tables implode them with comma as separator then check if the keyword WHERE is after them if not then it's done otherwise you check for the clause after the keyword WHERE and if it has something = something or whatever it could be you would technically loop through the array until you reach the word FROM when checking columns
ἔρως
ἔρως9mo ago
i want to use your class to get a list of users' names and ids. how do i implement your class and the select method, in pseudo-code?
MD
MDOP9mo ago
idk im just implementing select from scratch
ἔρως
ἔρως9mo ago
but that's what you need to think about you will use it you're writting it how do you want it to work?
MD
MDOP9mo ago
private function selectQueryFormatter(array $queryAsArray) : string|bool {

if($queryAsArray[0] == "SELECT") {
$columns = array();
// then check for columns
for($i = 0; $i < count($queryAsArray); $i++) {
if($queryAsArray[$i] == "FROM") {
break;
}
array_push($queryAsArray[$i]);
}


$columns = implode(",",$columns);



// check for FROM
} else {
return false;
}



// check for FROM

// check tables

// check if WHERE is there

// complete query with semicolon

// create final string

// return correct string


}
private function selectQueryFormatter(array $queryAsArray) : string|bool {

if($queryAsArray[0] == "SELECT") {
$columns = array();
// then check for columns
for($i = 0; $i < count($queryAsArray); $i++) {
if($queryAsArray[$i] == "FROM") {
break;
}
array_push($queryAsArray[$i]);
}


$columns = implode(",",$columns);



// check for FROM
} else {
return false;
}



// check for FROM

// check tables

// check if WHERE is there

// complete query with semicolon

// create final string

// return correct string


}
that is a start so far
ἔρως
ἔρως9mo ago
so, you want to pass the query bits, one by one?
MD
MDOP9mo ago
if you think about it if you're building a correct select query you need to check for SELECT first, then grab all the columns and parse them into something correctly formatted, then check for the FROM keyword then grab all the tables and format them, then check for the optional where keyword and if it exists then check for the conditions after odds are im blowing this out of proportion but im thinking in terms of "how do you make this from scratch"
ἔρως
ἔρως9mo ago
im thinking of "i want to use this thing that md wrote. wth do i need to do to get my data?"
MD
MDOP9mo ago
at this point idk i there's if there's an easier way I dont see it
ἔρως
ἔρως9mo ago
there's no easier or harder way there's what you want
MD
MDOP9mo ago
well do you see an easy solution to this? it feels like im basically recreating the SELECT method from scratch which feels a bit too much for this scale of project
ἔρως
ἔρως9mo ago
you're creating something, and im not sure what
MD
MDOP9mo ago
idk either might just quit
ἔρως
ἔρως9mo ago
or just think about it do you want to write the queries manually all the time?
MD
MDOP9mo ago
not really but people can feed you the wrong info and cause trouble
ἔρως
ἔρως9mo ago
how about you take the existing function and change it to take the name of the table and a list of columns?
MD
MDOP9mo ago
ok?
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {

}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {

}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
how does this work exactly
ἔρως
ἔρως9mo ago
$sql->select('users') that's it
MD
MDOP9mo ago
no way
ἔρως
ἔρως9mo ago
that's what you currently have
MD
MDOP9mo ago
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$sql->select('users');
}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$sql->select('users');
}

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
ἔρως
ἔρως9mo ago
no! that's what you use to run that function
MD
MDOP9mo ago
sorry epic im just not following on this problem can you explain what im not seeing i feel im doing way too much and this is just a todo app not a SQL ORM let's break the problem down
ἔρως
ἔρως9mo ago
if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$sql->select('users');
}
if (is_string($table)) {
$sql = "SELECT * FROM {$table}";
} else {
$sql->select('users');
}
you don't need the if if you only receive a string from this and the columns are the next argument
MD
MDOP9mo ago
ok now we have this
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

$sql = "SELECT * FROM {$table}";
$sql->select('users');

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
protected function select(string|array $table)
{
if (empty($table)) {
throw new ValueError("Select statement must not be empty");
}

$sql = "SELECT * FROM {$table}";
$sql->select('users');

$stmt = self::$pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
yield $row;
}
}
ἔρως
ἔρως9mo ago
$sql = "SELECT * FROM {$table}"; <-- just this
MD
MDOP9mo ago
ok got it
ἔρως
ἔρως9mo ago
now, the columns
MD
MDOP9mo ago
so we would use a loop to check the columns til you hit the word FROM?
ἔρως
ἔρως9mo ago
no! you take an array with the name of the columns
MD
MDOP9mo ago
how would you do that?
ἔρως
ἔρως9mo ago
an implode
MD
MDOP9mo ago
so $columnNames = implode(" ", $table); ?
ἔρως
ἔρως9mo ago
it has to be proper sql
MD
MDOP9mo ago
so just comma?
ἔρως
ἔρως9mo ago
comma and backticks
MD
MDOP9mo ago
backticks surrounding the comma? wait
$columnNames = implode("``,", $table);
$columnNames = implode("``,", $table);
ἔρως
ἔρως9mo ago
no, the backticks have to be around the column names
MD
MDOP9mo ago
$columnNames = implode("``", $table);
$columnNames = implode("``", $table);
at least that does this
SELECT``description``FROM``tasks``WHERE``userid``=``2``;
SELECT``description``FROM``tasks``WHERE``userid``=``2``;
ἔρως
ἔρως9mo ago
backtick, comma, backtick
MD
MDOP9mo ago
got it
SELECT`,`description`,`FROM`,`tasks`,`WHERE`,`userid`,`=`,`2`,`;
SELECT`,`description`,`FROM`,`tasks`,`WHERE`,`userid`,`=`,`2`,`;
example of what it produces using $columnNames = implode(",", $table);
ἔρως
ἔρως9mo ago
almost there
MD
MDOP9mo ago
what's next yes backticks are there just discord problem
ἔρως
ἔρως9mo ago
next, you need to implement that into the function
MD
MDOP9mo ago
implement what? removing things and formatting it? friend wants to game may have to do this tomorrow
ἔρως
ἔρως9mo ago
receiving the columns
MD
MDOP9mo ago
What if I just do something like this
public function query($sql, $params=[]) {
$stmt = self::$pdo->prepare($sql);
$stmt->execute($params);
return $stmt;
}
public function query($sql, $params=[]) {
$stmt = self::$pdo->prepare($sql);
$stmt->execute($params);
return $stmt;
}
Make a generic setup then have the other specific ones call on it i came up with this on my own
protected function select($columns, $table, $optionalWhere="", $optionalWhereClause="", $params=[]) {
$sql = "SELECT {$columns} FROM {$table}";
if($optionalWhere == "WHERE") {
$sql = $sql . $optionalWhere . $optionalWhereClause;
}

$stmt = self::$pdo->prepare($sql);
if(empty($params)) {
$stmt->execute();
} else {
$stmt->execute($params);
}

while($row = $stmt->fetch()) {
yield $row;
}
}
protected function select($columns, $table, $optionalWhere="", $optionalWhereClause="", $params=[]) {
$sql = "SELECT {$columns} FROM {$table}";
if($optionalWhere == "WHERE") {
$sql = $sql . $optionalWhere . $optionalWhereClause;
}

$stmt = self::$pdo->prepare($sql);
if(empty($params)) {
$stmt->execute();
} else {
$stmt->execute($params);
}

while($row = $stmt->fetch()) {
yield $row;
}
}
here's all ive done so far
public function query($sql, $params=[]) {
$stmt = self::$pdo->prepare($sql);
$stmt->execute($params);
return $stmt;
}

protected function selectAll($table, $params=[]) {
$sql = "SELECT * FROM `{$table}`";
$stmt = $this->query($sql, $params);

while($row = $stmt->fetch()) {
yield $row;
}
}
public function query($sql, $params=[]) {
$stmt = self::$pdo->prepare($sql);
$stmt->execute($params);
return $stmt;
}

protected function selectAll($table, $params=[]) {
$sql = "SELECT * FROM `{$table}`";
$stmt = $this->query($sql, $params);

while($row = $stmt->fetch()) {
yield $row;
}
}
protected function update($columnsAndValues, $table, $condition, $params) {
$sql = "UPDATE `{$table}` SET `{$columnsAndValues}` WHERE `{$condition}`;";

$stmt = self::$pdo->prepare($sql);

$stmt->bindParam($params);

if($stmt->execute()) {
echo "{$table} is successfully updated!";
} else {
throw new Exception("Failed to update {$table}");
}
}

protected function delete($table, $condition, $params) {
$sql = "DELETE FROM {$table} WHERE {$condition}";
$stmt = self::$pdo->prepare($sql);
$stmt->bindParam($params);

if($stmt->execute()) {
echo "record(s) was deleted successfully";
} else {
throw new Exception("Record(s) has failed to delete");
}
}
protected function update($columnsAndValues, $table, $condition, $params) {
$sql = "UPDATE `{$table}` SET `{$columnsAndValues}` WHERE `{$condition}`;";

$stmt = self::$pdo->prepare($sql);

$stmt->bindParam($params);

if($stmt->execute()) {
echo "{$table} is successfully updated!";
} else {
throw new Exception("Failed to update {$table}");
}
}

protected function delete($table, $condition, $params) {
$sql = "DELETE FROM {$table} WHERE {$condition}";
$stmt = self::$pdo->prepare($sql);
$stmt->bindParam($params);

if($stmt->execute()) {
echo "record(s) was deleted successfully";
} else {
throw new Exception("Record(s) has failed to delete");
}
}
ἔρως
ἔρως9mo ago
as long as it works for you, but $optionalWhere="", in the select is absolutely useless
MD
MDOP9mo ago
Right now setting up for testing isn't working I may have to build a router
ἔρως
ἔρως9mo ago
you do need one
MD
MDOP9mo ago
Whenever I run it nothing happens I even tried running a smaller scale todo app and all that happens is phpinfo displays in my console
ἔρως
ἔρως9mo ago
because you need a router
MD
MDOP9mo ago
I'll check Google
ἔρως
ἔρως9mo ago
if you want to use an already built one, you can use klein but there's more modern ones in packagist
MD
MDOP9mo ago
Oh?
ἔρως
ἔρως9mo ago
it makes things a lot easier
MD
MDOP9mo ago
what's a good setup for apache to use php?
ἔρως
ἔρως9mo ago
linux - any linux
MD
MDOP9mo ago
well yes 🙂 but configuration side for it to work
ἔρως
ἔρως9mo ago
the defauls work perfectly fine
MD
MDOP9mo ago
creating a virtual host is one thing i know of well see on nix I have to setup the config for it to work likewise docker
ἔρως
ἔρως9mo ago
yeah, that's something i don't know how to help you wish i usually install debian, apache and php then it's ready
MD
MDOP9mo ago
yeah sadly i dont have that option
ἔρως
ἔρως9mo ago
it's the easiest you can try docker and just use the bitnami image
MD
MDOP9mo ago
bitnami? the vmware images
ἔρως
ἔρως9mo ago
no, the docker images
MD
MDOP9mo ago
yeah which ones?
ἔρως
ἔρως9mo ago
there's php, apache and mariadb
MD
MDOP9mo ago
ok so bitnami php, and apache for my project wait they use fpm php so apache doesnt
ἔρως
ἔρως9mo ago
apache can use fpm or modphp
MD
MDOP9mo ago
oh cool
ἔρως
ἔρως9mo ago
i prefer modphp because it's easier
MD
MDOP9mo ago
Modphp
ἔρως
ἔρως9mo ago
yes, it's a module for apache that runs php for you
MD
MDOP9mo ago
gonna try getting apache working first so i can see it working then going move back to main todo app I GOT IT TO WORK!!!! apache is working!
MD
MDOP9mo ago
No description
MD
MDOP9mo ago
test 1 worked and now we got the first error Fatal error: Uncaught Error: Class "MD\controller\TodoController" not found in /var/www/html/app/view/todos/index.php:5 Stack trace: #0 {main} thrown in /var/www/html/app/view/todos/index.php on line 5 so does namespaces only work with the composer autloader? @ἔρως I noticed that namespaces can be used with importing but it doesn't seem very concrete issues with "finding files" come up often guessing the layers to it aren't very big so mainly stuff in same folder
Want results from more Discord servers?
Add your server