Showing posts with label PHP. Show all posts
Showing posts with label PHP. Show all posts

Find the dates ( month start and end dates) between the given two dates using PHP

When you want to pull the dates between two dates, where the between dates are connecting dots. If you want to list the complete date ranges through months. For example, to find the dates between below dates

date1 = '2022-12-29';
date2 = '2023-02-20';

expected output would be 


Start DateEnd Date
2022-12-292022-12-31
2023-01-012023-01-31
2023-02-012023-02-20
 

<?php

/*
 * Returns array of month's start & end dates between provided dates.
 *
 * @param string $start_date
 * @param string $end_date
 * 
 *
 * @return array
 */
 function getDates($start_date, $end_date){
    $start_date_obj = new DateTime($start_date);
    $end_date_obj = new DateTime($end_date);
    
    $diff = $end_date_obj->diff($start_date_obj);
    $months = (($diff->y) * 12) + ($diff->m);
    
    $dates[] = array('start_date' => $start_date, 'end_date' => date('Y-m-t', strtotime($start_date)), 'suffix' => date('Y-m', strtotime($start_date)));
    $new_start_date = strtotime(date('Y-m-01', strtotime($start_date)));
    for($i = 1; $i <= $months; $i++){ 
        $new_start_date = strtotime("+1 months", $new_start_date);
        $dates[] = array('start_date' => date('Y-m-01', $new_start_date), 'end_date' => date('Y-m-t', $new_start_date) , 'suffix' => date('Y-m', $new_start_date));
    }
    $dates[] = array('start_date' => date('Y-m-01', strtotime($end_date)), 'end_date' => $end_date, 'suffix' => date('Y-m', strtotime($end_date)));
    
    return $dates;
}

$start_date = '2022-12-29';
$end_date = '2023-02-20';
$dates = getDates($start_date, $end_date);
print_r($dates);
?>



Output:
Array
(
    [0] => Array
        (
            [start_date] => 2022-12-29
            [end_date] => 2022-12-31
            [Ym] => 2022-12
        )

    [1] => Array
        (
            [start_date] => 2023-01-01
            [end_date] => 2023-01-31
            [Ym] => 2023-01
        )

    [2] => Array
        (
            [start_date] => 2023-02-01
            [end_date] => 2023-02-20
            [Ym] => 2023-02
        )

)

PHP Code Review Guidelines

General
  •  The code works
  •  The code is easy to understand
  •  Follows coding conventions
  •  Names are simple and if possible short
  •  Names are spelt correctly
  •  Names contain units where applicable
  •  There are no usages of magic numbers
  •  No hard coded constants that could possibly change in the future
  •  All variables are in the smallest scope possible
  •  There is no commented out code
  •  There is no dead code (inaccessible at Runtime)
  •  No code that can be replaced with library functions
  •  Variables are not accidentally used with null values
  •  Variables are immutable where possible
  •  Code is not repeated or duplicated
  •  There is an else block for every if clause even if it is empty
  •  No complex/long boolean expressions
  •  No negatively named boolean variables
  •  No empty blocks of code
  •  Ideal data structures are used
  •  Constructors do not accept null/none values
  •  Catch clauses are fine grained and catch specific exceptions
  •  Exceptions are not eaten if caught, unless explicitly documented otherwise
  •  Files/Sockets and other resources are properly closed even when an exception occurs in using them
  •  null is not returned from any method
  •  == operator and === (and its inverse !==) are not mixed up
  •  Floating point numbers are not compared for equality
  •  Loops have a set length and correct termination conditions
  •  Blocks of code inside loops are as small as possible
  •  No methods with boolean parameters
  •  No object exists longer than necessary
  •  No memory leaks
  •  Code is unit testable
  •  Test cases are written wherever possible
  •  Methods return early without compromising code readability
  •  Performance is considered
  •  Loop iteration and off by one are taken care of

