We have already discussed how we can serialize/unserialize objects in PHP. Here we would see how we can achieve the same thing using the interface Serializable.
To use this interface, we have to do away with the __wakeup() and __sleep() magic methods. So, the class implementing this interface does not need these magical functions to be defined. The Serializable interface has 2 abstract functions declared ( without their content body ) which means we must define their content body inside the class which implements this interface. These function have the following signatures ::
// To get the String representation of our Object
abstract public string serialize ( void )
// To Construct our object back from the string
abstract public void unserialize ( string $serialized_data )
When the object needs to be serialized, the serialize() method is called. Similarly, when a new object ( of a particluar Class, and that Class Name is known as it (class name) was stored in the String Representation ) needs to be restored, the constructor __construct() is not automatically called; instead the unserialize() function is called as a working constructor. If we need to run the __construct(), we may call it explicitly ( self::__construct(); ) inside the unserialize() function.
So, let's check an example :
<?php
// Our class is implementing the
// Interface Serializable
class MyClass implements Serializable
{
// Private Data
private $data1;
private $data2;
// Protected Data
protected $data3;
protected $data4;
// Public Data
public $data5;
public $data6;
// Constructor called
public function __construct()
{
echo "<br> Constructor called";
$this->data1 = "100";
$this->data2 = "200";
$this->data3 = "300";
$this->data4 = "400";
$this->data5 = "500";
$this->data6 = "600";
}
// Serialize function
public function serialize()
{
echo "<br>Serialize called";
// Return the serialized data
return serialize(
array( "data10" => $this->data1,
"data20" => $this->data2,
"data30" => $this->data3,
"data40" => $this->data4,
"data50" => $this->data5,
"data60" => $this->data6,
));
}
// Unserialize function
public function unserialize($data)
{
// We can call __construct explicitly
// self::__construct();
echo "<br>unSerialize called";
// Unserialize
$d = unserialize($data);
// Re-Construct the Object
$this->data1 = $d['data10'];
$this->data2 = $d['data20'];
$this->data3 = $d['data30'];
$this->data4 = $d['data40'];
$this->data5 = $d['data50'];
$this->data6 = $d['data60'];
}
// display() function
function display()
{
for($i=1;$i<=6;$i++)
{
echo "<br>" . $this->{"data$i"} ;
}
}
}
// Create a new Object
$obj = new MyClass;
// Serialize the Object
$ser = serialize($obj);
// Check the Serialize Data
var_dump($ser);
// Create new Object from Serialized data
$p = unserialize($ser);
// Check the Object
var_dump($p);
// Call Public method on Object
$p->display();
?>
The code above is quite self-explanatory through its comments. Let's check out the output of the above code.
Constructor called
Serialize called
string 'C:7:"MyClass":144:{a:6:{s:6:"data10";s:3:"100";s:6:"data20";s:3:"200";s:6:"data30";s:3:"300";s:6:"data40";s:3:"400";s:6:"data50";s:3:"500";s:6:"data60";s:3:"600";}}' (length=164)
unSerialize called
object(MyClass)[2]
private 'data1' => string '100' (length=3)
private 'data2' => string '200' (length=3)
protected 'data3' => string '300' (length=3)
protected 'data4' => string '400' (length=3)
public 'data5' => string '500' (length=3)
public 'data6' => string '600' (length=3)
100
200
300
400
500
600
Let's analyse the output.
1. When $obj object was created, the respective constructor was called printing "Constructor called" on screen.
2. Next, when serialize() function is called upon the object $obj, the member method serialize() is called generating the message "Serialize called".
3. The 3rd line in the output is the result of serialization of object properties.
4. Next, we issue the statement $p = unserialize($ser); to unserialize the serialized data and create our new object $p. Hence "unSerialize called" message is displayed on screen.
5. var_dump( $p ) generates the couple of lines next, showing all the properties (and their values) within the object $p.
6. Next, when we call a public display() method in object $p, it iterates through all the properties inside the object and prints their values.
We have seen serialization/unserialization of common types in article : Serialization / Unserialization in PHP - I
Here, we would see object serialization. When objects are serialized, PHP looks for a member function __sleep() within the object before it actually does the serialization. So, we can write various clean-up jobs ( like close file pointers, close DB connections, close any open sockets, close any streams, free any used memory, free unused variables, destroy other related objects etc ) inside this function __sleep(). We need to remember that all the private and public properties inside object are serialized.
Similarly, when the serialized format is restored to Object through the function unserialize(), PHP calls __wakeup() member function (if exists) after it is finished with re-constructing the object. We can write various jobs ( restore DB connections, open files which the object works with etc ) inside this function __wakeup().
Let's check a code where object is serialized.
<?php
// Define a Class
class MyClass
{
// private properties
private $data1;
private $data2;
// protected properties
protected $data3;
protected $data4;
// public properties
public $data5;
public $data6;
public function __construct()
{
// Initialize private properties
$this->data1 = "\r\n";
$this->data2 = NULL;
// Initialize protected properties
$this->data3 = NULL;
$this->data4 = "400";
// Initialize public properties
$this->data5 = "500";
$this->data6 = "600";
}
// __sleep()
public function __sleep()
{
echo "__Sleep called";
// within __sleep(), all the clean-up jobs
// can be included. Along with that, it also
// needs to return name of the properties
// to be serialized in an array format
return array("data1", "data2", "data3", "data4", "data5", "data6");
}
// __wakeup
public function __wakeup()
{
echo "__wakeup called";
}
public function display()
{
// Display all the properties
// We used a simple Loop
for($i=1;$i<=6;$i++)
{
echo "<br>" . $this->{"data$i"} ;
}
}
}
// Create Object
$obj = new MyClass;
// Serialize it
$ser = serialize($obj);
// See what happened after serialization
echo "$ser";
// Unserialize it to restore the
// data/properties in a new object
$p = unserialize($ser);
// $p is the new Object, hence
// call a member function
$p->display();
?>
The above code defines a class called "MyClass", which includes 3 private and 3 public properties and __wakeup(), __sleep() methods. It also includes a method called display() which shows the values in all the properties. The __wakeup() method should return all the properties which need to be serialized within an array. The __wakeup() function does not have such restrictions.
The above program can run without the __sleep() and __wakeup() methods. In that case the clean-up jobs etc can't be defined and we can't define/select properties which need to be serialized. In that case PHP serializes all the properties within that object.
When serializing the private properties within the object, the class name is prepended to the property name. Protected properties get an asterisk '*' prepended to their names. Such prepended values have Null bytes padded on both sides.
The code above produces 2 outputs, first is the serialized text of the object, second a list of property values in object $p which is created during the unserialization process of the stored representation we generated when $obj was serialized. Let's check the first output rendered on browser.
O:7:"MyClass":6:{s:14:"MyClassdata1";s:2:" ";s:14: "MyClassdata2";N;s:8:"*data3";N;s:8:"*data4";s:3:"400"; s:5:"data5";s:3:"500";s:5:"data6";s:3:"600";}
O:7:"MyClass":6: means Object having name "MyClass" (length:7) with 6 properties
s:14:"MyClassdata1";s:2:" "; means private property "MyClassdata1" (string, length:14) holds a string (length:2) value " ". Class name "MyClass" is prepended to Private properties. The value " " is text representation rendered on browser, actually it contains '\r\n'. As class name "\0MyClass\0" (padded by Null bytes on both side) was prepended to "data1" making it to "\0MyClass\0data1", the length of the new string is 14 ( 12 of "MyClassdata1" + 2 NULL bytes ) .
s:14:"MyClassdata2";N; means private property "MyClassdata2" (string length 14) holds a NULL value;
s:8:"*data3";N; means protected property "*data3" (string length 8 including NULL bytes on both side) holds NULL value
s:8:"*data4";s:3:"400"; means protected property "*data4" holds string (length:3) value "400"
s:5:"data5";s:3:"500"; means public property "data5" holds string (length:3) value "500"
s:5:"data6";s:3:"600"; means public property "data6" holds string (length:3) value "600"
Next part of the program is the unserialization part where we call the unserialize() function to restore an object from the stored representation $ser. And as a result, we create new object $p of type "MyClass" (The serialized data starts with "O:7:"MyClass"). Hence the call $p->display() calls the member function display() which iterates through all the properties inside the $p object and prints them on screen.
In such cases where object of undefined class to be created during unserialization, it creates object of "stdClass". Check the example below :
<?php
// an Array is being converted to Object
$o = (object) array("name" => "chandan");
// Serialize
$ser = serialize($o);
// Unserialize would instatiate
// Object of class PHP default 'stdClass'
$po = unserialize( $ser );
// Print the new object details
var_dump($po);
// Access member properties
echo "Hello {$po->name}";
?>
The above code is quite self-explanatory. It produces the following output ::
object(stdClass)[2]
public 'name' => string 'chandan' (length=7)
Hello chandan
$po is an object of PHP built-in class 'stdClass' with a public member "name" and this property holds a string value "chandan". So, when $po->name is referred, it prints the correct value as unserialize() correctly re-constructed object from stored representation.
Through Serialization, we can produce a storable representation of a value. PHP offers serialize() function which accepts value of any type except resource type. The serialize() function can accept arrays also. Array/Objects with circular references ( means some values reference to others within the array or objects ) can also be serialized. The serialize() function returns a byte-stream representation of the value and this may include NULL bytes and in case NULL bytes are serialized, it is better to store the serialized representation in BLOB field within Database. If we try to save it in CHAR, VARCHAR or TEXT type fields, those NULL bytes will be removed.
The reverse process i.e converting the serialized format to respective PHP value ( including array/object ), is called unserialization. PHP offers unserialize() function for unserialization.
Now check an example ::
<?php
// Define same Variables
$i = 100;
$j = true;
$k = "sample text";
$l = array("name" => "John", "age" => 23, "salary" => 103.25, "is_adult" => true );
// Serialize and store in other variables
$ii = serialize($i);
$jj = serialize($j);
$kk = serialize($k);
$ll = serialize($l);
// Print Serialized data
echo "<b>Serialization returns storable strings ::</b><br>";
echo "$ii $jj $kk $ll <br>";
// Unserialize
echo "<b>Unserialization restores :: </b><br>";
// Print what we got after unserialization
echo unserialize($ii) . "<br>";
echo unserialize($jj) . "<br>";
echo unserialize($kk) . "<br>";
// print_r used to print the array
print_r( unserialize($ll) );
?>
In the above program, we defined some variables of type integer, boolean, string and array and we called the serialize() function on each of them and stored the results in other variables. Next, we called unserialize() function to restore original data from the serialized representation.
Check the output ::
Serialization returns storable strings ::
i:100;
b:1;
s:11:"sample text";
a:4:{s:4:"name";s:4:"John";s:3:"age";i:23;s:6:"salary";d:103.25;s:8:"is_adult";b:1;}
Unserialization restores ::
100
1
sample text
Array ( [name] => John [age] => 23 [salary] => 103.25 [is_adult] => 1 )
See that serialize($i) generates a string like "i:100" which means integer value 100.
Similarly, "b:1" means Boolean value 1
s:11:"sample text" means String with length: 11 and value: 'sample text'
Let's understand the serialized text a:4:{s:4:"name";s:4:"John";s:3:" age";i:23; s:6:"salary"; d:103.25;s:8:"is_adult";b:1;} created for array $l. Array's key and value - both are sequentially stored.
a:4 means array of 4 values. All the values are wrapped in curly braces.
s:4:"name"; => Key, String of length:4, key value:"name"
s:4:"John"; => Value, String of length:4, value:"John"
s:3:"age"; => Key, String of length:3, key value:"age"
i:23; => Value, Integer 23
s:6:"salary"; => Key, String of length:6, key value:"salary"
d:103.25; => Value, Double value 103.25
s:8:"is_adult"; => Key, String of length:8, key value:"is_adult"
b:1; => Value, Boolean value 1
Let's check some more serialized arrays.
<?php
// Array 1
$arr1 = array("John",20,103.5,true);
// Array 2
// See that the 3rd item would have index '11'
$arr2 = array(10=> "John", 9=> 20, 103.5, 1=>true);
// Array 3
$arr2 = array('name'=> "John", 'age'=> 20, 'is_adult'=>true);
echo unserialize($arr1);
echo unserialize($arr2);
?>
The first output of the above program is ::
a:4:{i:0;s:4:"John";i:1;i:20;i:2;d:103.5;i:3;b:1;}
As said earlier, array's key and value are stored sequentially.
a:4: means array consists of 4 items.
i:0;s:4:"John" means integer key 0 holds string (length:4) value "John".
i:1;i:20 means integer key 1 holds integer value 20.
i:2;d:103.5 means integer key 2 holds double value 103.5.
i:3;b:1 means integer key 3 holds boolean value true/1.
The 2nd output of the above program is ::
a:4:{i:10;s:4:"John";i:9;i:20;i:11;d:103.5;i:1;b:1;}
i:10;s:4:"John"; means integer Key/index '10' holds a string "John"
i:9;i:20; means integer Key/index '9' holds an integer value 20
i:11;d:103.5; means integer Key/index '11' holds a double value 103.5
The 3rd output of the above program is ::
a:3:{s:4:"name";s:4:"John";s:3:"age";i:20;s:8:"is_adult";b:1;}
s:4:"name";s:4:"John"; means string (length:4) key "name" holds string value "John" (length:4)
s:3:"age";i:20; means string (length:3) key "age" holds integer value 20
s:8:"is_adult";b:1; means string (length:8) key "is_adult" holds boolean value true/1
The unserialize() function converts the stored representation to appropriate PHP value. This function returns FALSE if the passed string is not unserializable.
Check out the next part of this article : Serialization / Unserialzation in PHP - II
In our previous article Handling Zip Archives in PHP - I, we learned about how to create ZIP archive in PHP. In this discussion, we would learn how to extract files from an Archive.
Check out our first code below, this just takes a ZIP Archive and extracts all files and folders from it.
<?php
// First Create a new Object of the class ZipArchive
$archive = new ZipArchive;
// Now OPEN Archive
$result = $archive->open("our_zip.zip");
// Check if the Archive was created successfully
if($result == 1)
{
// Extract to current Folder
$archive->extractTo('.');
// Close the Archive
$archive->close();
}
else
{
echo "Archive creation Error [Error code : $result]";
}
?>
The code above is quite self-explanatory. The extractTo() method takes 2 parameters, first being the target folder where it should extract; second parameter is for specifying filenames only which we want to extract from the Archive. We would see an example soon. But before that, check the statement :
$archive->extractTo('.');
The character "." means current folder, ".." means parent folder. However this statement could have been written as shown below ::
<?php
// Extract to Current Working Folder
$archive->extractTo(getcwd());
// Extract to 'test' folder in the current working folder.
//'test' folder will be created if does not exist.
$archive->extractTo("test");
?>
So, we know how to create an archive and extract files from it. Next we would try to modify the archive.
Here, we are going to rename a file inside the archive. Check out the statement ..
<?php
// This renames test.jpg to tester.jpg within 'images' folder
$zip->renameName('images/test.jpg','images/tester.jpg');
?>
However, we can move a file inside the Archive while renaming. Check the statement below ::
<?php
// This renames test.jpg to tester.jpg within 'images' folder
$zip->renameName('images/test.jpg','tester.jpg');
?>
The above statement move the file 'images/test.jpg' and renames it to 'tester.jpg' and saves it at the root level within the Archive. Similarly we can rename folders and move them accordingly.
Next, let's check out how we can delete files/folders from within a ZIP archive.
<?php
// Delete a file
$archive->deleteName("images/test.jpg");
// Delete a folder
$archive->deleteName("images/");
?>
While deleting a folder, we need to make sure that the name is followed by a "/" character. Secondly, only empty folders can be removed.
To set a comment for the Archive, we need to use setArchiveComment() method.
<?php
// Set the Archive Comment
$archive->setArchiveComment('this is a test comment');
// Get the Archive Comment
$comment = $archive->getArchiveComment();
?>
Next we can read each file within the Archive. Check the statement below ::
<?php
// Read File Content
$file_content = $archive->getFromName('a.txt');
// Print the Content
echo $file_content;
?>
We can get property of individual items within the Archive ::
<?php
// Get file stats
$data = $archive->statName('a.txt');
// Print
print_r($data);
?>
And here is the ourput ::
Array
(
[name] => a.txt
[index] => 9
[crc] => -1815353990
[size] => 177045
[mtime] => 1406113612
[comp_size] => 378
[comp_method] => 8
)
It shows that the file size is 177045 bytes which was compressed to 378 bytes.
All files/folders in the Archive has an index number. So, we can delete/rename or get statics of file or do other stuffs with that index number also. Let's check that out. In the example below, we show the list of all the items inside the Archive.
<?php
// LOOP thru files
for ($j = 0; $j < $archive->numFiles; $j++)
{
// GET filename
$filename = $archive->getNameIndex($j);
echo "<br>$filename";
}
?>
Check the Output ::
tester.jpg
images/contact.jpg
images/spinning.gif
images/responsive.png
images/scan0001.jpg
images/test_image.jpg
e.txt
a.txt
b.txt
c.txt
d.txt
See, the code above has listed all the files inside any sub-folders. The ZipArchive object property numFiles (used as $archive->numFiles) holds the total number of files inside the Archive.
Some more functions which take file index number ::
$archive->statIndex(index_number) => Get file statistics
$archive->renameIndex(index_number) => Rename a file
$archive->deleteIndex(index_number) => Delete a file
$archive->getFromIndex(index_number) ==> Get contents of a file
If we want to revert all the changes we did to an item within the Archive, we can use these methods ::
$archive->unchangeName("a.txt");
$archive->unchangeIndex(index_number);
Handling ZIP archives, like reading an archive, extracting files from it or putting some files into a ZIP archive etc can be done very easily with PHP. PHP provides the ZipArchive class to create and read zip files. Please make sure that ZIP extension is installed with your version of PHP. So, let's just start with a small piece of code which creates a ZIP archive and put some files in it.
So, here is our first code snippet ... which creates a Zip archive and puts some files into it.
<?php
// First Create Object of the class ZipArchive
$archive = new ZipArchive;
// Now create an Archive
$result = $archive->open("our_zip.zip", ZipArchive::CREATE);
// Check if the Archive was created successfully
if($result == 1)
{
// Put a file into it
$archive->addFile('a.txt', 'apple.txt');
// Close the Archive
$archive->close();
}
else
{
echo "Archive creation Error [Error code : $result]";
}
?>
So, here is what we did ..
i. Created an object of the ZipArchive class
ii. Called open() method of that class to create our archive
iii. Added a file into the archive by calling addFile() method
iv. And finally we closed the Archive by calling close() method.
The open() method takes various FLAGs as second argument. These FLAGs are described below.
ZipArchive::CREATE - Create Archive if it does not exist
ZipArchive::OVERWRITE - Overwrite existing Archive
ZipArchive::EXCL - Return error if the Archive already exists.
ZipArchive::CHECKCONS - Perform additional consistency checks on Archives. Return Error if these checks fail.
If open() method worked perfectly, it returns 1 otherwise various Error codes/FLAGs described below. "ER_" prefix is for errors.
ZipArchive::ER_EXISTS - File exists - 10
ZipArchive::ER_INCONS - Inconsistent Archive - 21
ZipArchive::ER_INVAL - Invalid arguments - 18
ZipArchive::ER_MEMORY - Memory allocation failure - 14
ZipArchive::ER_NOENT - File does not exist - 9
ZipArchive::ER_NOZIP - Not a zip archive - 19
ZipArchive::ER_OPEN - File opening error - 11
ZipArchive::ER_READ - File reading error - 5
ZipArchive::ER_SEEK - Seek Error - 4
Let's complicate stuffs a bit more. In our second example, we add a file "b.txt" to the existing Archive "our_zip.zip" but inside a folder 'balloon'.
<?php
// First Create Object of the class ZipArchive
$archive = new ZipArchive;
// Now use existing Archive our_zip.zip
// See that we dont use any FLAG with open() method
$result = $archive->open("our_zip.zip");
// Check if the Archive Open was success
if($result == 1)
{
// Put a file inside 'balloon' folder
$archive->addFile('b.txt', 'balloon/balloon.txt');
// Close the Archive
$archive->close();
}
else
{
echo "Archive creation Error [Error code : $result]";
}
?>
Was not a big deal at all, this piece of code was self-describing... So, now our sample Archive holds the following files ...
apple.txt
balloon/balloon.txt
Let's Add some Content manually to that ZIP archive. We need to use addFromString() method here. See example below..
<?php
// ... create class etc etc
// Build the String
$str = "This is a Sample String, it will be saved in a file called c.txt";
// Write the string as a file c.txt
$archive->addFromString('custom/text/c.txt', 'file content goes here');
?>
The code above would create a file custom/text/c.txt inside that zip archive (in custom/text folder) and write the content of $str variable inside it.
Next, we would browse a folder for some Image files, then put them into a Zip file. This may sometimes come very handy in a situation where a user selects some photos (or PDFs or mixed) on our website and wants them to be downloaded in a zip archive. We would use the ZipArchive class' addGlob() method.
The scenario :: We have couple of images at the current folder, and we also have some more images in a folder called "test_images". Now, we want to take all the images ( including those in test_images folder ), create a new folder "images" inside the ZIP Archive and put into them. Check out the code below.
<?php
// First Create a new Object of the class ZipArchive
$archive = new ZipArchive;
// Now create an Archive
$result = $archive->open("our_zip.zip", ZipArchive::OVERWRITE);
// Check if the Archive was created successfully
if($result == 1)
{
// Set some basic Options for addGlob() method
$options = array('add_path' => 'images/', 'remove_all_path' => TRUE);
// Now use the Glob() pattern to add all images from current path
$archive->addGlob('*.{png,jpg,jpeg,gif,bmp}', GLOB_BRACE, $options);
// Add all images under a folder 'test_images'
$archive->addGlob('test_images/*.{png,jpg,jpeg,gif,bmp}', GLOB_BRACE, $options);
// Close the Archive
$archive->close();
}
else
{
echo "Archive creation Error [Error code : $result]";
}
?>
This code is also very simple. We have used an array $options to create necessary options for the addGlob() method.
i. 'add_path' => 'images/' means we wanted to create a new path 'images' inside the Archive
ii. 'remove_all_path'=>TRUE means even if we take files from 'folder1' or 'folder2' or 'test_images' ( as found by glob() ), these folder paths will be removed
addGlob() method is taking a pattern as first parameter. Check this statement once again ::
$archive->addGlob('test_images/*.{png,jpg,jpeg,gif,bmp}', GLOB_BRACE, $options);
GLOB_BRACE flag is used to match any of the extensions provided within the curly braces {}. For more on how Filesystem's glob() function works and glob patterns, check the article Using GLOB in PHP.
We have a ZipArchive method called addEmptyDir() to create empty folders inside the Archive. Check out the statement below :
$archive->addEmptyDir("test_folder");
So, this is actually pretty simple. Check this blog for more on handling ZIP archives in PHP.
In our next article, Handling Zip Archives in PHP - II, we discuss on extracting/renaming/deleting files from the Archive.