How PHP handles requests at the same time

Today I'm just wondering how PHP handles requests at the same time. Since PHP can handle multiple requests at the same time, I was thinking about possible security holes or errors in PHP scripts, and I just wanted to know if I was too scared.

Thus, in the case when 100 requests at the same time, and apache is configured to send them to PHP. What will PHP do with the following examples (all the examples that I have already seen in some real applications anyway)

All examples are alike. (I do not ask for the best ways to solve these examples)


Example 1. Creating a cache

<?php if (!file_exists('my_cache.txt')) { // do something slow (taking a second or so) file_put_contents('my_cache.txt', $cache); } 

Suppose we have about 100 queries. Is it not possible that the cache is generated 100 times and stored 100 times in the cache file?


Example 2: Writing a cache entry

 <?php writeItemToDatabase($myItem); if (countAllItemsInDatabase() > 100) { $items = readAllItemsFromDatabase(); deleteAllItemsFromDatabase(); // Process items } 

This example is a bit silly due to the "deleteAllItemsFromDatabase" function. If this script is executed in parallel, it could happen like this:

  • Two users simultaneously process all elements.
  • Some items will never be processed because they are deleted before they are processed at any time.

Example 3: Virtual Money

 <?php if ($user->getMoney() > 100) { $user->decreaseMoney(100); $user->addItem($itemToBuy); } 

In this example, there is a big security issue if scripts can run at the same time. If I quickly clicked the buy button of this application, I could buy the product even if there was no money left in my account.


Question

I wonder if I'm just a little paranoid about writing scripts to prevent such problems or are these real real problems?

And - for the rare case - if I need to write some processed serial action (for example, in this example), is there a PHP function / extension to ensure that the script part is processed only once at a time, for example:

 <?php $semaphore->lock(); // Do something dangerous $semaphore->unlock(); 
+4
source share
4 answers

https://github.com/mpapec/simple-cache/blob/master/example3.php

 require "SafeCache.class.php"; // get non blocking exclusive lock $safe = new SafeCache("exclusive_lock_id"); if ( $safe->getExclusive() ) { print "we have exclusive lock now<br>"; // ... print "releasing the lock<br>"; $safe->doneExclusive(); } 

Also consider other examples of secure cache generation. https://github.com/mpapec/simple-cache/blob/master/example1.php

0
source

Things you are considering and code examples are not thread safe . This is not a problem with PHP, but concurrency general.

Decision:

  • for file operations, such as examples 1 and 2, use file locks.

  • for transactions such as a transaction with your money, use transactions Databases or, ultimately, table locks.

As I know, PHP does not provide a semaphore mechanism. Remember that an internal server implementation or configuration (for example, prefork / worker apache) can even spawn every request in a different process - so you donโ€™t have to worry about shared memory. Concern about resources - files, databases, etc.

The semaphores you are talking about are not a good solution. For example, at the database level, the db engine can lock / unlock individual tables or even rows, and this is very effective compared to "locking the entire server on this piece of code."

+1
source

+1 to killer_PL and in addition to his answer:

Memcache cas() or add() functions are very convenient for implementing file locking.

add() saves a variable with a specific key only if that key does not already exist on the server. cas() also performs a check and install operation. Based on one of these operations, it is very easy to create a semaphore.

0
source

Sorry for my English.

First, I will talk about some common features:

  • This is real =)

    $semaphore->lock();

    // Do something dangerous

    $ semaphore-> unlock ();

  • I am trying to describe a basic concept. The code is not suitable for release

  • A semaphore must have several types: a file , a database, and so on.
  • The implementation will be different for each type.

First, let's make the file implementation. We will use the built-in flock function (thanks to Salman A ).

 <?php $fname = 'test.txt'; $file = fopen($fname, 'a+'); sleep(5); // long operation if(flock($file,LOCK_EX|LOCK_NB )){// we get file lock - $semaphore->lock(); sleep(5); // long operation fputs($file, "\n".date('dmY H:i:s')); //something dangerous echo 'writed'; flock($file,LOCK_UN ); // release lock - $semaphore->unlock(); }else{ // file already locked echo 'LOCKED'; } fclose($file); 

Secondly, do a database lock. In general, some databases may have a write lock mechanism on a single table, in which case you should use this mechanism. But other databases did not support this feature, for example MySql. In this case, let's do the magic :)

For example, we have a simple table

  CREATE TABLE `threading` ( `id` INT(10) NOT NULL AUTO_INCREMENT, `val` INT(10) NOT NULL, PRIMARY KEY (`id`) )COLLATE='utf8_general_ci' ENGINE=InnoDB 

Allows you to add a column that will simulate a "lock" entry:

 ALTER TABLE `threading` ADD COLUMN `_lock` BIT NOT NULL AFTER `val`; 

Now we can set _lock fild to 1 for a locked write!

IMPORTANT : you must make the lock one request as follows: update threading set _lock = 1 where id = 1 AND _lock <> 1 ; .
Note: AND _lock <> 1 prevent the record from being locked when it is already locked, so you can decide whether the record was locked by the rows_affected mechanism.

 <?php // connect mysql_connect('localhost','root','root'); mysql_selectdb('testing'); // get info $res = mysql_query('select * from threading where id = 1;'); $row = mysql_fetch_assoc($res); print_r($row); // debug if($row['val']>=70){ sleep(5); // emulate long-long operation =) // try to lock mysql_query('update threading set _lock = 1 where id = 1 AND _lock <> 1 ;'); // _lock <> 1 - very IMPORTANT! sleep(5); // emulate long-long operation =) $affected_rows = mysql_affected_rows(); if($affected_rows!=1){ // lock failed - locked by another instance echo '<br> LOCKED!'; }else{ // lock succeed mysql_query('update threading set val = val-70 where id = 1;');//something dangerous mysql_query('update threading set _lock = 0 where id = 1;'); // UNLOCK! } } // view result $res = mysql_query('select * from threading where id = 1;'); $row2 = mysql_fetch_assoc($res); echo '<br>'; print_r($row2); // disconnect mysql_close(); 

So, the test was very simple - simultaneously run files in different browsers. For a different type of semaphore, you must use different logic and functions

0
source

All Articles