Saturday, November 21, 2015

Quick Language Reference: PHP

GETTING STARTED

A good graphical ide is Netbeans with the PHP pack.  Or install cygwin and the PHP interpreter

PHP can be run from the command line:

        file: yourscript.PHP

        <?PHP
            echo "hello world";
        ?>

Then to execute run (assuming PHP exe is in PATH)

        PHP yourscript.PHP

To syntax check a script  you can run (like lint):

    PHP -l yourscript.PHP


Notes: The start tag is required.  The closing tag of a PHP block at the end of a file is optional, and in some cases omitting it is helpful when using include() or require() so unwanted whitespace will not occur at the end of files, and you will still be able to add headers to the response later. It is also handy if you use output buffering, and would not like to see added unwanted whitespace at the end of the parts generated by the included files.

Otherwise, everything outside of the tags are interpreted as string literal

        <?PHP ?> tags cannot be nested inside another <?PHP ?> tag
        and ;  at end of commands is required


Print out server config

        <?PHP echo PHPinfo(); ?>

Print contents of data structure or object

        <?PHP var_dump($somevar); ?>

The log file is defined in PHP.ini (global PHP config file)

        log_errors = On
        error_log = C:\temp\PHP_error.log


To run as a shell script, add to top of file

  #!/usr/bin/env PHP

TESTING COMPATIBILITY

# test code in multiple PHP versions
http://3v4l.org/UCALa


PRINTING

To print a string to STDOUT, use:

    echo "hello world";
    print "hello world";


COMMENTS

    # single line comment (Perl style)
    // single line comment (Javascript style)
    /* multi line comments */


LOGICAL OPERATORS

    Example Name    Result
    $a and $b       And     TRUE if both $a and $b are TRUE.
    $a or $b        Or      TRUE if either $a or $b is TRUE.
    $a xor $b       Xor     TRUE if either $a or $b is TRUE, but not both.
    ! $a            Not     TRUE if $a is not TRUE.
    $a && $b        And     TRUE if both $a and $b are TRUE.
    $a || $b        Or      TRUE if either $a or $b is TRUE.


COMPARISON OPERATORS

    Example         Name            Result
    $a == $b        Equal           TRUE if $a is equal to $b after type
                                     juggling.  EG: '' == 0 == false
    $a === $b       Identical       TRUE if $a is equal to $b, and they are of
                                     the same type. (introduced in PHP 4)
    $a != $b        Not equal       TRUE if $a is not equal to $b after type
                                     juggling.
    $a <> $b        Not equal       TRUE if $a is not equal to $b after type
                                     juggling.
    $a !== $b       Not identical   TRUE if $a is not equal to $b, or they are
                                     not of the same type. (introduced in PHP 4)
    $a < $b         Less than    
    $a > $b         Greater than  
    $a <= $b        Less than or equal to
    $a >= $b        Greater than or equal to


ARITHMETIC OPERATORS

    +       Addition
    -       Subtraction
    *       Multiplication
    /       Division
    %       Modulus (division remainder)
    ++      Increment
    --      Decrement

    NOTE: there is no exponent ** or ^ operator.  Use: pow( base, exponent )


ASSIGNMENT OPERATORS

    Operator Example Is The Same As
    =        x=y     x=y
    +=       x+=y    x=x+y
    -=       x-=y    x=x-y
    *=       x*=y    x=x*y
    /=       x/=y    x=x/y
    .=       x.=y    x=x.y
    %=       x%=y    x=x%y


ERROR CONTROL OPERATOR
 
    @           can be prepended to value functions to suppress error

    For example:

        $text = @file_get_contents('non_existent_file');
        $value = @$cache[$key];

Warning: use this carefully.  You want to see most errors.

A more acceptable (safer) use is for arrays:

# manually check for null values if needed
$value = @$yourarray['non-existent-key'];
do_some_magic($value);


VARIABLES

Notes: There's no equivalent of "use strict" as in perl.  Though you can turn on warnings for use of null variables with the error_reporting E_STRICT flag

declaration and assignment

    $a = "asdf";
    $b = 2;


    $c = NULL;