Architecture

  •  Design patterns if used are correctly applied
  •  Law of Demeter is not violated
  •  A class should have only a single responsibility (i.e. only one potential change in the software's specification should be able to affect the specification of the class)
  •  Classes, modules, functions, etc. should be open for extension, but closed for modification
  •  Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program
  •  Many client-specific interfaces are better than one general-purpose interface.
  •  Depend upon Abstractions. Do not depend upon concretions.

API

  •  APIs and other public contracts check input values and fail fast
  •  API checks for correct oauth scope / user permissions
  •  Any API change should be reflected in the API documentation
  •  APIs return correct status codes in responses

Logging

  •  Logging should be easily discoverable
  •  Required logs are present
  •  Frivolous logs are absent
  •  Debugging code is absent
  •  No print_rvar_dump or similar calls exist
  •  No stack traces are printed

Documentation

  •  Comments should indicate WHY rather that WHAT the code is doing
  •  All methods are commented in clear language.
  •  Comments exist and describe rationale or reasons for decisions in code
  •  All public methods/interfaces/contracts are commented describing usage
  •  All edge cases are described in comments
  •  All unusual behavior or edge case handling is commented
  •  Data structures and units of measurement are explained

Security

  •  All data inputs are checked (for the correct type, length/size, format, and range)
  •  Invalid parameter values handled such that exceptions are not thrown
  •  No sensitive information is logged or visible in a stacktrace

Week dates between two dates

To list week's start date & end date between given two dates. It also includes number of dates in every set. It allows you to list only those weeks having total seven days. Here the starting day of the week is Monday & the end day of the week is Sunday.
/*
     * Returns array of week's start & end dates with number of days between those.
     *
     * @param string $start_date
     * @param string $end_date
     * @param boolean $only_full_week
     *
     * @return array
     */
    function getWeekDates($start_date, $end_date, $only_full_week = false)
    {
        $stime = strtotime($start_date);
        $etime = strtotime($end_date);

        $weeks = array();

        $i = 0;
        $j = 1;
        while ($stime <= $etime) {
            if ($i == 0 && $j == 1) {
                $weeks[$i]['start_date'] = date('Y-m-d', $stime);
                $weeks[$i]['end_date'] = date('Y-m-d', $stime);
                $weeks[$i]['count'] = $j;
            } else if (date('N', $stime) == 1) {
                $j = 1;
                $weeks[$i]['start_date'] = date('Y-m-d', $stime);
                $weeks[$i]['end_date'] = date('Y-m-d', $stime);
                $weeks[$i]['count'] = $j;
            }

            if (date('N', $stime) == 7) {
                $weeks[$i]['end_date'] = date('Y-m-d', $stime);
                $weeks[$i]['count'] = $j;

                $i ++;
            }
            $j ++;
            $stime = strtotime('+1 day', $stime);
        }
        if ($only_full_week) {
            foreach ($weeks as $key => $week) {
                if ($week['count'] != 7) {
                    unset($weeks[$key]);
                }
            }
        }

        return array_values($weeks);
    }

Here is the example:

$start_date = '2018-12-23';
$end_date = '2019-01-08';
print_r(getWeekDates($start_date, $end_date));
Output:
Array
(
    [0] => Array
        (
            [start_date] => 2018-12-23
            [end_date] => 2018-12-23
            [count] => 1
        )

    [1] => Array
        (
            [start_date] => 2018-12-24
            [end_date] => 2018-12-30
            [count] => 7
        )

    [2] => Array
        (
            [start_date] => 2018-12-31
            [end_date] => 2019-01-06
            [count] => 7
        )

    [3] => Array
        (
            [start_date] => 2019-01-07
            [end_date] => 2019-01-07
            [count] => 1
        )

)
If we need only full weeks, include the third parameter as 'true'

$start_date = '2018-12-23';
$end_date = '2019-01-08';
print_r(getWeekDates($start_date, $end_date, true));
Output:
Array
(
    [0] => Array
        (
            [start_date] => 2018-12-24
            [end_date] => 2018-12-30
            [count] => 7
        )

    [1] => Array
        (
            [start_date] => 2018-12-31
            [end_date] => 2019-01-06
            [count] => 7
        )

)

Format Sentences, format number and remove extra spaces. All in one solution

We had a requirement, where we were asked to format the content from a big database table. And the format includes making sentences to sentence case, format simple numbers to comma separated numbers within those sentences and remove the extra spaces. Here is the solution. Hope this helps you.
function formatString($string){
 //number formatting
 $string1 = preg_replace_callback('/\d+/', function($match){return number_format($match[0]);}, $string);
 
 //removing extra spaces
 $string2 = preg_replace('/\s+/', ' ', $string1);
 
 //sentence case
 $sentences = preg_split('/([.?!]+)/', $string2, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
    $new_string = '';
    foreach ($sentences as $key => $sentence) {
        $new_string .= ($key & 1) == 0?
            ucfirst(strtolower(trim($sentence))) :
            $sentence.' ';
    }
    return trim($new_string);
}

$str = "it was an awesome               day. i bought shares for 23000      and sold at 27000 in just 2        hours. i had a profit of 4000 INR. isn't it great?";
echo formatString($str);

Huge data Excel download using PHP

Ah! It was always a problem to download huge data in excel format. Tried the best library available 'PHPExcel' but the issue persists with memory limit and time taken to generate and download.

But here is the promising solution found after spending ages searching on Google. You can find it here. The author himself says "Never run out of memory with PHPExcel again".

You need to download it and include xlsxwriter.class.php where you want to fetch the data from DB and through it on to browser or save to a particular location.

Let's get started.
Below is the logic to generate the file and store in the current directory. If you want to store is at the desired location, specify the location for the method writeToFile.

include_once("xlsxwriter.class.php");
$writer = new XLSXWriter();
$data = array(
    array('year','month','amount'),
    array('2003','1','220'),
    array('2003','2','153.5'),
);

$writer = new XLSXWriter();
$writer->writeSheet($data);
$writer->writeToFile('output.xlsx');
In case if you want to download it on the fly use the below logic.
include_once("xlsxwriter.class.php");
$writer = new XLSXWriter();
$data = array(
    array('year','month','amount'),
    array('2003','1','220'),
    array('2003','2','153.5'),
);
$filename = 'output.xlsx';

$writer = new XLSXWriter();
$writer->writeSheet($data);

header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');

$writer->writeToStdOut();
Through this library, you can even set the column type. Please visit the official URL for more information.

Generate documentation for PHP Code using phpDocumentor

What is phpDocumentor?
phpDocumentor is the simple and best tool to generate API documentation for PHP source code. 


Downloading & installing phpDocumentor:
There are several dependencies to install phpDocumentor you can find them on official website i.e., here

To install phpDocumentor using PEAR type below command
  • In windows, run command prompt as administrator. Goto xampp/php folder and type
    pear channel-discover pear.phpdoc.org
    pear install phpdoc/phpDocumentor
  • In linux, open terminal and type (if install xampp for linux then goto xampp/bin folder)
    pear channel-discover pear.phpdoc.org
    pear install phpdoc/phpDocumentor
Generate API using phpDocumentor:
  • In windows, goto  phpdoc.bat folder and run phpdoc  command for source directory to destination
     phpdoc -d <source directory of php code> -t <destination directory>
  • In Linux, goto [phpdocumentor installation folder]/bin/ and run command
     phpdoc -d <source directory of php> -t <destination directory>
Note:
If in case, installation method through PEAR gives you below error. Try downloading phar and proceed
Fatal error: Uncaught TypeError: Argument 1 passed to Monolog\ErrorHandler::handleException() must be an instance of Exception...

Download .phar from here
To generate API run the below command from php directory with the location of .phar file
php phpDocumentor.phar -d <source directory of php code> -t <destination directory>

If you find any error related to Graphviz, download and install software from here. Set the environment variable for Graphviz/bin directory for 'dot' executable file.

Google like Pagination

In the beginning of my career, every technical interview has this question; "How do you write pagination script in PHP?". I think you too faced the same issue. It was very important to answer this because every application is developed to list set of information and off-course you can't show every thing in a single page. It will irritate the user to scroll to the bottom of page to read.

So, the pagination. Divide the list of items in number of pages and navigate to each by clicking on each page number displayed. At the beginning, the pagination was simple was limited to 10-15 pages, but after some time in real time the requirement has grown to 10 multiple pages. Solution was to show first few page numbers, last few page numbers and in middle show the ellipsis(...). As you navigate to any page it would show definite page numbers to both the sides.


/**
 * Pagination file
 * 
 * This file provides the google like pagination solution
 * 
 * @category Saral
 * @package Pager
 * @version  0.1
 * @since  0.1
 */

/**
 * Pager class
 *
 * Class is used generate google like pagination links
 *
 * @category Saral
 * @package Pager
 * @version Release: 0.1
 * @since 26.Nov.2013
 * @author Sailesh Jaiswal
 */
class Pager
{

    /**
     *
     * css class name for links container
     *
     * @var string
     */
    private $class_pagination = 'pagination';

    /**
     *
     * css class name for default style for links
     *
     * @var string
     */
    private $class_default = 'page';

    /**
     *
     * css class name for disabled style for links
     *
     * @var string
     */
    private $class_disabled = 'disabled';

    /**
     *
     * css class name for current/selected link style
     *
     * @var string
     */
    private $class_current = 'current';

    /**
     *
     * used to set the css classes for links
     *
     * @param string $pagination            
     * @param string $default            
     * @param string $disabled            
     * @param string $current            
     */
    function setStyles($pagination = '', $default = '', $disabled = '', $current = '')
    {
        if ($pagination != '') {
            $this->class_pagination = $pagination;
        }
        if ($default != '') {
            $this->class_default = $default;
        }
        if ($disabled != '') {
            $this->class_disabled = $disabled;
        }
        if ($current != '') {
            $this->class_current = $current;
        }
    }

    /**
     *
     * generates pagination links
     *
     * @param string $url   url of the page         
     * @param integer $current_page   current page         
     * @param integer $total_records   total records         
     * @param integer $rpp            records per page
     * @param integer $adjacents      how many pages to show both the side of the current page in case if there are plenty of pages      
     * @return string
     */
    function showLinks($url, $current_page, $total_records, $rpp = 5, $adjacents = 2)
    {
        $class_pagination = $this->class_pagination;
        $page = $this->class_default;
        $current = $this->class_current;
        $disabled = $this->class_disabled;
        
        $pagination = "";
        $prev = $current_page - 1; // previous page is page - 1
        $next = $current_page + 1; // next page is page + 1
        $lastpage = ceil($total_records / $rpp); // lastpage is = total pages / items per page, rounded up.
        $lpm1 = $lastpage - 1; // last page minus 1
        $prev = $url . '?page=' . $prev;
        if ($lastpage > 1) {
            $pagination .= "<div><ul class=\"$class_pagination\">";
            // previous button
            if ($current_page > 1)
                $pagination .= "<li><a href=\"$prev\" class=\"$page\"> << previous</a></li>";
            else
                $pagination .= "<li><span class=\"$disabled\"> << previous</span></li>";
            
            // pages
            if ($lastpage < 7 + ($adjacents * 2)) // not enough pages to bother breaking it up
            {
                for ($counter = 1; $counter <= $lastpage; $counter ++) {
                    $i = $url . '?page=' . $counter;
                    if ($counter == $current_page)
                        $pagination .= "<li class=\"$current\"><a>$counter</a></li>";
                    else
                        $pagination .= "<li><a href=\"$i\" class=\"$page\">$counter</a></li>";
                }
            } elseif ($lastpage > 5 + ($adjacents * 2)) // enough pages to hide some
            {
                // close to beginning; only hide later pages
                if ($current_page < 1 + ($adjacents * 2)) {
                    for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter ++) {
                        $i = $url . '?page=' . $counter;
                        if ($counter == $current_page)
                            $pagination .= "<li class=\"$current\"><a>$counter</a></li>";
                        else
                            $pagination .= "<li><a href=\"$i\" class=\"$page\">$counter</a></li>";
                    }
                    $lpm11 = $url . '?page=' . $lpm1;
                    $lastpage1 = $url . '?page=' . $lastpage;
                    $pagination .= "<li><a href='javascript: void(0)'>...</a></li>";
                    $pagination .= "<li><a href=\"$lpm11\" class=\"$page\">$lpm1</a></li>";
                    $pagination .= "<li><a href=\"$lastpage\" class=\"$page\">$lastpage</a></li>";
                } // in middle; hide some front and some back
                elseif ($lastpage - ($adjacents * 2) > $current_page && $current_page > ($adjacents * 2)) {
                    $pagination .= "<li><a href=\"$url?page=1\" class=\"$page\">1</a></li>";
                    $pagination .= "<li><a href=\"$url?page=2\" class=\"$page\">2</a></li>";
                    $pagination .= "<li><a href='javascript: void(0)'>...</a></li>";
                    for ($counter = $current_page - $adjacents; $counter <= $current_page + $adjacents; $counter ++) {
                        $i = $url . '?page=' . $counter;
                        if ($counter == $current_page)
                            $pagination .= "<li class=\"$current\"><a>$counter</a></li>";
                        else
                            $pagination .= "<li><a href=\"$i\" class=\"$page\">$counter</a></li>";
                    }
                    $lpm11 = $url . '?page=' . $lpm1;
                    $lastpage1 = $url . '?page=' . $lastpage;
                    $pagination .= "<li><a href='javascript: void(0)'>...</a></li>";
                    $pagination .= "<li><a href=\"$lpm11\" class=\"$page\">$lpm1</a></li>";
                    $pagination .= "<li><a href=\"$lastpage1\" class=\"$page\">$lastpage</a></li>";
                } // close to end; only hide early pages
                else {
                    $pagination .= "<li><a href=\"$url?page=1\" class=\"$page\">1</a></li>";
                    $pagination .= "<li><a href=\"$url?page=2\" class=\"$page\">2</a></li>";
                    $pagination .= "<li><a href='javascript: void(0)'>...</a></li>";
                    for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter ++) {
                        $i = $url . '?page=' . $counter;
                        if ($counter == $current_page)
                            $pagination .= "<li class=\"$current\"><a>$counter</a></li>";
                        else
                            $pagination .= "<li><a href=\"$i\" class=\"$page\">$counter</a></li>";
                    }
                }
            }
        
            // next button
            if ($current_page < $counter - 1) {
                $next = $url . '?page=' . $next;
                $pagination .= "<li><a href=\"$next\" class=\"$page\">next >></a></li>";
            } else {
                $pagination .= "<li><span class=\"$disabled\">next >></span></li>";
            }
            $pagination .= "</ul></div>\n";
        }
        return $pagination;
    }
}

