Recursively zip files with PHP

This function is useful for zipping files/directories when you only have FTP access to a site, and for when you are using a web host ‘File Manager’ which bugs out on permissions or otherwise.

function Zip($source, $destination)
{
    if (!extension_loaded('zip') || !file_exists($source)) {
        return false;
    }
    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
        return false;
    }
    $source = str_replace('\', '/', realpath($source));
    if (is_dir($source) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
        foreach ($files as $file)
        {
            $file = str_replace('\', '/', $file);
            // Ignore "." and ".." folders
            if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
                continue;
            $file = realpath($file);
            if (is_dir($file) === true)
            {
                $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
            }
            else if (is_file($file) === true)
            {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }
    return $zip->close();
}

Zip('/folder/to/compress/', './compressed.zip');

Recursively copy files with PHP

This function is useful for copying files when you only have FTP access to a site, and for when you are using a web host 'File Manager' which bugs out on permissions or otherwise.

function recurse_copy($src, $dst) { 
    $dir = opendir($src); 
    @mkdir($dst); 
    while(false !== ( $file = readdir($dir)) ) { 
        if (( $file != '.' ) && ( $file != '..' )) { 
            if ( is_dir($src . '/' . $file) ) { 
                recurse_copy($src . '/' . $file,$dst . '/' . $file); 
            } 
            else { 
                copy($src . '/' . $file,$dst . '/' . $file); 
            } 
        } 
    } 
    closedir($dir); 
} 

recurse_copy('./big-directory', './big-directory-new');

Download file with cURL & PHP

CURLOPT_RETURNTRANSFER is a simple way of copying a file from a remote server onto your own. However, if you're downloading a large file you may hit memory limits because the entire contents of the download have to be read to memory before being saved.

Note: Even if your memory limit is set extremely high, you would be putting unnecessary strain on your server by reading in a large file straight to memory.

Instead you can write the download straight to a file stream using CURLOPT_FILE.

$url  = 'http://www.example.com/a-large-file.zip';
$path = '/path/to/a-large-file.zip';
$fp = fopen($path, 'w');
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_FILE, $fp);
$data = curl_exec($ch);
curl_close($ch);
fclose($fp);

Batch Delete from Table in MSSQL

This script allows you to perform an operation in batches. This should allow you to keep the log file for the database down whilst performing large operations.

SET ROWCOUNT allows you to set how many records will be effected per query.
SET @intFlag = 1 sets the loop to start at 1
WHILE (@intFlag <=100) loop through this code X times (for this example 100)
SET @intFlag = @intFlag + 1 add 1 to the loop count

SET ROWCOUNT 500000;

DECLARE @intFlag INT
SET @intFlag = 1
WHILE (@intFlag <=100)
BEGIN

DELETE FROM tblTemp WHERE tmpDate < '2012-10-01'

SET @intFlag = @intFlag + 1
END
GO

Here the ROWCOUNT is set to 500,000. On this server attempting to delete 1,000,000 records by running 2 batches of 500,000 delete records was 35 seconds faster than running a single 1,000,000 delete records statement. But running 10 batches of 100,000 delete records was also slower than 2 batches at 500,000 records. This may vary server to server.

JavaScript str_pad function

JavaScript equivalent of str_pad function for PHP

function strPad(input, length, string) {
    string = string || '0'; input = input + '';
    return input.length >= length ? input : new Array(length - input.length + 1).join(string) + input;
}

Usage: strPad ('1', 3, 0);
Output: 001

Disable text selection with CSS

Cross browser CSS to correctly disable user selection of elements.

-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;

Although you should not need to interfere with normal selection behavior or conflict with a user's expectations when it comes to text selection, there are a few scenarios in which this property. For example:

  • Navigation buttons in which you do not want the user to accidentally select the text of the button instead of clicking it. (Some browsers e.g. Safari actually let you select the text of normal buttons).
  • This may be necessary for embedded devices. i.e. a device where a browser is used for rendering the UI.
  • There are also legal issues where 3rd party content is being legally republished but a clause in the license requires web publishers to prevent text from being easily copied and pasted.

A JavaScript option should be avoided due to the inconsistencies of browsers.

jQuery Get + Post returned headers

This snippet allows you to use the response headers from a $.get and $.post function.

Particularly useful if you return a PHP response with additional information for jQuery to handle. i.e: Custom Header: Could not connect to database.

$(function() {

	var result = $.get('index.php', function(data) {

		console.log ( result.getAllResponseHeaders() );
		console.log ( result.getResponseHeader('Date') );

	});

});

Result:

Date: Tue, 18 Sep 2012 11:24:40 GMT
Content-Encoding: gzip
X-Powered-By: PHP/5.3.5
Connection: Keep-Alive
Content-Length: 2713
Server: Apache/2.2.17 (Win32) PHP/5.3.5
Vary: Accept-Encoding
Content-Type: text/html
Keep-Alive: timeout=5, max=98
Tue, 18 Sep 2012 11:24:40 GMT

MySQL Datetime vs Timestamp

Taken from http://stackoverflow.com/a/409305/1089331

Timestamps in MySQL generally used to track changes to records, and are updated every time the record is changed. If you want to store a specific value you should use a datetime field.
If you meant that you want to decide between using a UNIX timestamp or a native MySQL datetime field, go with the native format. You can do calculations within MySQL that way ("SELECT DATE_ADD(my_datetime, INTERVAL 1 DAY)") and it is simple to change the format of the value to a UNIX timestamp ("SELECT UNIX_TIMESTAMP(my_datetime)") when you query the record if you want to operate on it with PHP.
- blivet, stackoverflow.com
An important difference is that DATETIME represents a date (as found in a calendar) and a time (as can be observed on a wall clock), while TIMESTAMP represents a well defined point in time. This could be very important if your application handles time zones. How long ago was '2010-09-01 16:31:00'? It depends on what timezone you're in. For me it was just a few seconds ago, for you it may represent a time in the future. If I say 1283351460 seconds since '1970-01-01 00:00:00 UTC', you know exactly what point in time I talk about. (See Nir's excellent answer below). [Downside: valid range].
- MattBianco, stackoverflow.com
In MYSQL 5 and above, TIMESTAMP values are converted from the current time zone to UTC for storage, and converted back from UTC to the current time zone for retrieval. (This occurs only for the TIMESTAMP data type, and not for other types such as DATETIME.)
By default, the current time zone for each connection is the server's time. The time zone can be set on a per-connection basis, as described here: MySQL Server Time Zone Support
- Nir, stackoverflow.com
I always use DATETIME fields for anything other than row metadata (date created or modified).
As mentioned in the MySQL documentation:

The DATETIME type is used when you need values that contain both date and time information. MySQL retrieves and displays DATETIME values in 'YYYY-MM-DD HH:MM:SS' format. The supported range is '1000-01-01 00:00:00' to '9999-12-31 23:59:59'.
...
The TIMESTAMP data type has a range of '1970-01-01 00:00:01' UTC to '2038-01-09 03:14:07' UTC. It has varying properties, depending on the MySQL version and the SQL mode the server is running in.


You're quite likely to hit the lower limit on TIMESTAMPs in general use -- e.g. storing birthdate.
- scronide, stackoverflow.com

Perform fast MSSQL Delete operations

The solution focuses on utilizing a view in order to simplify the execution plan produced for a batched delete operation. This is achieved by referencing the given table once, rather than twice which in turn reduces the amount of I/O required.

Before:

delete from t1 where a in (select top (10000) a from t1 order by a);

After:

create view v1 as (select top (10000) * from t1 order by a)

delete from v1

You can also use this method for update operations aswell:

update t1

  set b = 'y'

  from (select top (100) b

        from t1

        order by a ) t1

The posts that provided this information referenced a link (http://blogs.msdn.com/sqlcat/archive/2009/05/21/fa...) provided by Kevin Stephenson of MySpace and Lubor Kollar, a member of the SQL Server Customer Advisory Team. This link is now unavailable but the Internet Archive has a copy of this page at http://web.archive.org/web/20100212155407/http://blogs.msdn.com/sqlcat/archive/2009/05/21/fast-ordered-delete.aspx.