variables are NULL if:
        it has been assigned the constant NULL.
        it has not been set to any value yet.
        it has been unset().

check NULL value with:
        is_null()
        or $c === NULL
        or $c !== NULL


PARSE COMMAND LINE ARGS

for example:

    script.PHP plain-arg --foo --bar=baz 'plain arg 2' -abc

These will be stored in array $argv

$argv[0] is always the name that was used to run the script.


    function parseArgs($argv){
      array_shift($argv);
      $o = array();
      foreach ($argv as $a){
        if (substr($a,0,2) == '--'){
          $eq = strpos($a,'=');
          if ($eq !== false){
            $o[substr($a,2,$eq-2)] = substr($a,$eq+1); }
          else {
            $k = substr($a,2);
            if (!isset($o[$k])){ $o[$k] = true; }
          }
        } else if (substr($a,0,1) == '-'){
          if (substr($a,2,1) == '='){
            $o[substr($a,1,1)] = substr($a,3);
          } else { foreach (str_split(substr($a,1)) as $k){
            if (!isset($o[$k])){ $o[$k] = true; } }
          }
        }
        else { $o[] = $a; }
      }
      return $o;
    }

use

    $args = parseArgs($_SERVER['argv']);


LOOP OVER STARDARD INPUT

<?PHP
$f = fopen( 'PHP://stdin', 'r' );

while( ( $line = fgets( $f ) ) !== false ) {
 echo $line;
}

fclose( $f );


DEFINE CONSTANTS

    define('DS', DIRECTORY_SEPARATOR);
    echo DS;


STRINGS

like with perl, double quoted strings interpolate variables

    $a = "hello";
    print "$a world";
    # Outputs: hello world

Use single quotes for better performance, if possible.
Single quotes are literals, variables are not interpreted.

    <?PHP
    echo 'this is a simple string';
 
    echo 'You can also have embedded newlines in
    strings this way as it is
    okay to do';
 
    echo 'Arnold once said: "I\'ll be back"';
    // Outputs: Arnold once said: "I'll be back"
 
    echo 'You deleted C:\\*.*?';
    // Outputs: You deleted C:\*.*?
 
    echo 'You deleted C:\*.*?';
    // Outputs: You deleted C:\*.*?
 
    echo 'This will not expand: \n a newline';
    // Outputs: This will not expand: \n a newline
 
    echo 'Variables do not $expand $either';
    // Outputs: Variables do not $expand $either
    ?>

 
A string is also an array of characters
NOTE: if you get the first char of a string instead of a string,
code is executing $string[0]... an array assumed where there is none

$s = "hello there";
echo $s[3];  # prints l


PHP heredoc syntax (do not indent closing marker)

    $str = <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD;

 
NOTE: It is no problem for a string to become very large. PHP imposes no boundary on the size of a string; the only limit is the available memory of the computer on which PHP is running.


REPLACE STRINGS

    str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

    It's worth skimming over all string manipulation functions:

        http://PHP.net/manual/en/ref.strings.PHP


INCLUDE FILE

Code must be loaded into global memory space before it can be called. The following calls will scan the currect folder by default if no path information is given.

        try to include, ignore if missing or not found
        <?PHP include "somefile.PHP"; ?>

        try to include, die if file missing
        <?PHP require "somefile.PHP"; ?>

        like include, ignore if already included
        <?PHP include_once "somefile.PHP"; ?>

        like require, die if file missing, only include once
        <?PHP require_once "somefile.PHP"; ?>
 
 
For absolute paths, it is sometimes helpful to define constants like:
 
      // which page are we on?  eg /some/path/to/script.PHP
      if( ! defined( 'WEB_PATH' ) ) define('WEB_PATH', $_SERVER['PHP_SELF']);
   
      // web root dir. eg /some/path/to
      //if( ! defined( 'WEB_ROOT' ) ) define('WEB_ROOT', dirname(WEB_PATH));
      if( ! defined( 'WEB_ROOT' ) ) {
              $path = dirname(WEB_PATH);
              // avoid "//" at root
              if ( $path == '/' ) $path = '';
              define('WEB_ROOT', $path);
      }
   
   
      // what is the file name only?  // eg script.PHP
      if( ! defined( 'WEB_FILE' ) ) define('WEB_FILE', basename(WEB_PATH));
   
      // physical file path (system).  eg: /var/www/html/yoursite
      if( ! defined( 'ABS_ROOT' ) ) define('ABS_ROOT', dirname(dirname(__FILE__)));

      if( ! defined( 'ABS_ROOT' ) ) define('ABS_ROOT', dirname(__FILE__));