$total_records = 100; //total records that you want to show as paginated
$page = $_GET['page'];
$pager = new Pager;
echo $pager->showLinks("http://example.com/posts.php", $page, $total_records, 10, 2);

Sorting second dimension array

We can sort the second dimension array like table, for example


$users = array(
	array('name' => 'Mr. B', 'age' => 34), 
	array('name' => 'Mr. A', 'age' => 33),
	array('name' => 'Mr. C', 'age' => 32)
);

If you want to sort the array based on the name or age, here is the solution:



function arraySortByColumn(&$arr, $col, $dir = SORT_ASC){
	$sort_col = array();
	foreach ($arr as $key => $row) {
		$sort_col[$key] = $row[$col];
	}
	array_multisort($sort_col, $dir, $arr);
}

arraySortByColumn($users, 'name', SORT_DESC);
print_r($users);

Find relative duration of an event using PHP for given date time (seconds ago, minutes ago, hours ago, days ago, weeks ago, months ago, years ago)

You might have seen that many of the live applications shows the time relative to when it was posted as seconds ago, minutes ago, hours ago, days ago, weeks ago, month ago, year ago and so on. Instead of showing the whole date and time of any possible action on the application it is shown with smaller units, more simpler ones like a minute ago, 15 minutes and 10 seconds ago etc. Here is how we achieve it using PHP, below are two methods that can be used, it requires a Date Time string in YYYY-MM-DD HH:II:SS format.


