PHP Knowledge Base Update -- June 29th, 1999

[email protected] (Nathan Wallace) Wed, 30 Jun 1999 03:45:59 -0500
Newsgroups php.kb
Message-ID <[email protected]>
There are a few answers that I think may need some work today.  Could
kind, helpful people check and perhaps edit them in the knowledge base
for me?  In particular:
  - counter question could do with a solution
  - adding a C code function answer is very insufficient

Cheers,

Nathan


------------------------------------------------------------
Do I need to use locking when keeping a counter in a file?
Why does my counter keep resetting to zero?
Will I have concurrency problems with my counter?
http://e-gineer.com/phpkb/view.phtml/qid/245
------------------------------------------------------------
M.Brands

You need to be aware of concurrency problems when writing a counter.  If
several people visit your page at the same time, things can go wrong.
Here is a short example from the mailing list which explains some of the
possible problems.

(Assume the counterfile exists and contains 6)

process 1                       process 2
----------------------------------------------------------------------
$counterFile = "...";
if (!file_exists($counterFile))
$fp = fopen($counterFile,rw);
$num = fgets($fp,20);
$num += 1;
print "$num";
exec("rm -rf $counterFile");
                                $counterFile = "...";
                                if (!file_exists($counterFile)) {
                                    exec("echo $num > $counterFile");
                                    exec("echo 1 > $counterFile");
                                }
                                $fp = fopen($counterFile,rw);
                                $num = fgets($fp,20);
                                $num += 1;
                                print "$num";
                                exec("rm -rf $counterFile");
                                exec("echo $num > $counterFile");