NAME VS ID

On an html form element like <input id="" name="" />

ID is used mostly for javascript
NAME is used for form submission

If the name has [], like:

example[]

then PHP will parse it as an array of repeating elements.


WARNING: PHP also performs automatic replacement of special chars in names. For example

name="a.b"  becomes  "a_b"  in $_POST

For safest results, stick with these chars for input names:

alpha number _ [ ]  


ARRAYS

Arrays and hashes are the same thing.  One uses ints, the other strings.

WARNING: some functions will expect sequential indexes (for example fputcsv).  PHP does not automatically 'fill in' missing indexes.

define an array with:

        $arr = array();
 
assign values with:

        $arr[] = 134;
        $arr[] = 234;
     
    or

        $arr = array( 123, 234 );


define hash with:

<?PHP
$arr = array("foo" => "bar", 12 => true);

echo $arr["foo"]; // bar
echo $arr[12];    // 1
?>


hash/arrays can be nested any way you want :-)


multidimensional array

<?PHP
$arr = array("somearray" => array(6 => 5, 13 => 9, "a" => 42));

echo $arr["somearray"][6];    // 5
echo $arr["somearray"][13];   // 9
echo $arr["somearray"]["a"];  // 42
?>

print array

print_r($array);

serialize dump as valid PHP code

var_export( $array );

iterating with index

$array = array(1, 2);
$count = count($array);
for ($i = 0; $i < $count; $i++) {
echo "\nChecking $i: \n";
echo "Bad: " . $array['$i'] . "\n";
echo "Good: " . $array[$i] . "\n";
echo "Bad: {$array['$i']}\n";
echo "Good: {$array[$i]}\n";
}

array iteration with foreach

foreach ($array as $value) {
}

hash iteration

foreach ($array as $key => $value) {
unset($array[$key]);
}

alternate iteration (older syntax)

while ($el = current($arr)) {
$carr[ $el[$x] ] = $el[$y];
next($arr);
}


NOTE: In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.  Referencing $value is only possible if the iterated array can be referenced (i.e. if it is a variable).

<?PHP
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}

# $arr is now array(2, 4, 6, 8)
unset($value); // break the reference with the last element, may do inside loop
?>

array functions:

    pop and returns the last value of the array,
    shortening the array by one element.
    array_pop ( $array )

shift an element off the beginning of array

    array_shift( $array )
   
add to end


    int array_push ( $array , $var [, $var2... ] )
    or,
    $your_array[] = "new item";

hash specific

    array_values, array_keys

    more:
    http://www.PHP.net/manual/en/ref.array.PHP


ARRAY OPERATORS

    Example    Name         Result
    $a + $b    Union        Union of $a and $b.
    $a == $b   Equality     TRUE if $a and $b have the same key/value pairs.
    $a === $b  Identity     TRUE if $a and $b have the same key/value pairs
in the same order and of the same types.
    $a != $b   Inequality   TRUE if $a is not equal to $b.
    $a <> $b   Inequality   TRUE if $a is not equal to $b.
    $a !== $b  Non-identity TRUE if $a is not identical to $b.


FUNCTIONS

    define functions

    <?PHP
# typical function
        function a($n){
          b($n);
          return ($n * $n);
        }

     variable pass by reference, use &
        function b(&$n){
          $n++;
        }

        echo a(5); //Outputs 36

# WARNING, only variables can be passed by reference!
# array( 1,2,3 ) cannot be passed by ref, it must be assigned to var
    ?>


NOTE: to access a global variable inside a function, you must re-declare it as global:


$a = 1;
function example() {
print $a;   // $a not defined
global $a;  // now $a points to variable $a in the global scope
print $a;   // $a is 1
}