Method One:

  /**
     * returns the time ago in string
     * @param string $date_time
     *
     * @return string
     */
    function timeAgo($date_time)
    {
        $time_ago = strtotime($date_time);
        $cur_time = time();
        $time_elapsed = $cur_time - $time_ago;
        $seconds = $time_elapsed;
        $minutes = round($time_elapsed / 60);
        $hours = round($time_elapsed / 3600);
        $days = round($time_elapsed / 86400);
        $weeks = round($time_elapsed / 604800);
        $months = round($time_elapsed / 2600640);
        $years = round($time_elapsed / 31207680);
        $str = ''; // Seconds
        if ($seconds <= 60) {
            $str = "few seconds ago";
        } // Minuteselse
        if ($minutes <= 60) {
            if ($minutes == 1) {
                $str = "one minute ago";
            } else {
                $str = "$minutes minutes ago";
            }
        } // Hours
        else if ($hours <= 24) {
            if ($hours == 1) {
                $str = "an hour ago";
            } else {
                $str = "$hours hours ago";
            }
        } // Days
        else if ($days <= 7) {
            if ($days == 1) {
                $str = "yesterday";
            } else {
                $str = "$days days ago";
            }
        } // Weeks
        else if ($weeks <= 4.3) {
            if ($weeks == 1) {
                $str = "a week ago";
            } else {
                $str = "$weeks weeks ago";
            }
        } // Months
        else if ($months <= 12) {
            if ($months == 1) {
                $str = "a month ago";
            } else {
                $str = "$months months ago";
            }
        } // Years
        else {
            if ($years == 1) {
                $str = "one year ago";
            } else {
                $str = "$years years ago";
            }
        }
        return $str;
    }
ex:
echo timeAgo('2015-01-17 14:20:00'); 
o/p: 31 minutes ago

Method Two: in detail

  /**
     * gives time ago in detailed
     *
     * @param string $date_time
     * @return string
     */
    function timeAgo($date_time)
    {
        $then = new DateTime($date_time);
        $now = new DateTime();
        $delta = $now->diff($then);
        $quantities = array(
            'year' => $delta->y,
            'month' => $delta->m,
            'day' => $delta->d,
            'hour' => $delta->h,
            'minute' => $delta->i,
            'second' => $delta->s
        );
        $str = '';
        foreach ($quantities as $unit => $value) {
            if ($value == 0)
                continue;
            $str .= $value . ' ' . $unit;
            if ($value != 1) {
                $str .= 's';
            }
            $str .= ', ';
        }
        $str = $str == '' ? 'a moment ' : substr($str, 0, - 2);
        return $str . ' ago';
    }
    
ex:
echo timeAgo('2015-01-17 14:20:00'); 
o/p: 31 minutes, 14 seconds ago

Convert Date Time to GMT/UTC

If you want to convert any date time to GMT/UTC for given timezone (observing DST/not).

/**
     * coverts the given date time to GMT/UTC based on timezone provided
     * @param string $date
     * @param string $timezone
     * @return string
     */
    function getGMT($date, $timezone){
        date_default_timezone_set("UTC");
        $daylight_savings_offset_in_seconds = timezone_offset_get( timezone_open( $timezone ), new DateTime() );
        return $new_date = date('Y-m-d H:i:s', strtotime('-'.$daylight_savings_offset_in_seconds.' seconds', strtotime($date)));
    }



$date = "2014-11-30 23:50:00"; //yyyy-mm-dd
//$date = "11/30/2014 23:50:00"; //mm/dd/yyyy
//$date = "30-11-2014 23:50:00"; //dd-mm-yyyy
$timezone = "Asia/Kolkata";
//$timezone = "Australia/Adelaide";
//$timezone = "America/New_York";

echo getGMT($date, $timezone);

Find Random Coordinates in the proximity

If you are asked to find the random coordinates (latitude & longitude) considering that your latitude & longitude as the center and with in the given proximity (radius), then this service will help to get one such coordinate. An example, you are at X location (17.414472, 78.449024) and radius 1 mile.



 /**
     * picks the random latitude & longitude with respect to given within the provided radius
     *
     * @param array $centre    (lat, lng)
     * @param number $radius           
     * @return array:
     */
    function getCoordinates($centre, $radius)
    {
        $radius_earth = 3959; // miles
                             
        // Pick random distance within $distance;
        $distance = lcg_value() * $radius;
       
        // Convert degrees to radians.
        $centre_rads = array_map('deg2rad', $centre);
       
        // First suppose our point is the north pole.
        // Find a random point $distance miles away
        $lat_rads = (pi() / 2) - $distance / $radius_earth;
        $lng_rads = lcg_value() * 2 * pi();
       
        // ($lat_rads,$lng_rads) is a point on the circle which is
        // $distance miles from the north pole. Convert to Cartesian
        $x1 = cos($lat_rads) * sin($lng_rads);
        $y1 = cos($lat_rads) * cos($lng_rads);
        $z1 = sin($lat_rads);
       
        // Rotate that sphere so that the north pole is now at $centre.
       
        // Rotate in x axis by $rot = (pi()/2) - $centre_rads[0];
        $rot = (pi() / 2) - $centre_rads[0];
        $x2 = $x1;
        $y2 = $y1 * cos($rot) + $z1 * sin($rot);
        $z2 = - $y1 * sin($rot) + $z1 * cos($rot);
       
        // Rotate in z axis by $rot = $centre_rads[1]
        $rot = $centre_rads[1];
        $x3 = $x2 * cos($rot) + $y2 * sin($rot);
        $y3 = - $x2 * sin($rot) + $y2 * cos($rot);
        $z3 = $z2;
       
        // Finally convert this point to polar co-ords
        $lng_rads = atan2($x3, $y3);
        $lat_rads = asin($z3);
       
        return array_map('rad2deg', array(
            $lat_rads,
            $lng_rads
        ));
    }


    $my_location = array(17.414472, 78.449024);
    $radius = 1;
    print_r(getCoordinates($my_location, $radius));

