Saturday, January 27, 2024

Real time chat

In a web application, real time chat, i.e. sending/receiving messages without database polling and refreshing the page, can be implemented with socket.io as follows:
Note that besides a web server, you also need a chat server that coordinates messages and sends requests to web server to save chat messages to database.

Tuesday, January 23, 2024

The Turkish uppercase "İ"

PHP's mb_strtoupper function converts its input string to uppercase. For Turkish characters, "İ" is a special case, the other characters (ŞÇÖĞÜ) work fine with UTF-8 encoding. mb_strtoupper("izmir", 'UTF-8') returns "IZMIR". If you want it to return "İZMİR" you have to have Turkish locale installed on your OS,  add setlocale(LC_ALL, 'tr_TR.UTF-8') to your startup index.php and use mb_strtoupper("izmir", 'tr_TR'). If you don't have Turkish locale on your OS, mb_strtoupper("izmir", 'tr_TR') will return false. In your index.php, check if locale is set correctly by issuing echo setlocale(LC_ALL, 0); If the printout is "LC_COLLATE=C;LC_CTYPE=English_United States.1252;..." you were not able to set the locale to Turkish. I have a search function that compares two strings. I don't have Turkish locale on my development environment, therefore I use the following hack (PHP 7.4):

$modProduct = str_replace("İ", "i", $productName); //Change "İ" to "i" so that for UTF-8, it's 
uppercase becomes "I" instead of "İ"
$modSearch = str_replace("İ", "i", $searchTerm); if (strpos(mb_strtoupper($modProduct,'UTF-8'), mb_strtoupper($modSearch, 'UTF-8'))!==false) {...}

Friday, January 19, 2024

PHPStorm structural search

To find code of the form
if (isset(...)) {
    ...
} else {
    ...
}
you can use PHPStorm structural search (Edit - Find - Search Structurally):
if (isset($a$)) {
$b$
} else {
$c$
}

Monday, January 15, 2024

Use SQL COUNT(*) instead of PHP count()

If you need the number of items that satisfy a criteria in a SQL database table, intead of
count($db->query("SELECT * FROM customer_comments WHERE status = 1")->rows)
use
$db->query("SELECT COUNT(*) FROM customer_comments WHERE status = 1")->value
Using COUNT(*) is faster because databases are optimized for such operations. When you fetch all rows into your application and then count them using count(), you're moving the data processing to the application level, which is generally less efficient.

Friday, January 12, 2024

Software complexity

The unpredictability of users is one of the key sources of software complexity. Since a web app is exposed to the entire internet, you have to consider numerous edge cases in addition to the nominal use case. People often underestimate the required effort because they typically consider only the app's normal use.

Consider the example of adding a commenting feature to an e-commerce site. The nominal case is the user writing a comment and submitting it. After admin approval, comment should be displayed on product page. Steps:
  1. User writes comment
  2. User presses submit button
  3. A message saying "comment will be visible after admin approval" is shown to user
  4. Comment is saved to database for admin approval
  5. Admin reviews and approves comment
  6. When product page is loaded, approved comments are shown
Additional considerations:
  • If the user is not logged in, they must login. If the user has no account, they must create an account. After login / account creation, they should be redirected back to same product page with "comment will be visible after admin approval" message. Do not show that message for normal product page loads.
  • After pressing submit button, user might want to edit or delete the comment.
  • Although admin approval prevents spam, a malicious user could still:
In addition to usability, performance, low resource usage and maintainability, these details make up most of software engineering work.

Thursday, January 11, 2024

Button click not working

When you add a button to an existing page and see that you cannot click the button, it might be due to an existing component margin overlapping with it, you can check it with your browser development tools - inspect element:
One way to solve it is to increase the z-index of your button. You also have to set position: relative for z-index to work:
<button style="padding-left: 20px; padding-right: 20px; 
position: relative; z-index: 99;" type="submit">Yorum yap</button>

Wednesday, January 3, 2024

Using both static and dynamic versions of the same class

During my web app refactoring, I have to apply changes incrementally instead of changing everything at once. One of my main updates is making classes static. This change usually affects hundreds of files that access properties via magic methods. To avoid changing only the part I am focused on, I use the following strategy to be able to use properties of the class both in a static and dynamic way:
class Request {
  public static $get = array();

  private function __construct() {
    // Set the instance properties so that they can be accessed dynamically
    // until I convert every instance of request property use to static, e.g. Request::$get
    $this->get = &self::$get;
  }

  public static function create() {
    //Sanitize input data, primarily to prevent issues like cross-site scripting (XSS)
    $_GET = self::clean($_GET);
    self::$get = $_GET;
    return new self();
  }
  ...
The only global change I have to make is using Request::create() instead of new Request() to obtain request object.