multiple variables can be declared with , operator:

global $a, $b, $c ;


NOTE: PHP does not support function overloading, nor is it possible to undefine or redefine previously-declared functions.

However PHP does allow:

default values to be set to primitives

# variable arguments, default set in def
function example($a, $b=0) {
//$b is optional

}

also the type of the arg can be tested
function do_somthing( $data ) {

if ( is_array( $data ) ) {
// do something
} else {
// do something else
}
}


nested functions:

All functions are declared in the global scope, unless defined in a class (then it's class-scoped)

<?PHP
function foo()
{
 function bar()
 {
echo "I don't exist until foo() is called.\n";
 }
}

/* We can't call bar() yet
  since it doesn't exist. */

foo();

/* Now we can call bar(),
  foo()'s processesing has
  made it accessible. */

bar();

?>


Both variable number of arguments and default arguments are supported in functions. See also the function references for func_num_args(), func_get_arg(), and func_get_args() for more information.


anonymous functions

Only recent versions of PHP support the concept of closures.

<?PHP
$greet = function($name)
{
printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
?>


NOTE: Closures may also inherit variables from the parent scope. Any such variables must be declared in the function header. Inheriting variables from the parent scope is not the same as using global variables

$callback = function ($quantity, $product) use ($tax, &$total)
            {
                $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                    strtoupper($product));
                $total += ($pricePerItem * $quantity) * ($tax + 1.0);
            };


return by reference

function &bar()
{
$a = 5;
return $a;
}

get a reference to a defined function

<?PHP
echo "test";
function test1() {
 echo " works";
}

$test2 = 'test1'; # just use string name
$test2();
?>

test if function exists

if(function_exists('testfn') ) {}


GET/POST DATA

    <?PHP
      if( $_GET["name"] || $_GET["age"] )
      {
         echo "Welcome ". $_GET['name']. "<br />";
         echo "You are ". $_GET['age']. " years old.";
         exit();
      }
    ?>
    <html>
    <body>
      <form action="<?PHP echo $_SERVER['PHP_SELF']; ?>" method="GET">
      Name: <input type="text" name="name" />
      Age: <input type="text" name="age" />
      <input type="submit" />
      </form>
    </body>
    </html>


    hashes defined:
    $_GET
    $_POST
 
    $_REQUEST  contains both GET and POST values
    $_SERVER   contains server/environment data


     WARNING:  PHP mangles names with ., replaces with _


COOKIES

set cookie
    setcookie(name, value, expire, path, domain);

    <?PHP
    setcookie("user", "Alex Porter", time()+3600);
    ?>

read cookie

    <html>
    <body>
    <?PHP
    if (isset($_COOKIE["user"]))
      echo "Welcome " . $_COOKIE["user"] . "!<br />";
    else
      echo "Welcome guest!<br />";
    ?>
    </body>
    </html>



SESSIONS

    start a session
    <?PHP
        session_start();
    ?>

    store session variable
    <?PHP
        session_start();
        $_SESSION['views'] = 1; // store session data
        echo "Pageviews = ". $_SESSION['views']; //retrieve data
    ?>

    test session var if set
    <?PHP
        if(isset($_SESSION['views'])) {
            $_SESSION['views'] = $_SESSION['views']+ 1;
        } else {
            $_SESSION['views'] = 1;
        }
    ?>

    destroy session var
    <?PHP
        unset($_SESSION['cart']);
    ?>

    destroy session
    <?PHP
        session_destroy();
    ?>


MYSQL DATABASE

core functions:

mysqli mysqli_connect ([ string $host = ini_get("mysqli.default_host") [,
string $username = ini_get("mysqli.default_user") [,
string $passwd = ini_get("mysqli.default_pw") [,
string $dbname = "" [,
int $port = ini_get("mysqli.default_port") [,
string $socket = ini_get("mysqli.default_socket") ]]]]]] )

int mysqli_connect_errno ( void )
string mysqli_connect_error ( void )

bool mysqli_select_db ( mysqli $link , string $dbname )

bool mysqli_ping ( mysqli $link )

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

mixed mysqli_query ( mysqli $link , string $query [,
int $resultmode = MYSQLI_STORE_RESULT ] )
string mysqli_error ( mysqli $link )

mixed mysqli_fetch_array ( mysqli_result $result [, int $resulttype = MYSQLI_BOTH ])
MYSQLI_ASSOC, MYSQLI_NUM, or MYSQLI_BOTH.

for assoc array only:

mysqli_fetch_assoc


mixed mysqli_insert_id ( mysqli $link )

bool mysqli_close ( mysqli $link )


example:

        <?PHP
        include "lib/global.PHP";

        $mysqli = new mysqli($dbhost, $dbuser, $dbpass, $db);
        print "ok";

        if(!mysqli->connect_errno) {
            $sql = "SELECT * from tblPrize where SerialNumber > ? limit 5";
            if($stmt = $mysqli->prepare($sql)) {

                // type string, bind by ref to variable
                // 'typestrings', $var1, $var2, $var3
                // i = int , s = string, d = double, b = blob
                // bool mysqli_stmt::bind_param( string $types , mixed &$var1 [, mixed &$... ] )

                $stmt->bind_stmt_param("i", $serial);
                $serial = 1;
                $stmt->execute();

                $stmt->bind_result($serial, $prize);
                while ($stmt->fetch()) {
                    echo "$serial, $prize\n";
                }

                $mysqli->close();
            } else {
                echo 'Unable to connect';
                exit();
            }
        }

        ?>


ESCAPING HTML

htmlentities( $s, ENT_QUOTES, 'UTF-8');

This takes an optional third argument charset which defines character set used in conversion. Presently, the ISO-8859-1 character set is used as the default.

    http://PHP.net/manual/en/function.htmlentities.PHP

An alternate function (less recommended) is

htmlspecialchars()


ENCODE/DECODE URL DATA

urlencode( $s )
urldecode( $s )

Be careful about variables that may match html entities. Things like &amp, &copy &pound &curren are parsed by the browser and the actual entity is used instead of the desired variable name. This is an obvious hassle that the W3C has been telling people about for years. The reference is here: Ṡ http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.2.


TRY/CATCH

    try {
        echo inverse(5) . "\n";
        echo inverse(0) . "\n";
    } catch (Exception $ex) {
        echo 'Caught exception: ' . $ex->getMessage() . "\n";
    }

throwing: message [, code]

    throw new Exception('2 is not allowed as a parameter', 6);


READ/WRITE FILES

read small file into a string

    $file = file_get_contents ('YourFile.txt');

loop over lines

    $handle = fopen($YourFile, 'r');
while (($buffer = fgets($handle, 4096)) !== false) {
        echo $buffer;
        echo "\n";
    }
    fclose($handle);


WRITE TO FILE

    $myFile = "testFile.txt";
    $fh = fopen($myFile, 'w') or die("can't open file");
    $stringData = "Floppy Jalopy\n";
    fwrite($fh, $stringData);
    $stringData = "Pointy Pinto\n";
    fwrite($fh, $stringData);
    fclose($fh);


READ WEBPAGE

    #Open a site as file
    $read = fopen("http://www.yoursite.com", "r")
                or die("Couldn't open file");
    /# read the bytes with fread ...
 
    # alt, for small files:
# read whole file
    echo file_get_contents("http://127.0.0.1/");

# write to file
file_put_contents ( 'newfile.txt', $your_string );



OBJECT ORIENTED PHP, OOP

Note  'var' keyword is  for declaring class member variables in PHP4, and is no longer needed. It will work in PHP5, but will raise an E_STRICT warning in PHP5 up to version 5.3, as of which it has been deprecated. Though I happen to like it.  :-)