Get Latitude & Longitude from address

function getLatLong($address){
    $prep_addr = str_replace(' ','+',$address);

    $geocode = file_get_contents('http://maps.google.com/maps/api/geocode/json?address='.$prep_addr.'&sensor=false');

    $output= json_decode($geocode);

    $lat = $output->results[0]->geometry->location->lat;
    $long = $output->results[0]->geometry->location->lng;

    return array("latitude" => $lat, "longitude" => $long);
}

Read .docx file as string

I found it after googling for a day, finally a function that reads the .docx file and return you a string. Hats off to the original author of this function.

function readDocx($file_name){
    $striped_content = '';
    $content = '';
    if(!$file_name || !file_exists($file_name))
        return false;

    $zip = zip_open($file_name);
    if (!$zip || is_numeric($zip))
        return false;

    while ($zip_entry = zip_read($zip)) {
        if (zip_entry_open($zip, $zip_entry) == FALSE)
            continue;
        if (zip_entry_name($zip_entry) != "word/document.xml")
            continue;
        $content .= zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
        zip_entry_close($zip_entry);
    }// end while
    zip_close($zip);

    $content = str_replace('</w:r></w:p></w:tc><w:tc>', " ", $content);
    $content = str_replace('</w:r></w:p>', "\r\n", $content);
    $striped_content = strip_tags($content);
    return $striped_content;
}

Convert object to multi dimensional array

   /**
    *
    * Convert an object to an array
    *
    * @param    object  $object The object to convert
    * @reeturn      array
    *
    */
    
function objectToArray$object )
    {
        if( !
is_object$object ) && !is_array$object ) )
        {
            return 
$object;
        }
        if( 
is_object$object ) )
        {
            
$object get_object_vars$object );
        }
        return 
array_map'objectToArray'$object );
    }

    
/*** convert the array to object ***/
    
$array objectToArray$obj );

    
/*** show the array ***/
    
print_r$array );


 Taken from phpro.org

Windows Azure SQL Database PDO

I faced a problem while trying to connect Windows Azure SQL Database from local system using PDO. Here I'm using PHP 5.4+ and Apache 2.4 on Windows 8. I didn't find better tutorial for trouble shooting.

Following are the steps that explains you what to do..

  1. Download php_pdo_sqlsrv_54_ts.dll and placed it in php/ext directory. Here you can find it
    http://www.microsoft.com/en-us/download/details.aspx?id=20098
    Download SQLSRV30.EXE and extracted to php/ext directory
  2. Open the php/php.ini file and added the following line
    extension=php_pdo_sqlsrv_54_ts.dll
  3. It needs Microsoft SQL Server 2012 Native Client, to go further. So download it from
    http://www.microsoft.com/en-us/download/confirmation.aspx?id=29065
    for 32bits(x86)
    http://go.microsoft.com/fwlink/?LinkID=239647&clcid=0x409
    for 64bits(x64)
    http://go.microsoft.com/fwlink/?LinkID=239648&clcid=0x409
  4. Restart the Apache server.
  5. Write the following code in php file to connect.
    $server_url = "xxxxxxx.database.windows.net,1433";
    $db_name = "database_name";
    $db_user = "db_username";
    $db_pwd = "db_pword";
    $conn = new PDO("sqlsrv:Server=tcp:$server_url;Database=$db_name", $db_username, $db_pwd);
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
  6. Fetching data from table
    $query = "SELECT * FROM categories";
      $stmt = $conn->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
      $stmt->excute();
    
      while($cat = $stmt->fetch(PDO::FETCH_ASSOC)){
        echo $cat['cat_id']."---".$cat['cat_name']."<br />";
      }
    
That's it...

Download file using PHP

Downloading a file can be done in two ways:

  1. Direct download
  2. Indirect download
Direct Download:
Assumptions:
File location: c:/xampp/htdocs/project/docs/test.doc
URL: http://localhost/project/docs/test.doc

Code:  <a href='http://localhost/project/docs/test.doc'>click here</a>

Indirect Download:
(recommended)
Assumptions:
File location: c:/xampp/htdocs/project/docs/test.doc
PHP file for the download code:
location: c:/xampp/htdocs/project/download.php

<?php
  $file_name = $_GET['file'];
  $file_path = "docs/".$file_name;

 //setting the content type
  header('content-type: application/octet-stream');

  //downloads file as attachment
  header("content-disposition: attachment; filename='$file_name'");

  //actual file path
  readfile($file_path);

?>

Note: content-type application/octet-stream can be used for any type of file.

To use the above file for download. Create link like as below
<a href='http://localhost/project/download.php?file=test.doc'>click here</a>

For any queries or clarifications post a comment.

PHP Best Practices

This guide will give you solutions to common PHP design problems. It also provides a sketch of an application layout that I developed during the implementation of some projects.
php.ini quirks
Some settings in the php.ini control how PHP interpretes your scripts. This can lead to unexpected behaviour when moving your application from development to the productive environment. The following measures reduce dependency of your code on php.ini settings.
short_open_tagAlways use the long PHP tags: php
echo "hello world"; ?>
Do not use the echo shortcut .
asp_tagsDo not use ASP like tags: <% echo "hello world"; %>
gpc_magic_quotesI recommend that you include code in a global include file which is run before any $_GET or $_POST parameter or $_COOKIE is read. That code should check if the gpc_magic_quotes option is enabled and run all $_GET, $_POST and $_COOKIE values through the stripslashes function.
register_globalsNever rely on this option beeing set. Always access all GET, POST and COOKIE values through the 'superglobal' $_GET, $_POST and $_COOKIE variables. For convenience declare $PHP_SELF = $_SERVER['PHP_SELF']; in your global include file after the gpc_magic_quotes quirk.
File uploads:The maximum size of an uploaded file is determined by the following parameters:

  • file_uploads must be 1 (default)
  • memory_limit must be slightly larger than the post_max_size and upload_max_filesize
  • post_max_size must be large enough
  • upload_max_filesize must be large enough