Each line represends one little step in time. I'm assume you machine
only has one cpu, so that's why only one line of code is executed
at a time. (In reality, lines may be interrupted in the middle
of doing something, not conveniently after they've finished.)

Process 1 encounters a valid file, reads it, inreases the number
and erases the file. By this time, process 2 tries to read it to.
After that, process 1 creates a new file with the value 7 in it.
Process 1 is now finished. Process 2, having failed to open the
counterfile, creates a new one (overwrites actually) with the
value 1. It then reads the file, increments the number (now 2)
and erases the value. Then, a new file with the value 2 is created.
So, after both process 1 and 2 have finished, the counter contains
2 and not the expected 8!

This may seem far fetched, but if you have enough processes trying
to update counter.text at the same time, something like this is
going to happen.

So, to build a better mousetrap, you need to make sure only one
process can update counter.text at one. Concurrent reading is not
a problem, as long as a read cannot combined with a write. There
are several ways to solve this common problem, such as using a
semaphore or locking counter.text with flock. (There are more
ways.) Also, you should try not to use the exec's.
One, because it's slow, and two, because it's safer to do as much
as possible from PHP. (If you did it completely in PHP, you might
not have noticed this problem so soon, although it would occur
if you waited long enough.) On, and you might want to leave out
the 'rm -rf' bit, since it doesn't do anything useful. Even worse,
it causes more diskaccess than simply overwriting the content of
the counter. If you were to remove the rm, this script would
already run a lot better (although it would still be faulty).

Btw. you may want to use 0 as a starting value for your counter,
since you're starting to count at 2 and not 1 visitor. But only
an complete ass (like myself ;) would complain about that...


------------------------------------------------------------
Why does mcrypt add NULLs to my string before encrypting it?
http://e-gineer.com/phpkb/view.phtml/qid/248
------------------------------------------------------------
Sascha Schumann

Triple DES (and all other algorithms provided by mcrypt) are
block algorithms. That means that most of them work only on fixed
size blocks. Triple DES takes 64 bits per block. It simply cannot
work on fewer data, so it has to fill up the buffer.


------------------------------------------------------------
How can I read a line at random from a file?
http://e-gineer.com/phpkb/view.phtml/qid/249
------------------------------------------------------------
Fred Isler, Colin Viebrock, Ariel

There are a few ways read a line from a file at random.

If you don't want to lond the file into memory, do it in two passes,
count the lines in the file (unix command wc is probably fastest for it,
popen it) then pick an random number, and find that line. You can use
head, or tail for it, or do it in php.

If you can load it into memory, then use file to get an array, count the
array, and pick an element.  Here is some code to do this:

    // get the number of lines in the file
    $fileLines = file("file.txt");
    $i = count($fileLines);
    // seed and pick a random number between 0 and $i:
    srand((double)microtime()*1000000);
    $idx = rand(0,$i);  
    // print your random line:
    print ("$fileLines[$idx]");


------------------------------------------------------------
How can I write a forum in PHP?
What database schema should I use to implement discussion lists?
Where can I find more information about building a bulletin board?
http://e-gineer.com/phpkb/view.phtml/qid/250
------------------------------------------------------------
Many, many helpful people

There are a number of different techniques that can be used to build a
threaded discussion list.  Rather than choose one or outline them all
here it is better to point you at a relevant thread in the mailing list
archives:

http://www.progressive-comp.com/Lists/?l=php3-general&m=93062134631409

Unless you really want to build one yourself you are probably much
better off using one of the existing open source systems.

A list of PHP projects that you can utilize is available at:

    http://www.php.net/projects.php3

In this case, Phorum may be of particular interest:

    http://www.phorum.org


------------------------------------------------------------
I have added a function to file.c and recompiled.  Why doesn't it exist
in PHP?
http://e-gineer.com/phpkb/view.phtml/qid/253
------------------------------------------------------------
Chad

Did you add it to the function list at the top of the file? It has to
make itself known to php or it is just text in a file.


------------------------------------------------------------
How can I create a column in MySQL that auto increments?
How can I insert data into MySQL with an automatic unique ID?
http://e-gineer.com/phpkb/view.phtml/qid/254
------------------------------------------------------------
Richard Seymour, Christophe LAUER

In MySQL you just create a column of type integer and give it the
AUTO_INCREMENT attribute.

    create table foo (
        bar integer auto_increment );

http://www.mysql.com/Manual_chapter/manual_Reference.html#CREATE_TABLE


------------------------------------------------------------
How can I create a column in Postgres that auto increments?
How can I insert data into Postgres with an automatic unique ID?
http://e-gineer.com/phpkb/view.phtml/qid/256
------------------------------------------------------------
Christophe LAUER, DeJuan Jackson

PostgreSQL uses the concept of 'sequences', just like Oracle does.  Try:

    CREATE SEQUENCE name-of-sequence START startnumber;

ie:

    CREATE SEQUENCE jobsequence START 123456;

gets you a named autoincrementing field.

You can then get the next value and automatically increment the sequence
using the following:

    SELECT NEXTVAL ('name-of-sequence');

To do auto incrementing columns the keyword in PostgreSQL is SERIAL. For
example:

    create table foo (bar SERIAL PRIMARY KEY, other_data TEXT);
    insert into foo (other_data) values ('This is a test');
    select * form foo;  --- foo.bar should have been set to 1

It is currently implemented as a sequence, so the above create table
statement is equivelent to:

    create sequence seq_foo_bar;
    create table foo (
        bar INT PRIMARY KEY DEFAULT nextval('seq_foo_bar'),
        other_data TEXT);


------------------------------------------------------------
How can I stop users on my web server from using system()?
How can I setup PHP for safe scripting only?
http://e-gineer.com/phpkb/view.phtml/qid/258
------------------------------------------------------------
Rasmus Lerdorf, Chad

Turn on safe-mode and the system()-like functions can only execute
whatever programs are located in the designated safe-mode-exec-dir.  If
you point that to an empty dir people won't be able to execute anything.

    http://www.php.net/manual/config-security.php3

    http://www.php.net/manual/phpfi2.html#safemode


------------------------------------------------------------
How can I sort results by a certain column?
http://e-gineer.com/phpkb/view.phtml/qid/260
------------------------------------------------------------
Steve Lianoglou

Let's say the column you want to sort by is "user_num".

    SELECT * 
    FROM   table_name
    WHERE  whatever='whathaveyou' 
    ORDER BY user_num ASC

This sorts the rows from smallest to highest, change the ASC to DESC and
you get from highest to lowest...


------------------------------------------------------------
Can I use PHP and SSI in the same document?
http://e-gineer.com/phpkb/view.phtml/qid/261
------------------------------------------------------------
Jim Winstead

If you are running PHP as an Apache module, check out the virtual()
function. You cannot mix SSI and PHP content in the same document.

    http://www.php.net/manual/function.virtual.php3


------------------------------------------------------------
How are strings constructed in PHP?
What quotes can I use to make strings in PHP?
Can I include a variable reference in a string?
http://e-gineer.com/phpkb/view.phtml/qid/262
------------------------------------------------------------
Nathan Wallace, Rasmus Lerdorf

Basically a string in PHP is anything between matching quotes:

    'I am a string in single quotes'

    "I am a string in double quotes"

You can include quotes inside the string that do not match the quote
that started the string.  So you can include single quotes in a string
that was started (and finishes) with double quotes.  So, these will
work:

    "I am a 'single quote string' inside a double quote string"

    'I am a "double quote string" inside a double quote string'

PHP thinks it has reached the end of the string as soon as it sees that
matching quote.  So the example

    "Why doesn't "this" work?"

is actually seen by PHP as being

    "Why doesn't " - a string

    this           - crap letters that PHP doesn't recognize

    " work?"       - a string

It is also helpful to know that you can include a quote character of the
same type inside a string if you escape it.  The escape tells PHP that
the next character should just be used as part of the string.  For
example:

    "Why doesn't \"this\" work?"

is a single string that contains double quote characters.

Double quote strings and single quote strings are treated differently by
PHP.  Double quote strings are interpreted by PHP while single quotes
strings are treated exactly as is.  For example:

    $foo = 2;
    echo "foo is $foo";   // this prints:  foo is 2
    echo 'foo is $foo';   // this prints:  foo is $foo

So you should use double quote strings when you want to include a
variable.

Note however that PHP only supports simple constructs inside quoted
strings, so this will work:

    "$a[$i]"

But this won't:

    "$a[$i][$j]"

And as such it is usually advisable to simply pop out of the string when
you are doing anything more complex than a simple variable substitution.
ie.

    "some string ". $a[$i][$j] . " more text"

The last thing to know about strings is that you can join them together
easily using the concatenate operator (.).  Here is an example:

    $var = "crappy ending";
    echo "this " . 'is an' . ' example ' . "with a " . $variable;


------------------------------------------------------------
How do I pass a variable to the compare() function when calling it from
usort?
http://e-gineer.com/phpkb/view.phtml/qid/268
------------------------------------------------------------
Shawn Bernard, Colin Viebrock

You can learn more about usort() here:

    http://www.php.net/manual/function.usort.php3

An example of when you might like to pass a variable to the compare
function is if you wanted to sort by a difference array column.

You can't really pass a variable to the compare function.  The kludge is
to set up a global variable, and set that before calling usort():

    function compare() {
        global $USORT_COLUMN;
        if ($a[$USORT_COLUMN] == $b[$USORT_COLUMN]) {
            return 0;
        } else {
            return ($a[$USORT_COLUMN] > $b[$USORT_COLUMN]) ? 1 : -1 ;
        }
    }

    $USORT_COLUMN = "name";
    usort($array, "compare");


------------------------------------------------------------
How can I get the ordinal for a number (eg: st for 1st, th for 24th) ?
http://e-gineer.com/phpkb/view.phtml/qid/269
------------------------------------------------------------
Colin Viebrock

You can use the date() function in many cases to do this:

    http://www.php.net/manual/function.date.php3

If you just want to do it for a number in general then try this
function:

    function ordinal($num) {
        if ($num>10 && $num<20) {
                $r="th";
        } else {
                switch ($num%10) {
                   case 1:
                        $r="st";
                        break;
                   case 2:
                        $r="nd";
                        break;
                   case 3:
                        $r="rd";
                        break;
                   default:
                        $r="th";
                        break;
                }
        }
        return $r;
    }