value objects (like struct in c, POJO in Java)

class Person {
 var $id, $first_name, $last_name, $email;
}


populate value object from a hash:

# constructor
public function __construct( $hash = null ) {
 if ( $hash ) {
foreach ( $hash as $key => $value ) {
 # note: reference to variable $key
 $this->$key = $value;
}
 }
}


class example

    class PersonDAO {
       var $conn;

       function PersonDAO(&$conn) {
         $this->conn =& $conn;
       }

       function save(&$vo) {
         if ($v->id == 0) {
           $this->insert($vo);
         } else {
           $this->update($vo);
         }
       }


       function get($id) {
         #execute select statement
         #create new vo and call getFromResult
         #return vo
       }

       function delete(&$vo) {
         #execute delete statement
         #set id on vo to 0
       }

       #-- private functions

       function getFromResult(&vo, $result) {
         #fill vo from the database result set
       }

       function update(&$vo) {
          #execute update statement here
       }

       function insert(&$vo) {
         #generate id (from Oracle sequence or automatically)
         #insert record into db
         #set id on vo
       }
    }



# access functions and variables in the class with:

$this->your_variable_name;
$this->your_function_name();


public/private/static keywords


// static member variable
private static $verbose = 1;

// static function
        public static function something($data) {
//
}

...