Have one single configuration file
You should define all configuration parameters of your application in a single (include) file. This way you can easily exchange this file to reflect settings for your local development site, a test site and the customer's production environment. Common configuration parameters are:

  • database connection parameters
  • email addresses
  • options
  • debug and logging output switches
  • application constants
Keep an eye on the namespace
As PHP does not have a namespace facility like Java packages, you must be very careful when choosing names for your classes and functions.

  • Avoid functions outside classes whenever possible and feasible. Classes provide some extra namespace for the methods and variables that live inside them.
  • If you declare global functions use a prefix. Some examples are dao_factory(),db_getConnection(), text_parseDate() etc.
Use a database abstraction layer
In PHP there are no database-independent functions for database access apart from ODBC (which nobody uses on Linux). You should not use the PHP database functions directly because this makes it expensive when the database product changes. Your customer may move from MySQL to Oracle one day or you will need an XML database maybe. You never know. Moreover an abstraction layer can ease development as the PHP database functions are not very userfriendly.Use Value Objects (VO)
VOs are actually a J2EE pattern. It can easily be implemented in PHP. A value object corresponds directly to a C struct. It's a class that contains only member variables and no methods other than convenience methods (usually none). A VO corresponds to a business object. A VO typically corresponds directly to a database table. Naming the VO member variables equal to the database fields is a good idea. Do not forget the ID column.class Person {
var $id, $first_name, $last_name, $email;
}
Use Data Access Objects (DAO)
DAO is actually a J2EE pattern. It can easily be implemented in PHP and helps greatly in separating database access from the rest of your code. The DAOs form a thin layer. The DAO layer can be 'stacked' which helps for instance if you want to add DB caching later when tuning your application. You should have one DAO class for every VO class. Naming conventions are a good practice. 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
}
}
A DAO typically implements the following methods:

  • save: inserts or updates a record
  • get: fetches a record
  • delete: removes a record
The DAO may define additional methods as required by your application's needs. The should only perform actions that require the database (maybe only for performance reasons) and can not be implemented in a different mannor. Examples: isUsed(), getTop($n), find($criteria).
The DAO should only implement basic select / insert / update operations on onetable. It must not contain the business logic. For example the PersonDAO should not contain code to send email to a person. For n-to-n relationships create a separate DAO (and even a VO if the relationships has additional properties) for the relation table.
Write a factory function that returns the proper DAO given the class name of a VO.Caching is a good idea here. function dao_getDAO($vo_class) {
$conn = db_conn('default'); #get a connection from the pool
switch ($vo_class) {
case "person": return new PersonDAO($conn);
case "newsletter": return new NewsletterDAO($conn);
...
}
}
Generate code
99% of the code for your VOs and DAOs can be generated automatically from your database schema when you use some naming conventions for your tables and columns. Having a generator script ready saves you time when you are likely to change the database schema during development. I successfully used a perl script to generate my VOs and DAOs for a project. Unfortunately I am not allowed to post it here.Business logic
Business logic directly reflects the use cases. The business logic deals with VOs, modifies them according to the business requirements and uses DAOs to access the persistence layer. The business logic classes should provide means to retrieve information about errors that occurred. class NewsletterLogic {
function NewsletterLogic() {
}
function subscribePerson(&$person) {
...
}
function unsubscribePerson(&$person) {
...
}
function sendNewsletter(&$newsletter) {
...
}
}
Page logic (Controller)
When a page is called, the page controller is run before any output is made. The controller's job is to transform the HTTP request into business objects, then call the approriate logic and prepare the objects used to display the response.The page logic performs the following steps:
1. The cmd request parameter is evaluated. 2. Based on the action other request parameters are evaluated.3. Value Objects (or a form object for more complex tasks) are created from the parameters.4. The objects are validated and the result is stored in an error array.5. The business logic is called with the Value Objects.6. Return status (error codes) from the business logic is evaluated.7. A redirect to another page is executed if necessary.8. All data needed to display the page is collected and made available to the page as variables of the controller. Do not use global variables.
Note: it is a good idea to have a utility function that returns a parameter that is sent via GET or POST respectivly and provide a default value if the parameter is missing. The page logic is the only non-HTML include file in the actual page! The page logic file must include all other include files used by the logic (see base.inc.php below). Use the require_once PHP command to include non-HTML files.class PageController {
var $person; #$person is used by the HTML page
var $errs;
function PageController() {
$action = Form::getParameter('cmd');
$this->person = new Person();
$this->errs = array();
if ($action == 'save') {
$this->parseForm();
if (!this->validate()) return;
NewsletterLogic::subscribe($this->person);
header('Location: confirmation.php');
exit;
}
}
function parseForm() {
$this->person->name = Form::getParameter('name');
$this->person->birthdate = Util::parseDate(Form::getParameter('birthdate');
...
}
function validate() {
if ($this->person->name == '') $this->errs['name'] = FORM_MISSING;
#FORM_MISSING is a constant
...
return (sizeof($this->errs) == 0);
}
}
Presentation Layer
The top level page will contain the actual HTML code. You may include HTML parts that you reuse across pages like the navigation etc. The page expects the page logic to prepare all business objects that it needs. It's a good idea to document the business objects needed at the top of the page.The page accesses properties of those business objects and formats them into HTML. <?php require_once('control/ctl_person.inc.php'); #the page controller
$c =& new PageController();
?>
<html>
<body>
<form action="<?php echo htmlspecialchars($PHP_SELF) ?>" method="POST">
<input type="hidden" name="cmd" value="save">
<input type="text" name="name"
value="<?php echo htmlspecialchars($c->person->name); ?>">
<button type="submit">Subscribe</button>
</form>
</body>
</html>
Localization
Localization is a problem. You must choose among a) duplicating pages b) removing all hardcoded strings from your HTML.
As I work in a design company I usually take approach a). Approach b) is not feasible as it makes the HTML very hard to read and nearly impossible to edit in a visual web editor like Dreamweaver. Dynamic content is hard enough to edit with Dreamweaver. Removing also all strings, makes the page look quite empty...
So finish the project in one language first. The copy the HTML pages that need translation. Use a naming convention like index_fr.php to designate the French version of the index page. Always use the ISO two letter language codes. Do not invent your own language codes.
To keep track of the language the user selected you must choose among a) storing the language setting in a session variable or cookieb) reading the preferred language (locale) from the HTTP headers the browser sends youc) appending the language to the URL of every link in your application
While a) seems a lot more easier than c) it may be subject to session timeout. Option b) should only be implemented as an extension to a) or c). Strings in a database must be localized too!Making your application location independent
PHP has problems in some situations when include files are nested and reside in different folders and it is unclear at which directory level the file will be included. One can solve this by using absolute path names or using $_SERVER['DOCUMENT_ROOT']as a starting point. However this makes your code location dependent - it will not run anymore if you move it down your directory structure one level. Of cource we do not like that.I have found a convenient solution to this problem. The toplevel page (the one that is called by the browser) needs to know the relative path to the application root directory. Unfortunately there is no such function in PHP and the webapp context concept is completely absent in PHP. So we can not automatically determine the application root reliably in all situations (It is *really* impossible. Don't even try. It's not worth the effort.)Let's define a global variable called $ROOT in an include file in every directory that contains toplevel pages. The include file (call it root.inc.php) must be included by the page logic before any other include files. Now you can use the $ROOT variable to reference include files with their exact path!
Sample:We have toplevel pages in /admin/pages/. The $ROOT variable must therefore be set to$ROOT = '../..';. The page logic included by pages in that folder would reference their include files like require_once("$ROOT/lib/base.inc.php");.
In my suggested folder outline (see below) we don't even need that, since all toplevel pages reside in the webapp root directory anyway. So the webapp root directory is always the current directory.Folder outline
I suggest you make one file per class and follow a naming convention. Make sure that all your include files end with .php to avoid disclosure of your code to malicious users, which is a major security problem. I suggest the following folder structure:

