ignace
-
Posts
6,457 -
Joined
-
Last visited
-
Days Won
26
Posts posted by ignace
-
-
- It's good practice to assume that if data was entered by a user it is dangerous and should be validated and not be input directly into any strings ("INSERT INTO users (username) VALUE('$username')") or in any HTML (<span><?= $username ?></span>) without validating it thoroughly.
-
What do you mean with constants? Do you mean define('SOME_CONSTANT', 'some value'); or are you referring to the 'AND', 'OR' in my example?
- @param, @return are for documentation, you can learn more about them here: https://docs.phpdoc.org/3.0/guide/references/phpdoc/basic-syntax.html#basic-syntax
-
It's a new feature from PHP 8.0+ where you can define the type of a variable.
- In previous versions you would write function foo($bar) when you declared a function. Which meant a lot of boilerplate code inside your functions, now you can just write function foo(string $bar) and you know it's a string.
-
The general rule of thumb is that if the value will not be changed after it's been set initially in your application, use a constant, otherwise use a variable.
- So your DB hostname, port, username and password are gonna be set at the start of your application, and then nothing should be able to change these values.
The first block loops over all fields and then tests each field against a list of known field names, if a match is found, it executes the case statement.
The second block I define the default return value.
The third block I check if the $where variable is empty, it's the same as if (empty($where))
The fourth block I check if there is a result, if there was no result, there is no point in doing a SELECT count(*) query as the count would be 0, which is the same as the default set previously.
I highly recommend you view this video on youtube. It does a great job of explaining everything in depth.
-
<?php //TEMPLATE. //FULLY WORKING! //COMPLETE! //mysqli_stmt_store_result(). //mysqli_stmt_free_result(). //$rows_count = mysqli_stmt_num_rows($stmt). //mysqli_stmt_get_result(). //http_build_query(). //Report Error. ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); //Valid $_GET Items. //$tables = array('spidered_web_index','$submitted_web_index','$items_listings','visiting_history','following_history'); $spidered_web_index = ['id', 'date_and_time', 'domain', 'url', 'title', 'header', 'meta_keyword', 'meta_description', 'keyword', 'keyphrase']; $submitted_web_index = ['id', 'date_and_time', 'domain', 'url', 'title', 'description', 'country', 'keyword', 'keyphrase']; $items_listings = ['id', 'date_and_time', 'item', 'brand', 'manufacturer', 'model', 'heading', 'year', 'description', 'price', 'country', 'keyword', 'keyphrase']; $visiting_history = ['id', 'date_and_time', 'searcher', 'domain', 'url', 'title', 'header', 'description', 'country', 'meta_keyword', 'meta_description', 'keyword', 'keyphrase']; $following_history = ['id', 'date_and_time', 'searcher', 'follower', 'domain', 'url', 'title', 'header', 'description', 'country', 'meta_keyword', 'meta_description', 'keyword', 'keyphrase']; $tables = [ 'spidered_web_index' => [ 'id' => 'ID', 'date_and_time' => 'Date & Time', 'domain' => 'Domain', 'domain_point' => 'Domain Point', 'url' => 'Url', 'url' => 'Url Point', 'title' => 'Title', 'title' => 'Title Point', 'heading_1' => 'Heading 1', 'heading_1_point' => 'Heading 1 Point', 'heading_2' => 'Heading 2', 'heading_2_point' => 'Heading 2 Point', 'heading_3' => 'Heading 3', 'heading_3_point' => 'Heading 3 Point', 'heading_4' => 'Heading 4', 'heading_4_point' => 'Heading 4 Point', 'heading_5' => 'Heading 5', 'heading_5_point' => 'Heading 5 Point', 'heading_6' => 'Heading 6', 'heading_6_point' => 'Heading 6 Point', 'keyword_superscript' => 'Keyword Superscript', 'keyword_superscript' => 'Keyword superscript', 'keyword_strong' => 'Keyword Strong', 'keyword_strong' => 'Keyword Strong', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_bold' => 'Keyword Bold', 'keyword_bold' => 'Keyword Bold', 'keyword_italic' => 'Keyword Italic', 'keyword_italic' => 'Keyword Italic', 'keyword_marked' => 'Keyword Marked', 'keyword_marked' => 'Keyword Marked', 'keyword_inserted' => 'Keyword Inserted', 'keyword_inserted' => 'Keyword Inserted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_small' => 'Keyword Small', 'keyword_small' => 'Keyword Small', 'keyword_subscript' => 'Keyword Subscript', 'keyword_subscript' => 'Keyword Subscript', ], 'submitted_web_index' => [ 'id' => 'ID', 'date_and_time' => 'Date & Time', 'domain' => 'Domain', 'domain_point' => 'Domain Point', 'url' => 'Url', 'url' => 'Url Point', 'title' => 'Title', 'title' => 'Title Point', 'heading_1' => 'Heading 1', 'heading_1_point' => 'Heading 1 Point', 'heading_2' => 'Heading 2', 'heading_2_point' => 'Heading 2 Point', 'heading_3' => 'Heading 3', 'heading_3_point' => 'Heading 3 Point', 'heading_4' => 'Heading 4', 'heading_4_point' => 'Heading 4 Point', 'heading_5' => 'Heading 5', 'heading_5_point' => 'Heading 5 Point', 'heading_6' => 'Heading 6', 'heading_6_point' => 'Heading 6 Point', 'keyword_superscript' => 'Keyword Superscript', 'keyword_superscript' => 'Keyword superscript', 'keyword_strong' => 'Keyword Strong', 'keyword_strong' => 'Keyword Strong', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_bold' => 'Keyword Bold', 'keyword_bold' => 'Keyword Bold', 'keyword_italic' => 'Keyword Italic', 'keyword_italic' => 'Keyword Italic', 'keyword_marked' => 'Keyword Marked', 'keyword_marked' => 'Keyword Marked', 'keyword_inserted' => 'Keyword Inserted', 'keyword_inserted' => 'Keyword Inserted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_small' => 'Keyword Small', 'keyword_small' => 'Keyword Small', 'keyword_subscript' => 'Keyword Subscript', 'keyword_subscript' => 'Keyword Subscript', ], 'links_crawls_drummin' => [ 'id' => "ID", 'date_and_time' => "Date and Time", 'domain' => "Domain", 'domain_point' => "Domain Point", 'url' => "URL", 'url_point' => "Url Point", 'title' => "Title", 'title_point' => "Title Point", 'header' => "Header", 'header_point' => "Header Point", 'kw_1' => "Keyword 1", 'kw_1_point' => "Keyword 1 Point", 'kw_2' => "Keyword 2", 'kw_2_point' => "Keyword 2 Point", 'kw_3' => "Keyword 3", 'kw_3_point' => "Keyword 3 Point", 'kw_4' => "Keyword 4", 'kw_4_point' => "Keyword 4 Point", 'description' => "Description", 'description_point' => "Description Point", ], 'links_submits_drummin' => [ 'id' => "ID", 'date_and_time' => "Date and Time", 'domain' => "Domain", 'domain_point' => "Domain Point", 'url' => "URL", 'url_point' => "Url Point", 'title' => "Title", 'title_point' => "Title Point", 'header' => "Header", 'header_point' => "Header Point", 'kw_1' => "Keyword 1", 'kw_1_point' => "Keyword 1 Point", 'kw_2' => "Keyword 2", 'kw_2_point' => "Keyword 2 Point", 'kw_3' => "Keyword 3", 'kw_3_point' => "Keyword 3 Point", 'kw_4' => "Keyword 4", 'kw_4_point' => "Keyword 4 Point", 'description' => "Description", 'description_point' => "Description Point", ], ]; //Extract $_GETs. $table = !empty($_POST['tbl']) ? $_POST['tbl'] : (!empty($_GET['tbl']) ? $_GET['tbl'] : 'spidered_web_index'); $column = !empty($_POST['col']) ? $_POST['col'] : (!empty($_GET['col']) ? $_GET['col'] : 'id'); $limit = !empty($_POST['lmt']) ? $_POST['lmt'] : (!empty($_GET['lmt']) ? $_GET['lmt'] : 1); $match = !empty($_POST['mtch']) ? $_POST['mtch'] : (!empty($_GET['mtch']) ? $_GET['mtch'] : 'fuzzy'); $search = !empty($_POST['srch']) ? $_POST['srch'] : (!empty($_GET['srch']) ? $_GET['srch'] : 'mobile'); $page = !empty($_GET['pg']) ? intval($_GET['pg']) : 1; $offset = $page * $limit - $limit; $headings = array_values($tables[$table]); $columns = array_keys($tables[$table]); echo $columns_count = count($columns); if (!in_array($column, $columns)) { die('Invalid Column!'); } if (!in_array($tables[$table], $tables)) { die('Invalid Table!'); } $search = !empty($search) && $match === 'fuzzy' ? str_replace('*', '%', $search) : $search; $char_types = str_repeat('s', $columns_count); $search_values = array_fill(0, $columns_count, $search); $comparator = $match === 'exact' ? '=' : 'LIKE'; $sql = "SELECT * FROM $table WHERE " . implode(" $comparator ? OR ", $columns) . " $comparator ? "; $sql .= "ORDER BY id DESC LIMIT $limit OFFSET $offset"; //Query DB. mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $conn = mysqli_connect("localhost", "root", "", "buzz"); //mysqli_connect("server","user","password","db"); mysqli_set_charset($conn, 'utf8mb4'); if (mysqli_connect_errno()) { printf("Mysqli Connection Error: %s", mysqli_connect_error()); } $stmt = mysqli_stmt_init($conn); if (!mysqli_stmt_prepare($stmt, $sql)) { //Fetch Rows based on Row Limit per page. echo 'Mysqli Error: ' . mysqli_stmt_error($stmt); echo '<br>'; echo 'Mysqli Error No: ' . mysqli_stmt_errno($stmt); } else { mysqli_stmt_bind_param($stmt, $char_types, ...$search_values); mysqli_stmt_execute($stmt); mysqli_stmt_store_result($stmt); //Necessary to use with mysqli_stmt_num_rows() when SQL query is SELECT. echo '<br><br>'; echo '<br><br>'; echo '<br><br>'; echo '<br><br>'; //Fetch Matching Rows Count. //mysqli_stmt_num_rows() has to come after mysqli_stmt_store_result(). echo 'Total Result: ' . ($rows_count = mysqli_stmt_num_rows($stmt)); echo '<br><br>'; mysqli_stmt_free_result($stmt); //Is this really necessary ? if (!mysqli_stmt_prepare($stmt, $sql)) { //Fetch Rows based on Row Limit per page. echo 'Mysqli Error: ' . mysqli_stmt_error($stmt); echo '<br>'; echo 'Mysqli Error No: ' . mysqli_stmt_errno($stmt); } else { mysqli_stmt_bind_param($stmt, $char_types, ...$search_values); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); mysqli_stmt_free_result($stmt); //Is this really necessary ? while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) { for ($i = 0; $i < $columns_count; $i++) { echo '<b>' . $headings[$i] . ': </b>' . $row[$columns[$i]]; echo '<br>'; } } } } mysqli_stmt_close($stmt); mysqli_close($conn); echo 'Total Pages: ' . ($total_pages = ceil($rows_count / $limit)); echo '<br><br>'; $i = 0; while ($i < $total_pages) { $i++; $pagination_section_array = ["tbl" => "$table", "mtch" => "$match", "lmt" => "$limit", "srch" => "$search", "pg" => intval($i)]; $serps_url = $_SERVER['PHP_SELF'] . '?' . http_build_query($pagination_section_array, '', '&'); //Did convert '&' to '&' and so NO need to add htmlspecialchars() when echoing link. if ($i == $page) { echo '<a href="' . $serps_url . '">' . "<b>$i</b>" . '</a>'; //No need to add htmlspecialchars(), to convert '&' to '&', when echoing link here. } else { echo '<a href="' . $serps_url . '">' . "$i" . '</a>'; //No need to add htmlspecialchars(), to convert '&' to '&', when echoing link here. } } echo '<br>'; ?>
To improve this script, the first thing I would do is cut out the large arrays. So I would create a new directory called config and create a new file tables.php to hold the table configuration, like this:
search-engine-project/ ├─ config/ │ ├─ tables.php
Then in tables.php I would paste the large array:
<?php return [ 'spidered_web_index' => [ 'id' => 'ID', 'date_and_time' => 'Date & Time', 'domain' => 'Domain', 'domain_point' => 'Domain Point', 'url' => 'Url', 'url' => 'Url Point', 'title' => 'Title', 'title' => 'Title Point', 'heading_1' => 'Heading 1', 'heading_1_point' => 'Heading 1 Point', 'heading_2' => 'Heading 2', 'heading_2_point' => 'Heading 2 Point', 'heading_3' => 'Heading 3', 'heading_3_point' => 'Heading 3 Point', 'heading_4' => 'Heading 4', 'heading_4_point' => 'Heading 4 Point', 'heading_5' => 'Heading 5', 'heading_5_point' => 'Heading 5 Point', 'heading_6' => 'Heading 6', 'heading_6_point' => 'Heading 6 Point', 'keyword_superscript' => 'Keyword Superscript', 'keyword_superscript' => 'Keyword superscript', 'keyword_strong' => 'Keyword Strong', 'keyword_strong' => 'Keyword Strong', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_bold' => 'Keyword Bold', 'keyword_bold' => 'Keyword Bold', 'keyword_italic' => 'Keyword Italic', 'keyword_italic' => 'Keyword Italic', 'keyword_marked' => 'Keyword Marked', 'keyword_marked' => 'Keyword Marked', 'keyword_inserted' => 'Keyword Inserted', 'keyword_inserted' => 'Keyword Inserted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_small' => 'Keyword Small', 'keyword_small' => 'Keyword Small', 'keyword_subscript' => 'Keyword Subscript', 'keyword_subscript' => 'Keyword Subscript', ], 'submitted_web_index' => [ 'id' => 'ID', 'date_and_time' => 'Date & Time', 'domain' => 'Domain', 'domain_point' => 'Domain Point', 'url' => 'Url', 'url' => 'Url Point', 'title' => 'Title', 'title' => 'Title Point', 'heading_1' => 'Heading 1', 'heading_1_point' => 'Heading 1 Point', 'heading_2' => 'Heading 2', 'heading_2_point' => 'Heading 2 Point', 'heading_3' => 'Heading 3', 'heading_3_point' => 'Heading 3 Point', 'heading_4' => 'Heading 4', 'heading_4_point' => 'Heading 4 Point', 'heading_5' => 'Heading 5', 'heading_5_point' => 'Heading 5 Point', 'heading_6' => 'Heading 6', 'heading_6_point' => 'Heading 6 Point', 'keyword_superscript' => 'Keyword Superscript', 'keyword_superscript' => 'Keyword superscript', 'keyword_strong' => 'Keyword Strong', 'keyword_strong' => 'Keyword Strong', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_emphasised' => 'Keyword Emphasised', 'keyword_bold' => 'Keyword Bold', 'keyword_bold' => 'Keyword Bold', 'keyword_italic' => 'Keyword Italic', 'keyword_italic' => 'Keyword Italic', 'keyword_marked' => 'Keyword Marked', 'keyword_marked' => 'Keyword Marked', 'keyword_inserted' => 'Keyword Inserted', 'keyword_inserted' => 'Keyword Inserted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_deleted' => 'Keyword Deleted', 'keyword_small' => 'Keyword Small', 'keyword_small' => 'Keyword Small', 'keyword_subscript' => 'Keyword Subscript', 'keyword_subscript' => 'Keyword Subscript', ], 'links_crawls_drummin' => [ 'id' => "ID", 'date_and_time' => "Date and Time", 'domain' => "Domain", 'domain_point' => "Domain Point", 'url' => "URL", 'url_point' => "Url Point", 'title' => "Title", 'title_point' => "Title Point", 'header' => "Header", 'header_point' => "Header Point", 'kw_1' => "Keyword 1", 'kw_1_point' => "Keyword 1 Point", 'kw_2' => "Keyword 2", 'kw_2_point' => "Keyword 2 Point", 'kw_3' => "Keyword 3", 'kw_3_point' => "Keyword 3 Point", 'kw_4' => "Keyword 4", 'kw_4_point' => "Keyword 4 Point", 'description' => "Description", 'description_point' => "Description Point", ], 'links_submits_drummin' => [ 'id' => "ID", 'date_and_time' => "Date and Time", 'domain' => "Domain", 'domain_point' => "Domain Point", 'url' => "URL", 'url_point' => "Url Point", 'title' => "Title", 'title_point' => "Title Point", 'header' => "Header", 'header_point' => "Header Point", 'kw_1' => "Keyword 1", 'kw_1_point' => "Keyword 1 Point", 'kw_2' => "Keyword 2", 'kw_2_point' => "Keyword 2 Point", 'kw_3' => "Keyword 3", 'kw_3_point' => "Keyword 3 Point", 'kw_4' => "Keyword 4", 'kw_4_point' => "Keyword 4 Point", 'description' => "Description", 'description_point' => "Description Point", ], ];
Then back to our main script I would then include the configuration like this:
<?php ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); $items_listings = ['id', 'date_and_time', 'item', 'brand', 'manufacturer', 'model', 'heading', 'year', 'description', 'price', 'country', 'keyword', 'keyphrase']; $visiting_history = ['id', 'date_and_time', 'searcher', 'domain', 'url', 'title', 'header', 'description', 'country', 'meta_keyword', 'meta_description', 'keyword', 'keyphrase']; $following_history = ['id', 'date_and_time', 'searcher', 'follower', 'domain', 'url', 'title', 'header', 'description', 'country', 'meta_keyword', 'meta_description', 'keyword', 'keyphrase']; // refactored part $tables = require 'config/tables.php'; $spidered_web_index = array_keys($tables['spidered_web_index']); $submitted_web_index = array_keys($tables['submitted_web_index']); // end refactored part //Extract $_GETs. $table = !empty($_POST['tbl']) ? $_POST['tbl'] : (!empty($_GET['tbl']) ? $_GET['tbl'] : 'spidered_web_index'); $column = !empty($_POST['col']) ? $_POST['col'] : (!empty($_GET['col']) ? $_GET['col'] : 'id'); $limit = !empty($_POST['lmt']) ? $_POST['lmt'] : (!empty($_GET['lmt']) ? $_GET['lmt'] : 1); $match = !empty($_POST['mtch']) ? $_POST['mtch'] : (!empty($_GET['mtch']) ? $_GET['mtch'] : 'fuzzy'); $search = !empty($_POST['srch']) ? $_POST['srch'] : (!empty($_GET['srch']) ? $_GET['srch'] : 'mobile'); $page = !empty($_GET['pg']) ? intval($_GET['pg']) : 1; $offset = $page * $limit - $limit; $headings = array_values($tables[$table]); $columns = array_keys($tables[$table]); echo $columns_count = count($columns); if (!in_array($column, $columns)) { die('Invalid Column!'); } if (!in_array($tables[$table], $tables)) { die('Invalid Table!'); } $search = !empty($search) && $match === 'fuzzy' ? str_replace('*', '%', $search) : $search; $char_types = str_repeat('s', $columns_count); $search_values = array_fill(0, $columns_count, $search); $comparator = $match === 'exact' ? '=' : 'LIKE'; $sql = "SELECT * FROM $table WHERE " . implode(" $comparator ? OR ", $columns) . " $comparator ? "; $sql .= "ORDER BY id DESC LIMIT $limit OFFSET $offset"; //Query DB. mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $conn = mysqli_connect("localhost", "root", "", "buzz"); //mysqli_connect("server","user","password","db"); mysqli_set_charset($conn, 'utf8mb4'); if (mysqli_connect_errno()) { printf("Mysqli Connection Error: %s", mysqli_connect_error()); } $stmt = mysqli_stmt_init($conn); if (!mysqli_stmt_prepare($stmt, $sql)) { //Fetch Rows based on Row Limit per page. echo 'Mysqli Error: ' . mysqli_stmt_error($stmt); echo '<br>'; echo 'Mysqli Error No: ' . mysqli_stmt_errno($stmt); } else { mysqli_stmt_bind_param($stmt, $char_types, ...$search_values); mysqli_stmt_execute($stmt); mysqli_stmt_store_result($stmt); //Necessary to use with mysqli_stmt_num_rows() when SQL query is SELECT. echo '<br><br>'; echo '<br><br>'; echo '<br><br>'; echo '<br><br>'; //Fetch Matching Rows Count. //mysqli_stmt_num_rows() has to come after mysqli_stmt_store_result(). echo 'Total Result: ' . ($rows_count = mysqli_stmt_num_rows($stmt)); echo '<br><br>'; mysqli_stmt_free_result($stmt); //Is this really necessary ? if (!mysqli_stmt_prepare($stmt, $sql)) { //Fetch Rows based on Row Limit per page. echo 'Mysqli Error: ' . mysqli_stmt_error($stmt); echo '<br>'; echo 'Mysqli Error No: ' . mysqli_stmt_errno($stmt); } else { mysqli_stmt_bind_param($stmt, $char_types, ...$search_values); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); mysqli_stmt_free_result($stmt); //Is this really necessary ? while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) { for ($i = 0; $i < $columns_count; $i++) { echo '<b>' . $headings[$i] . ': </b>' . $row[$columns[$i]]; echo '<br>'; } } } } mysqli_stmt_close($stmt); mysqli_close($conn); echo 'Total Pages: ' . ($total_pages = ceil($rows_count / $limit)); echo '<br><br>'; $i = 0; while ($i < $total_pages) { $i++; $pagination_section_array = ["tbl" => "$table", "mtch" => "$match", "lmt" => "$limit", "srch" => "$search", "pg" => intval($i)]; $serps_url = $_SERVER['PHP_SELF'] . '?' . http_build_query($pagination_section_array, '', '&'); //Did convert '&' to '&' and so NO need to add htmlspecialchars() when echoing link. if ($i == $page) { echo '<a href="' . $serps_url . '">' . "<b>$i</b>" . '</a>'; //No need to add htmlspecialchars(), to convert '&' to '&', when echoing link here. } else { echo '<a href="' . $serps_url . '">' . "$i" . '</a>'; //No need to add htmlspecialchars(), to convert '&' to '&', when echoing link here. } } echo '<br>'; ?>
Then as the second step I would cut out the pagination HTML into a separate directory (html) and file (pagination.php), like this:
search-engine-project/ ├─ config/ │ ├─ tables.php ├─ html/ │ ├─ pagination.php
In our pagination.php file:
<?php for ($i = 0; $i < $total_pages; $i++): ?> <a href="?<?= http_build_query(array_merge($pagination_section_array, ['pg' => $i]), '', '&') ?>"> <?php if ($i == $page): ?> <b> <?php endif ?> <?= $i ?> <?php if ($i == $page): ?> </b> <?php endif ?> </a> <?php endfor ?> <br>
Then back to our original script (scroll to the end):
<?php ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); $items_listings = ['id', 'date_and_time', 'item', 'brand', 'manufacturer', 'model', 'heading', 'year', 'description', 'price', 'country', 'keyword', 'keyphrase']; $visiting_history = ['id', 'date_and_time', 'searcher', 'domain', 'url', 'title', 'header', 'description', 'country', 'meta_keyword', 'meta_description', 'keyword', 'keyphrase']; $following_history = ['id', 'date_and_time', 'searcher', 'follower', 'domain', 'url', 'title', 'header', 'description', 'country', 'meta_keyword', 'meta_description', 'keyword', 'keyphrase']; $tables = require 'config/tables.php'; $spidered_web_index = array_keys($tables['spidered_web_index']); $submitted_web_index = array_keys($tables['submitted_web_index']); //Extract $_GETs. $table = !empty($_POST['tbl']) ? $_POST['tbl'] : (!empty($_GET['tbl']) ? $_GET['tbl'] : 'spidered_web_index'); $column = !empty($_POST['col']) ? $_POST['col'] : (!empty($_GET['col']) ? $_GET['col'] : 'id'); $limit = !empty($_POST['lmt']) ? $_POST['lmt'] : (!empty($_GET['lmt']) ? $_GET['lmt'] : 1); $match = !empty($_POST['mtch']) ? $_POST['mtch'] : (!empty($_GET['mtch']) ? $_GET['mtch'] : 'fuzzy'); $search = !empty($_POST['srch']) ? $_POST['srch'] : (!empty($_GET['srch']) ? $_GET['srch'] : 'mobile'); $page = !empty($_GET['pg']) ? intval($_GET['pg']) : 1; $offset = $page * $limit - $limit; $headings = array_values($tables[$table]); $columns = array_keys($tables[$table]); echo $columns_count = count($columns); if (!in_array($column, $columns)) { die('Invalid Column!'); } if (!in_array($tables[$table], $tables)) { die('Invalid Table!'); } $search = !empty($search) && $match === 'fuzzy' ? str_replace('*', '%', $search) : $search; $char_types = str_repeat('s', $columns_count); $search_values = array_fill(0, $columns_count, $search); $comparator = $match === 'exact' ? '=' : 'LIKE'; $sql = "SELECT * FROM $table WHERE " . implode(" $comparator ? OR ", $columns) . " $comparator ? "; $sql .= "ORDER BY id DESC LIMIT $limit OFFSET $offset"; //Query DB. mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $conn = mysqli_connect("localhost", "root", "", "buzz"); //mysqli_connect("server","user","password","db"); mysqli_set_charset($conn, 'utf8mb4'); if (mysqli_connect_errno()) { printf("Mysqli Connection Error: %s", mysqli_connect_error()); } $stmt = mysqli_stmt_init($conn); if (!mysqli_stmt_prepare($stmt, $sql)) { //Fetch Rows based on Row Limit per page. echo 'Mysqli Error: ' . mysqli_stmt_error($stmt); echo '<br>'; echo 'Mysqli Error No: ' . mysqli_stmt_errno($stmt); } else { mysqli_stmt_bind_param($stmt, $char_types, ...$search_values); mysqli_stmt_execute($stmt); mysqli_stmt_store_result($stmt); //Necessary to use with mysqli_stmt_num_rows() when SQL query is SELECT. echo '<br><br>'; echo '<br><br>'; echo '<br><br>'; echo '<br><br>'; //Fetch Matching Rows Count. //mysqli_stmt_num_rows() has to come after mysqli_stmt_store_result(). echo 'Total Result: ' . ($rows_count = mysqli_stmt_num_rows($stmt)); echo '<br><br>'; mysqli_stmt_free_result($stmt); //Is this really necessary ? if (!mysqli_stmt_prepare($stmt, $sql)) { //Fetch Rows based on Row Limit per page. echo 'Mysqli Error: ' . mysqli_stmt_error($stmt); echo '<br>'; echo 'Mysqli Error No: ' . mysqli_stmt_errno($stmt); } else { mysqli_stmt_bind_param($stmt, $char_types, ...$search_values); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); mysqli_stmt_free_result($stmt); //Is this really necessary ? while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) { for ($i = 0; $i < $columns_count; $i++) { echo '<b>' . $headings[$i] . ': </b>' . $row[$columns[$i]]; echo '<br>'; } } } } mysqli_stmt_close($stmt); mysqli_close($conn); echo 'Total Pages: ' . ($total_pages = ceil($rows_count / $limit)); echo '<br><br>'; $pagination_section_array = ["tbl" => "$table", "mtch" => "$match", "lmt" => "$limit", "srch" => "$search"]; // refactored part require 'html/pagination.php'; // end refactored part ?>
This way bit by bit, the code gets smaller, and easier to maintain.
- 1
-
Quote
Q1. Is it true that, I need to use mysqli_stmt_store_result($stmt) prior to using mysqli_stmt_num_rows($stmt) ?
Q2. Is it true that, I need to use mysqli_stmt_free_result($stmt) after every mysqli_stmt_store_result($stmt) ?
Q3. Anything else I need to know apart from I should use pdo ?
Q4. Is my code bad, ok, good or great ? I reckon it is ok.
Q1. yes, it needs a stored result to count the rows.
Q2. yes, it clears the stored result from the internal buffer freeing memory. It's good practice to always clean up after yourself.
Q3. either use mysqli_* or PDO, most people use PDO for its simple API. PDO has no pdo_* functions, only it's Object Oriented interface.
Q4. bad, but not in the way of ugly or "newbie" but because
- it can be exploited by hackers;
- it will be hard to maintain (lots of duplicate code, weird indents, no separation between HTML and PHP, ..);
- using die() in a script is not good UX, the user can't get back to the homepage or try another search;
- 1
-
You avoid SQL injection when you use prepared statements assuming you write the full SQL and don't inject foreign SQL using variables.
So while this is safe:
mysqli_stmt_prepare($stmt, 'SELECT * FROM users WHERE username = ?');
This is not:
mysqli_stmt_prepare($stmt, "SELECT * FROM $tbl WHERE $col_1 = ?");
Because now a hacker can control what the prepared statement is. So this could end up being:
mysqli_stmt_prepare($stmt, "SELECT * FROM users; DELETE FROM users WHERE 1 = ?");
Which is not what you intended.
--
I assume nobody told you, because I don't think anyone read over your code like I did. Walls of text do that to people. Which is why, as a programmer, we divide the program into little pieces like lego blocks. Building or re-using blocks to build a car or a house.
We even have "rules" defining how small things should be: https://refactoring.guru/refactoring/smells/bloaters
--
I assumed it was either a search engine or an SEO kinda tool. You might want to read up on cursor based pagination: https://medium.com/swlh/how-to-implement-cursor-pagination-like-a-pro-513140b65f32 as LIMIT OFFSET is not ideal for pagination (performance wise).
- 1
-
Also don't do this:
SELECT id,domain,word,phrase from $tbl WHERE $col_1 = ?
Because now it's easy for a hacker to do something like:
?col_1=1;DELETE FROM users WHERE id&input_1=1
Instead do something like:
/** * @param array{col_1: string} $fields * @param string $operator Possible values AND, OR * @param int $limit Possible value between 0 and 100, if invalid defaults to 100 * @param int $offset * * @return array{items: array, total_items: int} */ function page_search(array $fields, string $operator, int $limit, int $offset = 0): array { $where = $values = []; $operator = in_array($operator, ['OR', 'AND']) ? $operator : 'AND'; $limit = 0 < $limit && $limit < 100 ? $limit : 100; $offset = 0 <= $offset ? $offset : 0; foreach ($fields as $field => $term) { switch ($field) { case 'col_1': $where[] = 'col_1 = ?'; $values[] = $term; break; // other fields you want to allow to search on } $result = [ 'items' => [], 'total_items' => 0, ]; if ([] === $where) { return $result; } $result['items'] = db_fetch_all('SELECT * FROM some_table WHERE ' . implode($operator, $where) . " LIMIT $offset, $limit", $values); if (count($result['items'])) { // only execute a count() query if we have a result $result['total_items'] = db_fetch_column('SELECT count(*) FROM some_table WHERE ' . implode($operator, $where), $values); } return $result; }
By dividing your program into little pieces you reduce the cognitive load necessary to work on pieces of your application or to find and fix bugs.
The same goes for the database. I see you use mysqli_connect in the script with the values hardcoded. Instead you should create a file that holds your configuration values:
// bootstrap.php define('DB_HOST', 'localhost'); define('DB_USER', 'the_username'); define('DB_PASS', 'the_password'); define('DB_NAME', 'the_database'); define('DB_PORT', 3306); define('DB_CHARSET', 'utf8mb4'); define('EMERGENCY_EMAIL', '[email protected]'); // in case of fatal errors // other configuration values
Then in your database functions file:
function db_connect() { static $connection; if (null === $connection) { $connection = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT); if ($connection) { mysqli_set_charset($connection, DB_CHARSET); } } return $connection; }
All of this creates building blocks you can use to build upon further. The next step would be to create a db_fetch_all function that uses db_connect to get the active DB connection etc...
- 1
-
3 minutes ago, TheStudent2023 said:
Sorry. Did not really understand the structure or the code that much. This is adv stuff. Right ? Not intermediate, let alone beginner level. Yes ?
Actually this is beginner level. You start with a directory structure something like:
/functions /html index.php search.php register.php bootstrap.php
Where index.php, search.php, register.php are the files users will visit like http://website.com/search.php and http://website.com/register.php
Inside search.php and register.php the code will look something like:
// defines constants like DB_USER, DB_PASS, DB_HOST, .. so you only need to change this in one place require __DIR__ . '/bootstrap.php'; // load functions you need require APP_PROJECT_DIR . '/functions/page_functions.php'; require APP_PROJECT_DIR . '/functions/search_functions.php'; // do the work // load the HTML require APP_PROJECT_DIR . '/html/search_view.php';
Using this way of working you avoid having a big wall of text and give each file their own responsibility.
- 1
-
As for your question on how the pros do it, this is how procedural programmers structure their project:
- load any required files
- get the inputs and validate them and take appropriate action if they are invalid
- do something with the input
- report back to the user
A popular pattern found online is Controller-Model-View. Where your controller is your php file (index.php, search.php, register.php), and the view is your HTML. Your model are the necessary functions you execute to do the thing.
Take for example a search:
Your controller:
// search.php // // Some description // // Arguments: // q: the search query // page: the page number, defaults to 1 // Supports: // GET, HEAD // Usage: // search.php?q=some+query&page=1 require __DIR__ . '/bootstrap.php'; require APP_PROJECT_DIR . '/lib/search_functions.php'; require APP_PROJECT_DIR . '/lib/page_functions.php'; $term = get_search_term(); if (empty($term)) { redirect('/to/the/search/form'); } // more validation here $page = get_page(); $search_results = do_page_search($term, $page, 30 /* results per page */); // $search_results contains: // [items] // [total_items] // [total_pages] // [items_per_page] // [current_page] // everything is rendered in a view require APP_PROJECT_DIR . '/views/search_results.php';
Then the result is passed off to the view:
<?php // views/search_results.php // // Description // require APP_PROJECT_DIR . '/views/header.php'; ?> <div class="search-results"> <?php foreach ($search_results as $search_result): ?> ... <?php endforeach ?> </div> <div class="pagination"> <?php // uses total_items, total_pages, current_page, items_per_page because these are abstract names and can be reused require APP_PROJECT_DIR . '/views/pagination.php'; ?> </div> <?php require APP_PROJECT_DIR . '/views/footer.php'; ?>
-
Hi,
It won't take you 6 years to understand OOP. OOP is about ownership and responsibility. Procedural is about functionality.
From a procedural point of view a user is an array with certain fields like id, username, password, ... Any function can freely use, modify, or even remove these fields as they see fit.
Nobody owns the data, and so anything can do to it what it wants.
In OOP you assign a class with the ownership and responsibility of this data:
class User { private ?int $id = null; private string $username; private string $password; }
In the above example the User class owns the data, and doesn't allow anyone to touch it. Not very useful.
class User { private ?int $id = null; private string $username; private string $password; public function changeUsername(string $username) { assertLength($username, 8); assertNotProfane($username); assertNotSameAsFirstOrLastName($username); assertNotSameAsEmail($username); $this->username = $username; } }
Now the User class allows changing the username, if ALL requirements are met. Otherwise it will throw an Exception.
Exceptions are a big part of OOP, it ensures your code follows a controlled path, like an assembly line, any bad items are cast to a different line.
The advantage of encapsulating data is that there is only one place that controls access to the data, and not hundreds of places.
So that if your requirements change, you can make the change in one place and not spend weeks tracking down all uses.
I am not saying you should switch to OOP, just that it won't take you 6 years as the concept is quite simple.
- 1
-
Thanks @oni-kun. Good observation about the pattern. I will look into that.
-
Thank you @requinix. I am afraid it probably is the last one.
-
9M[bfv�����������"2<I=0L_nt���������+8@,IOPG:e~��������������$0<(4@LXdp|���������� ,8$0<HT`lx������ �63PQWa,MFk^����ʺ�������$08(4@LXdp|���������� ,8$0<HT`lx��������2D=ZWtU[ePrj������������$0<(4<LXdp|���������� ,8$0<HT`lx����������=Vha^[xy�t�����������$0<(4@LX`p|���������� ,8$0<HT`lx�����������(0BG:x��������������$0<(4@LXdp|���������� ,8$0<HT`lx��������>ENI4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$08Job�������������(4<,8DP\ht�����������$0<(4@LXdp|��������#�Z`j6@<HT`lx�����������(4@,8DP\ht�����������$0<(4@LX`r����������� ,8$0<DT`lx�����������(4@,8DP\ht�����������&K>LfY\jdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht���������!�$0<(4@LXdl|���������� ,8$0<HT`lx�����������(4<.SFt�������������� 0<(4@LXdp|���������� ,8$0<HT`lx��������(B58M@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<DV{n������������(4@(8DP\ht�����������$0<(4@LXdp|���������/"Pj=BLHT`lx�����������(4@,8DP\ht�����������$0<(4@LXdl~���������� ,8$0<HP`lx�����������(4@,8DP\ht����������� 2W*Xrej|p|���������� ,8$0<HT`lx�����������(4@,8DP\ht����˾�&��($0<(4@LXdpx���������� ,8$0<HT`lx�����������(4@(:_R��������������$,<(4@LXdp|���������� ,8$0<HT`lx���������4NAEP,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HPb�z�����������(4@,4DP\ht�����������$0<(4@LXdp|���������5N`YVSpqw�l����ö��,%BOXcr`8DP\hp�����������$0<(4@LXdp|���������� ,8 Qj|u����������*8�7*nibYv}�t�����������$0<(4@LXdp|���������� ,8$0<HT`lt���������D�C6f[jx�}�������������$0<(4@LXdp|���������� ,8$0<HT`lx�������#�=+\6[.rx�mxt�����������$0<(4@LXdp|���������� ,8$0<HT`lx��������89RMHs,8DP\ht|����������$0<(4@LXdp|���������� ,8$,]k������Ϫ����89RMdo@kDP\ht|����������$0<(4@LXdp|���������� ,8$,]k������������7�7*Ulb]r��ht�����������$0<(4@LXdp|���������� ,8$0<HT`lt�����������(4@,8DPXht�����������$0<(4@LXdp|���������� (:?2v|�q�x�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(0BgZdp|���������� ,4$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DPXj������������$0<(4<LXdp|���������� ,8$0<HT`lx�����������C6@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\dt�����������$0<(4@LXdp|���������� ,4&K>HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4<Nsfp|���������� ,8 0<HT`lx�����������(4@,8DP\ht���������'�$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\dv�����������$0<(4@HXdp|���������� ,8$0<HT`lx�����������*OB,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx���ö������(4@,8DP\hp�����������$0<(4@LXdp|���������� ,8 2WJT`lx�����������$4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@HZr|���������� ,8$,<HT`lx�����������(4@,8DP\ht����������3&0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\hp�����������$0<(4@LTdp|���������� ,8$0<HT`lx�����������$6[.8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht|����������$0<(4@LXdp|���������� ,8$,>cV`lx�����������(0@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LTf�~���������� ,8$08HT`lx�����������(4@,8DP\ht�����������?2<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht|����������$0<(4@LX`p|���������� ,8$0<HT`lx�����������(0BG:DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$08Joblx�����������(4<,8DP\ht�����������$0<(4@LXdp|��������#� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LX`r����������� ,8$0<DT`lx�����������(4@,8DP\ht�����������&K>(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdl|���������� ,8$0<HT`lx�����������(4<.SFP\ht����������� 0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<DV{nx�����������(4@(8DP\ht�����������$0<(4@LXdp|���������/",8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdl~���������� ,8$0<HP`lx�����������(4@,8DP\ht����������� 2W*4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht����˾�����$0<(4@LXdpx���������� ,8$0<HT`lx�����������(4@(:_R\ht�����������$,<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HPb�z�����������(4@,4DP\ht�����������$0<(4@LXdp|����������;.8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdpx���������� ,8$0<HT\lx�����������(4@,8DP\ht�����������$,>C6@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,4Fk^ht�����������$08(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT\n������������(4@,8@P\ht�����������$0<(4@LXdp|����������"G:$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`hx�����������(4@,8DP\ht�����������$08*OBLXdp|�����������,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8@Rwjt�����������$0<$4@LXdp|���������� ,8$0<HT`lx���������+�(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`hz�����������(4@,8DL\ht�����������$0<(4@LXdp|�����������.S&0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���Ǻ����� ,8$0<HT`lt�����������(4@,8DP\ht�����������$0<$6[NXdp|���������� (8$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DL^�v�����������$0<(0@LXdp|���������� ,8$0<HT`lx����������7*4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lt�����������(4@,8DPXht�����������$0<(4@LXdp|���������� (:?2<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\ht�����������$0<(0BgZdp|���������� ,4$0<HT`lx�����������(4@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DPXj������������$0<(4<LXdp|���������� ,8$0<HT`lx�����������C6@,8DP\ht�����������$0<(4@LXdp|���������� ,8$0<HT`lx�����������(4@,8DP\dt�����������$0<(4@LXdp|���������� ,4&KWue������������+7CO[GS_kw�����������'3?KWCO[gs����������#/;GS?KWco{�����������+7CO[GS_kw�����������'3?KWCO[
Anyone recognizes the encryption used here? It's part of a file that supposedly would be the database of a program. But I can't work with it. Hoping someone of you recognizes the format.
-
I am looking for someone that can help me optimize an InnoDB/MariaDB server. There are a few issues that the current server has that I would like someone to have a look at.
If you are interested send your resume and hourly and daily rate to [email protected]
Put PHPFreaks & InnoDB in the e-mail subject.
Thank you.
-
On 8/25/2018 at 7:50 PM, Destramic said:
im trying to digest everything your saying here...so...
ok validating a form and returning error messages for a user is great...but for instance, if form data is passed to my business model after being validated would i need to validate the data again in themodel, but this time returning exceptions?
Okay, let's try something simpler. Say you have this code:
<?php declare(strict_types=1); // .. public function setAge(int $age) { $this->age = $age; }
Passing a string to this method will throw an exception. The form processing code should validate that the age is indeed a number before passing it to your setAge() method. You can't start accepting "Dave", TRUE, ['foo']['bar'] = 'bat' as age. What I said above is basically the same thing. You protect your model from invalid data.
If the business you are writing this software for has other rules, like must be over the age of 18. Then you will need to add additional checks, that his rule is not broken. And this is where you use exceptions. To halt the code from polluting your model.
So to answer your question: yes, you must validate the data both in your model and in your form. The reasoning here is that your data might not always come from a form. But perhaps some data is input from a 3rd party API. Or from the CLI. Wherever the data comes from, you can be certain your model will always be valid. If you are too lazy to write out all the exceptions, follow these rules:
- Use this library: https://github.com/webmozart/assert
- Write custom value objects like the example in my previous post: BirthDate. Which you can use over and over again. Need another example?
<?php class EmailAddress { private $emailAddress; public function __construct(string $emailAddress) { if (!filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException(sprintf('The e-mail address "%s" is invalid.', $emailAddress)); } $this->emailAddress = $emailAddress; } public function getMailTo(string $subject, string $body): string { return sprintf('mailto:%s?%s', $this->emailAddress, http_build_query([ 'subject' => $subject, 'body' => $body, ])); } public function getEmailAddress(): string { return $this->emailAddress; } }
To answer your 2nd question:
Don't use exceptions for validation. The code should not be halted at that point, as you want to report back ALL errors at once to the user instead of 1-by-1.And use a format that is most easy to work with like an array to store the validation errors.
Also create dedicated validators for your entities. As you might need to validate your entity in more than 1 place. It can still extend from a base generic validator:
<?php class LoginValidator extends Validator { public function validate(array $data) { return parent::validate($data, [ 'username', 'password', ]); } }
-
Yes, your form and any other client-side code should have validation that checks that all business rules (being 18+) have been enforced.
Validation and exceptions are not the same thing. Validations are done before you pass it down. While exceptions are thrown after it has been passed down and is invalid.
Because your model sits at the center of your application and is guarded with exceptions it becomes impossible to use them with invalid data. Which makes your life as a developer much easier.
And anything you add on top adds it's own validation and has it's own method of communicating error's back to the user.
-
Entities should not have error messages, they should throw exception's on invalid input. Error messages are to indicate to the user that what he input is invalid.
Why exceptions? Because your model must be a source of truth, it enforces the business rules.
The application (forms, buttons, ui) lies on top of your model. So for the application to communicate effectively with the model it must ensure all input is valid BEFORE the model is called.
The exception's are usually called like this in the model:
<?php public function setBirthDate(DateTimeInterface $dt) { $this->assertGreaterThanOrEqualTo18($dt); $this->assertLessThan100($dt); $this->birthDate = $dt; }
Because you may have these business rules. You can take it one step further:
<?php class BirthDate { private $date; public function __construct(DateTimeInterface $date) { $this->assertGreaterThanOrEqualTo18($date); $this->assertLessThan100($date); $this->date = $date; } } public function setBirthDate(BirthDate $bd) { $this->birthDate = $bd; }
Now every time you use a birthdate you will apply the business rules.
-
1) Let the view do a sub-request to the header controller to load the menu.
2) Create a view helper that loads the menu.
Both will require the current route to determine which hierarchy to load/show.
-
If you have a monetary income in any officially recognized currency*, you need to pay tax! It's that simple, really. Now it's possible that certain income is exempt from tax, for example, a yearly turn-over less then 5.000 individually, or 25.000 as a business. That's the current law in Belgium.
I am not an accountant, and I am unfamiliar with tax regulations in Australia. If you have a business, it's recommended to have an accountant, he'll advise you on the best approach, and keep your taxes in check.
*So anything not monetary, or any unofficial currency, would be tax-free. Get paid in litecoin?
-
6 hours ago, dalecosp said:
Yeah, but does anyone ever actually punch it .... and follow through?
Congrats, Maxxd ... I'm sure you'll be a tip-top rep for the community ?Nope, they don't. At least not in my case, others may have had better luck. I was only joking when I said that. I am already happy if I get a thumbs up on my answers.
-
Admin seems more like a privilege/role, so I would make that a field of User.
Then your User and Person. I get what you are saying, and I have this in my system. I would have a Person object with no reference to a User. And let the User reference Person. Thus decoupling my application from the auth layer.
<?php class User { const ROLE_SYSTEM = 'role_system'; const ROLE_ADMIN = 'role_admin'; /** * @var string[] */ private $roles = [ ]; /** * @var Person|null */ private $person; }
For system accounts, the Person reference would be NULL as it's not required and it's role would be ROLE_SYSTEM.
-
And after you started using the power sander, how often have you done it by hand since?
And if given the chance to do it by hand, would you still do it?
Regardless of the fact that these are apples and oranges. If done by hand or machine delivers about the same end-product. Regardless with which you started or ended. You can start by hand, go to the machine, and finish by hand. Also the work still has to be done, to get the final product.
This is not the case in software development. You would basically pick up an already sanded object of any length and any given size in an infinite supply.
-
Events are a good way to extend your code without adding unnecessary dependencies.
A ghost copy is a class with only it's ID filled in.
Personally I avoid writing things that have been written before and rather build upon them. In the long run of an application you get burned by the "not invented here" syndrome. I have seen it over and over, and over again.
-
What requinix said, and to reduce unnecessary: querying, hydrating, etc.. for what is essentially throwing something into the bin.
And it still allows your mapper to fire off any associated events, if there would be any.
Another option would be to use a ghost copy, and drop the deleteOneById. But that depends on how you architect your software.
Since a DELETE is the opposite of a SELECT. You can create any "query" methods for it:
deleteAllByCompanyName(string $companyName);
Internally they do the same thing as with the deleteOneById. They query and store any ID's scheduled for deletion. So that any future find's would return NULL even before the database is synchronised.
Avoid any createFromArray or updateFromArray methods though, use the find methods instead.
-
For a Data Mapper this is mixed. Since your mapper maps from and to the database. You would need to be able to use several types of input to get what you need.
<?php interface UserMapper { function findOneById(int $id): ?User; function findAllById(array $id): ?UserCollection; function findOneByUsernameAndEmail(string $username, string $email): ?User; function findAllRegisteredBetween(DateTimeInterface $start, DateTimeInterface $end): ?UserCollection; function findAllRecentlyRegistered(DateTimeInterface $currentDate): ?UserCollection; function create(User $user): void; function createAll(UserCollection $users): void; function update(User $user): void; function updateAll(UserCollection $users): void; function delete(User $user): void; function deleteAll(UserCollection $users): void; function deleteOneById(int $id): void; }
-
On 6/14/2018 at 2:30 PM, NotionCommotion said:
I've heard people claim that classes should only do one thing, but going too far will result with every class with a single method.
And that's bad? Why? Ever seen classes implement nothing more than __invoke? Or a Command with only an execute() method.
What about 2 methods? How many methods should in your opinion a class have?
Suggest Me Top Artificial Intelligents Like ChatGpt
in Miscellaneous
Posted
I use GitHub Copilot daily, in my opinion it's an advanced autocomplete.
I have tried giving it prompts of what I want to achieve, but it doesn't understand your codebase, it just outputs a bunch of code it was trained on.
Like trying to use it to write SQL queries you get tables and columns that don't exist in your project. When trying to use it to write a class or function, it uses functions, libraries, and classes that don't exist.
Even though I try to stick to popular libraries and follow the PSR conventions, it still outputs abandoned libraries, non existing PHP functions etc..
Where it does shine is autocompletion, it understands the context surrounding it (array keys, arguments, variable names, ..), and your coding patterns.
It's also a bit slow, so often times I am waiting for a suggestion, get impatient and start typing, and then use the autocompletion when it's finally ready.
So, in conclusion, you still need to be a software developer to use it, and as long as you don't try to use it to write your entire project, it's okay. In the end, it saves you time writing or refactoring your code.