Then you can call externally with:

TheClass::something($data);

or call variable with

if ( TheClass::$verbose ) {
//...
}



INCREASE MEMORY

edit PHP.ini

max_execution_time=1800
max_input_time=1800
memory_limit=128M

or set in  .htaccess

PHP_value max_execution_time 1800
PHP_value max_input_time 1800
PHP_value memory_limit 128M


to strip automatic slashes

PHP_value magic_quotes_gpc off

turn off error reporting

PHP_value display_errors off
PHP_value output_buffering 8096


do not stop script if user disconnects

int ignore_user_abort ([ string $value ] )



CHANGE HEADER

<?PHP
  # note: you can't manipulate headers after you write content to the browser
  header('Content-type: text/xml');
?>


SMTP WITH PEAR:


 <?PHP
 require_once "Mail.PHP";

 $host = "mail.emailsrvr.com";
 $username = "yourgmailuser@gmail.com";
 $password = "****************";

 $from = "test <name@domain.com>";
 $to = "Some Person <name@domain.com>";
 $subject = "Test email using PHP SMTP\r\n\r\n";
 $body = "This is a test email message";


 $headers = array ('From' => $from,
'To' => $to,
'Subject' => $subject);

 $smtp = Mail::factory('smtp',
array ('host' => $host,
 'auth' => true,
 'username' => $username,
 'password' => $password));

 $mail = $smtp->send($to, $headers, $body);

 if (PEAR::isError($mail)) {
echo("<p>" . $mail->getMessage() . "</p>");
 } else {
echo("<p>Message successfully sent!</p>");
 }
 ?>


 # notes: to send BCC, append email both
to header Bcc: $to
and "to1,$to" string


REFLECTION

    print_r( $obj )                          -- prints structure of hash
    var_dump( $obj )                         -- structure of hash, with types
    print_r( get_class_methods( $obj ) )     -- gets list of object methods.
    print_r( get_class_vars( $obj ) )        -- gets list of public vars


SET DEFAULT TIMEZONE

# store all data internally as GMT:

#   local tz 1 \
#   local tz 2  + <-> webservice.PHP <-> gmt tz
#   local tz 2 /

date_default_timezone_set('Zulu');



// timestamp to convert (just an example)
// can also use: DateTime( $date_string )
$timestamp = 1240349566;

// FROM GMT TO LOCAL
// set this to the time zone provided by the user
$tz = $_SESSION['tz'];
// create the DateTimeZone object for later
$dtzone = new DateTimeZone($tz);
// create a DateTime object
$dtime = new DateTime();
// set it to the timestamp (PHP >= 5.3.0)
$dtime->setTimestamp($timestamp);
// convert this to the user's timezone using the DateTimeZone object
$dtime->setTimeZone($dtzone);
// print the time using your preferred format
$time = $dtime->format('g:i A m/d/y');

// FROM LOCAL TO GMT
// time to convert (just an example)
$time = 'Tuesday, April 21, 2009 2:32:46 PM';
// set this to the time zone provided by the user
$tz = $_SESSION['tz'];
// create the DateTimeZone object for later
$dtzone = new DateTimeZone($tz);
// now create the DateTime object for this time and user time zone
$dtime = new DateTime($time, $dtzone);
// print the timestamp
$timestamp = $dtime->format('U');