/Webapp root directory. Contains the pages that are actually called by the browser.
/lib/Contains base.inc.php and config.inc.php
/lib/common/Contains libraries and tools reusable for other projects, like your database abstraction classes.
/lib/model/Contains the Value Object classes
/lib/dao/Contains the DAO classes and the DAO factory
/lib/logic/Contains the business logic classes
/parts/Contains partial HTML that is included by pages
/control/Contains the page logic.
For larger applications you may want additional sub-directories for the individual parts (e.g. /admin/, /pub/) of your application to make the root directory a little lighter. Each of them would have their own control sub-directory.
Provide a base.inc.php file that includes (require_once) in the right order:

  • frequently used stuff (database layer) from /lib/common
  • the config include file
  • all classes from /lib/model
  • all classes from /lib/dao

Of course you will have additional directories for your images, uploaded files, ... etc.
php.ini quirks
Some settings in the php.ini control how PHP interpretes your scripts. This can lead to unexpected behaviour when moving your application from development to the productive environment. The following measures reduce dependency of your code on php.ini settings.
short_open_tag
Always use the long PHP tags: php
echo "hello world"; ?>
Do not use the echo shortcut .
asp_tags
Do not use ASP like tags: <% echo "hello world"; %>
gpc_magic_quotes
I recommend that you include code in a global include file which is run before any $_GET or $_POST parameter or $_COOKIE is read. That code should check if the gpc_magic_quotes option is enabled and run all $_GET, $_POST and $_COOKIE values through the stripslashes function.
register_globals
Never rely on this option beeing set. Always access all GET, POST and COOKIE values through the 'superglobal' $_GET, $_POST and $_COOKIE variables. For convenience declare $PHP_SELF = $_SERVER['PHP_SELF']; in your global include file after the gpc_magic_quotes quirk.
File uploads:
The maximum size of an uploaded file is determined by the following parameters:

  • file_uploads must be 1 (default)
  • memory_limit must be slightly larger than the post_max_size and upload_max_filesize
  • post_max_size must be large enough
  • upload_max_filesize must be large enough
Have one single configuration file
You should define all configuration parameters of your application in a single (include) file. This way you can easily exchange this file to reflect settings for your local development site, a test site and the customer's production environment. Common configuration parameters are:

  • database connection parameters
  • email addresses
  • options
  • debug and logging output switches
  • application constants
Keep an eye on the namespace
As PHP does not have a namespace facility like Java packages, you must be very careful when choosing names for your classes and functions.

  • Avoid functions outside classes whenever possible and feasible. Classes provide some extra namespace for the methods and variables that live inside them.
  • If you declare global functions use a prefix. Some examples are dao_factory(),db_getConnection(), text_parseDate() etc.
Use a database abstraction layer
In PHP there are no database-independent functions for database access apart from ODBC (which nobody uses on Linux). You should not use the PHP database functions directly because this makes it expensive when the database product changes. Your customer may move from MySQL to Oracle one day or you will need an XML database maybe. You never know. Moreover an abstraction layer can ease development as the PHP database functions are not very userfriendly.
Use Value Objects (VO)
VOs are actually a J2EE pattern. It can easily be implemented in PHP. A value object corresponds directly to a C struct. It's a class that contains only member variables and no methods other than convenience methods (usually none). A VO corresponds to a business object. A VO typically corresponds directly to a database table. Naming the VO member variables equal to the database fields is a good idea. Do not forget the ID column.
class Person {
var $id, $first_name, $last_name, $email;
}
Use Data Access Objects (DAO)
DAO is actually a J2EE pattern. It can easily be implemented in PHP and helps greatly in separating database access from the rest of your code. The DAOs form a thin layer. The DAO layer can be 'stacked' which helps for instance if you want to add DB caching later when tuning your application. You should have one DAO class for every VO class. Naming conventions are a good practice.
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
}
}
A DAO typically implements the following methods:

  • save: inserts or updates a record
  • get: fetches a record
  • delete: removes a record
The DAO may define additional methods as required by your application's needs. The should only perform actions that require the database (maybe only for performance reasons) and can not be implemented in a different mannor. Examples: isUsed(), getTop($n), find($criteria).
The DAO should only implement basic select / insert / update operations on onetable. It must not contain the business logic. For example the PersonDAO should not contain code to send email to a person. For n-to-n relationships create a separate DAO (and even a VO if the relationships has additional properties) for the relation table.
Write a factory function that returns the proper DAO given the class name of a VO.Caching is a good idea here.
function dao_getDAO($vo_class) {
$conn = db_conn('default'); #get a connection from the pool
switch ($vo_class) {
case "person": return new PersonDAO($conn);
case "newsletter": return new NewsletterDAO($conn);
...
}
}
Generate code
99% of the code for your VOs and DAOs can be generated automatically from your database schema when you use some naming conventions for your tables and columns. Having a generator script ready saves you time when you are likely to change the database schema during development. I successfully used a perl script to generate my VOs and DAOs for a project. Unfortunately I am not allowed to post it here.
Business logic
Business logic directly reflects the use cases. The business logic deals with VOs, modifies them according to the business requirements and uses DAOs to access the persistence layer. The business logic classes should provide means to retrieve information about errors that occurred.
class NewsletterLogic {
function NewsletterLogic() {
}
function subscribePerson(&$person) {
...
}
function unsubscribePerson(&$person) {
...
}
function sendNewsletter(&$newsletter) {
...
}
}
Page logic (Controller)
When a page is called, the page controller is run before any output is made. The controller's job is to transform the HTTP request into business objects, then call the approriate logic and prepare the objects used to display the response.
The page logic performs the following steps:
1. The cmd request parameter is evaluated. 
2. Based on the action other request parameters are evaluated.
3. Value Objects (or a form object for more complex tasks) are created from the parameters.
4. The objects are validated and the result is stored in an error array.
5. The business logic is called with the Value Objects.
6. Return status (error codes) from the business logic is evaluated.
7. A redirect to another page is executed if necessary.
8. All data needed to display the page is collected and made available to the page as variables of the controller. Do not use global variables.
Note: it is a good idea to have a utility function that returns a parameter that is sent via GET or POST respectivly and provide a default value if the parameter is missing. The page logic is the only non-HTML include file in the actual page! The page logic file must include all other include files used by the logic (see base.inc.php below). Use the require_once PHP command to include non-HTML files.
class PageController {
var $person; #$person is used by the HTML page
var $errs;
function PageController() {
$action = Form::getParameter('cmd');
$this->person = new Person();
$this->errs = array();
if ($action == 'save') {
$this->parseForm();
if (!this->validate()) return;
NewsletterLogic::subscribe($this->person);
header('Location: confirmation.php');
exit;
}
}
function parseForm() {
$this->person->name = Form::getParameter('name');
$this->person->birthdate = Util::parseDate(Form::getParameter('birthdate');
...
}
function validate() {
if ($this->person->name == '') $this->errs['name'] = FORM_MISSING;
#FORM_MISSING is a constant
...
return (sizeof($this->errs) == 0);
}
}
Presentation Layer
The top level page will contain the actual HTML code. You may include HTML parts that you reuse across pages like the navigation etc. The page expects the page logic to prepare all business objects that it needs. It's a good idea to document the business objects needed at the top of the page.
The page accesses properties of those business objects and formats them into HTML.
<?php require_once('control/ctl_person.inc.php'); #the page controller
$c =& new PageController();
?>
<html>
<body>

php
echo htmlspecialchars($PHP_SELF) ?>" method="POST">

value="php echo htmlspecialchars($c->person->name); ?>">




Localization
Localization is a problem. You must choose among 
a) duplicating pages 
b) removing all hardcoded strings from your HTML.
As I work in a design company I usually take approach a). Approach b) is not feasible as it makes the HTML very hard to read and nearly impossible to edit in a visual web editor like Dreamweaver. Dynamic content is hard enough to edit with Dreamweaver. Removing also all strings, makes the page look quite empty...
So finish the project in one language first. The copy the HTML pages that need translation. Use a naming convention like index_fr.php to designate the French version of the index page. Always use the ISO two letter language codes. Do not invent your own language codes.
To keep track of the language the user selected you must choose among 
a) storing the language setting in a session variable or cookie
b) reading the preferred language (locale) from the HTTP headers the browser sends you
c) appending the language to the URL of every link in your application
While a) seems a lot more easier than c) it may be subject to session timeout. Option b) should only be implemented as an extension to a) or c). 
Strings in a database must be localized too!
Making your application location independent
PHP has problems in some situations when include files are nested and reside in different folders and it is unclear at which directory level the file will be included. One can solve this by using absolute path names or using $_SERVER['DOCUMENT_ROOT']as a starting point. However this makes your code location dependent - it will not run anymore if you move it down your directory structure one level. Of cource we do not like that.
I have found a convenient solution to this problem. The toplevel page (the one that is called by the browser) needs to know the relative path to the application root directory. Unfortunately there is no such function in PHP and the webapp context concept is completely absent in PHP. So we can not automatically determine the application root reliably in all situations (It is *really* impossible. Don't even try. It's not worth the effort.)
Let's define a global variable called $ROOT in an include file in every directory that contains toplevel pages. The include file (call it root.inc.php) must be included by the page logic before any other include files. Now you can use the $ROOT variable to reference include files with their exact path!
Sample:
We have toplevel pages in /admin/pages/. The $ROOT variable must therefore be set to$ROOT = '../..';. The page logic included by pages in that folder would reference their include files like require_once("$ROOT/lib/base.inc.php");.
In my suggested folder outline (see below) we don't even need that, since all toplevel pages reside in the webapp root directory anyway. So the webapp root directory is always the current directory.
Folder outline
I suggest you make one file per class and follow a naming convention. Make sure that all your include files end with .php to avoid disclosure of your code to malicious users, which is a major security problem. I suggest the following folder structure:
/Webapp root directory. Contains the pages that are actually called by the browser.
/lib/Contains base.inc.php and config.inc.php
/lib/common/Contains libraries and tools reusable for other projects, like your database abstraction classes.
/lib/model/Contains the Value Object classes
/lib/dao/Contains the DAO classes and the DAO factory
/lib/logic/Contains the business logic classes
/parts/Contains partial HTML that is included by pages
/control/Contains the page logic.
For larger applications you may want additional sub-directories for the individual parts (e.g. /admin/, /pub/) of your application to make the root directory a little lighter. Each of them would have their own control sub-directory.
Provide a base.inc.php file that includes (require_once) in the right order:

  • frequently used stuff (database layer) from /lib/common
  • the config include file
  • all classes from /lib/model
  • all classes from /lib/dao

Of course you will have additional directories for your images, uploaded files, ... etc.
Original Article: http://www.odi.ch/prog/design/php/guide.php



Original article:This guide will give you solutions to common PHP design problems. It also provides a sketch of an application layout that I developed during the implementation of some projects.