DATE MATH

strtotime('last Sunday', strtotime('09/01/2010'));

# snap to date starting on sun (week of date)
if( ! defined( 'DATE_FORMAT' ) ) define ('DATE_FORMAT', 'Y-m-d H:i:s');
date_default_timezone_set('Zulu');
$tstart = strtotime($start) ;
$weekday  = date('w',$tstart);
if ( $weekday != 0 ) {
$start = date(DATE_FORMAT, strtotime('last Sunday',$tstart));
}
# end snap


JSON

$string = json_encode( $obj );
$obj = json_decode( $string, 1 ); // 1 for hash

$obj will be null on error, more info with:
$intcode = json_last_error()


REDIRECT FUNCTION EXAMPLE:

This should be called before page is rendered, or buffer is flushed any chars echo'd to screen before this call can break redirect if output buffer is flushed

function redirect ( $url ) {
    header( 'Location: ' . $url ) ;
    exit();
}


SIMPLE PHP PAGE EXAMPLE WITH VIEWS/CONTROLLER

$data     = $_REQUEST;
$action   = $data['action'];
$postback = $data['postback']; # set in hidden

# validation
if ( $postback ) {
if ( ! @$data['dateStart'] && ! @$data['dateEnd'] ) {
$data['message'] = 'Please select a date range';
$postback = 0;
} else if ( $action == 'report_referral' && ! @$data['reportType'] ) {
$data['message'] = 'Please select a report type';
$postback = 0;
}
}

# controller
switch( $action ) {

case 'report_complete':

echo view_report_search( $data );
if ( $postback ) {
# show search results on postback only
echo view_report_complete( $data );
}

break;

case 'report_billed':

echo view_report_billed( $data );
break;

default:
break;
}


# views

# take data, render to html
function view_report_complete( $data ) {
$html = '';
$html .= '<a href="?action=report_billed">test</a>';
return $html;
}

function view_report_billed( $data ) {
$html = '';
$html .= '2';
return $html;
}



PHP GOTCHAS


There are a couple features in PHP that are counter intuitive (as compared to other languages).

1. The PHP interpreter ignores the read/write/execute permission bits on a file.  That means anything with a .PHP extension, or, .PHP. in the name is an executable file (since Apache allows multiple file extensions).  So for example, if someone uploads a file named fake_image.PHP.gif, it will be executable.  This also means: all files uploaded must be renamed, and assigned one extension.

Also, apache supports the idea of multiple file extensions.  Depending on how the server is set up, image.PHP.gif may execute as a PHP file, because the name contains ".PHP."  Meaning, any uploaded file should be renamed.

2. There is no good way to handle threading on all platforms.  At best it is only possible on linux with either:

a. pcntl_fork  (on systems where lib is installed)
b. lower level system call using & to force process into background

3. By default arrays are copied by value, not by reference.

Well, more exactly:  PHP does not copy arrays until you modify them - internally, they are always passed by reference, only if you modify them does it create an actual copy.

To pass by reference, you can use a function signature:

function somefunction(&$argument){
 print_r($argument);
}

So, if you change somefunction($o) to somefunction(&$o), you can see a small increase in array time.

However, this only works if the argument has been assigned to a variable.

PHP generally has issues with nesting operations.  In old versions of PHP you coulndn'even t do:

somefunction( array(1,2,3) );

You had to do:

$silly = array(1,2,3);
somefunction( $silly );


Also for looping, if you expect to modify an array, you must use:

foreach ( $things as &$thing ) {
# now you have a reference to item in the array
}
unset ( $thing ); // break last ref

4. Variable scoping rules are not really what you might expect.  Most languages have the concept of a hierarchical scoping.  In PHP a function has no concept of a parent scope, they only exist in the global scope.

$variable1 = 1;
function a () {
# i can't see $variable1

$variable2 = 1;
function nested_example () {
# i can't see $variable2
# it's not global either
}

}

All functions are registered under a global namespace.

The only communication to a function (outside of the arguments) is via global declaration or $this for objects

$variable1 = 1;
function b () {
global $variable1
# now i can see $variable1
}

5. Functions can't be redefined, or undefined.  In javascript, the opposite is true.

6. Including files:

If a relative path is defined in an include, it's relative to the including script's path, not the included path's script.  This means that if you include the same file in two different scripts, the relative paths used in the included file may be wrong.

You can use  __FILE__ and dirname (__FILE__ ) to get the absolute location of an included script.

from docs:

If a path is defined whether absolute (starting with a drive letter or \ on Windows, or / on Unix/Linux systems) or relative to the current directory (starting with . or ..) the include_path will be ignored altogether. For example, if a filename begins with ../, the parser will look in the parent directory to find the requested file.

dirname(__FILE__) gets the directory of the file being included while dirname($_SERVER['SCRIPT_FILENAME']) gets the directory of the file including it. The script simply finds the difference in how far off the base directory the two are and prints the appropriate number of '../' before the URL.

NOTE: dirname(__FILE__) can be replaced by __DIR__ in PHP greater than or equal to 5.3.0

or, keep all includes in the same directory.

7. There's no 'strict' variable definition.  the most you can have is warnings if a varible has been used without being defined.  That means it's harder to see if a variable is mistyped.  You can turn on E_STRICT and view error messages, though depending on the libararies you use, the log can fill up with so much text that it's hard to see the worthwhile messages.

8. You'd think there would be an 8, but there isn't.  That in itself is very counter-intuitive. :)

9. false == 0 == null;  To compare equality more strictly, you must use ===

10. order of operations on ternary a?b:x is counter intuitive, ? is left associative.  Always use parans around the entire thing, or avoid altogether.

# won't work
echo " ... " . $test?1:0 . " ... ";
# prints: 1

# ok
echo " ... " . ($test?1:0) . " ... ";
  # prints: ... 0 ...

11. "magic quotes" aren't really magic, just a mistake in the language design.  Drop this in the .htaccess file to disable them:

PHP_value magic_quotes_gpc off

otherwise, PHP will corrupt all your quote data by added backslashes to it.  You'll start to see strings like this show up:  "And that\\\'s not good" :)

12. PHP automatically mangles names of variable data in forms.  For example, if you have an HTML form with <input name="x.1"> this will show up as a variable "x_1" when the form is submitted.  Stick with alphanum and underscore to be safe.

13: Unlike most languages, PHP functions may return more than one type of data.

this becomes relevant, for example, as functions like strpos return:

false (no match), and
0 (beginnging of string)

but:
false == 0

If you are coming from a strictly typed langauge, it can be confusing that a function in PHP can return two different types of values: a boolean, or an int.  Every other language returns -1 as a 'false' value


14: Also there is no option of "overloading" the function by different types.  Just pass in the two different types, and check for them inside the function, manually. for example:

function example( $a = null ) {

if ( $a === null ) {
// function was called without argument
} else if ( is_array( $a) ) {
// do somethign
} else {
// do somethign else
}
}

Though often it's clean to use the rule of thumb:

a function can have one argument (an array/hash), and return one array/hash.

For example

avoid:
function avoidthis ( $a , $b , $c , $d , $e , $f );

better:
function avoidthis ( $data = array() );
// where $data['a'], $data['b'] contain values of different types


PHP SECURITY CONSIDERATIONS

1. Don't trust user input.  Without some consideration, don't allow data the user submits to be used for:

including files
  echoing contents of files
  data piped straight to command line util
data piped straight to db
dynamically executing PHP functions
echoing directly to a web page

Ideally this data needs to be:

A. scrubbed (restricted to a known set of characters)
B. Restricted to known length.
C. and escaped for the context it is used in.

Avoid using any information that is supplied by the user that is related to a filesystem, rather manage the filesystem yourself and map the user data to the file with a database.

Remember that a chunk of data is really two things:
1. a peice of information
2. a representation in a given context (html, sql, javascript, PHP, ...)

2. Be careful with functions like $response = file_get_contents($url);  Not only will it retrieve a URL, but it will retrieve any file on the system.

3. Always rename files that are uploaded (rename with file extension).  Maintain a "whitelist" of safe file types: *.txt, *.gif, *.jpg, etc.

No comments: