Monthly Archives: September 2010

Live video streaming with PHP

Picture this. We want to stream live video. In fact don’t need PHP. We only need a flash player (or HTML5) and our live feed. The problem appear when we need to offer some kind of security. For example we want to show videos only to registered users based on our authentication system. Imagine we’re using sessions for validate users. That’s means we cannot put the media in a public folder and point our media player to those files. We can obfuscate the file name but it’ll remain public. In this small tutorial We’re going to see how to implement it with PHP. Let’s start.

First we are going to forget for a while video files. We are going to think in pictures.

If we don’t need to protect the file we can place the image in public folder:

<image href=’/path/to/image.png’ alt=’image’ width=’10’ height=’10’/>

But if we want to protect the picture we cannot place it on a public folder. We need to place it on a server folder and flush it with a code like this:

// here you can chech your sessions
header("Content-Type: image/jpeg");
readfile(‘/path/of/image’);

That’s easy. Isn’t it?.
Video files are similar than pictures. We cannot use image tag but the behaviour is the same. With HTML5 we can show them with HTML tags and without HTML5 we need to use a client viewer (generally a flash viewer).

We can use the same technique than the previous example with video files but the problem now is the size of the file. Live video feeds are never ends. If we use the previous code our server must read the live feed forever, and our browser will be waiting forever too. We can easily change this behaviour. Instead to read the entire video file and flush it to the browser we can read small pieces of the file and flush them without waiting until the end of the file.

<?php
// here you can chech your sessions

function flush_buffers(){
    ob_end_flush();
    ob_flush();
    flush();
    ob_start();
}

$filename = "/path/of/fideofeed.flv";
if (is_file($filename)) {
    header('Content-Type: video/x-flv');
    header("Content-Disposition: attachment; filename=video.flv");
    $fd = fopen($filename, "r");
    while(!feof($fd)) {
        echo fread($fd, 1024 * 5);
        flush_buffers();
        }
    fclose ($fd);
    exit();
}

If we want to stream static video files we also need to take into account the start of the video. When someone want to move across the video going forward and backward within the video client, our server script will retrieve a variable with the start time. That’s means we need to use this start time to open the file from this byte instead from the beggining.

Building an ORM with PHP.

First of all I must clear one thing. If you are looking for a full ORM you must take a look to Doctrine or maybe to the new version, still not stable, Doctrine2. Doctrine is probably the most advanced PHP project that we can find. Having said that let’s start with Nov/Orm. What’s the motivation for me to build this ORM? The answer is a bit ambiguous. I like SQL. It allows us to speak with the database in a very easy way. When I want to connect with the database with PHP I like PDO. It’s a great extension. But my problem is the following one: I need to write SQL and I need to know the names of the tables and the names of the fields and write them within my SQL string again and again. So the idea I figured out was to create a set of classes based on my tables, in a similar way than traditional ORMs to help me to autocomplete the fields and table names. PHP’s IDEs help us with the autocomplete of PHP but not with SQL. You must change the perspective or even the software. If my tables are mapped with PHP objects in a particular way I can get two objectives at the same time.

First basic example:

Let’s show a usage example. Imagine we have a table called tbl1 in the test schema. And we want to perform a simple select:

CREATE TABLE test.tbl1
(
    id integer NOT NULL,
    field1 character varying,
    field3 timestamp without time zone
)
WITH (
OIDS=FALSE
);
SELECT * FROM test.tbl1

We can easily use PDO with this SQL and fetch the results. As I told before we have created a class for our table (later we’ll see the code). So now we can execute:

$all = test\tbl1::factory($db)->select()->exec();

If we have properly set the IDE when we type test\ an autocompletion pop up will appear.

Mapped clases:

Each database table will be mapped into two PHP classes. One for the table indeed and another for the recordset associated to the table.  Imagine our table test.tbl1 has three fiels: id, field1 and field2. Our first class will be allocated into Orm\test namespace. “Orm” because all classes will be there and “test” because the schema name is test.

namespace Orm\test;
use \Nov;
class tbl1 extends Nov\Db\Orm\Table
{
    protected $_schema = 'test';
    protected $_object = "tbl1";

    const ID = "id";
    const FIELD1 = "field1";
    const FIELD3 = "field3";

    protected $_conf = array(
        self::ID => array(Nov\Types::STR),
        self::FIELD1 => array(Nov\Types::STR),
        self::FIELD3 => array(Nov\Types::STR),
    );
}

The second class is for the recordset. Why this class? The answer is because PHP’s lack of strong typed variables. It’s very typical working with databases to iterate over a recordset.

$all = test\tbl1::factory($db)->select()->exec();
foreach ($all as $reg){
 ....
}

But $reg is a class and is difficult to hint to the IDE what class is it. Here you can find different ways to solve the problem. But as well as we need to create the first mapped class we also can create a second helper one (Those classes will be created with a script not by hand)

$all = test\tbl1::factory($db)->select()->exec();
foreach ($all as $reg){
 $ar = new test\tbl1_Record($reg);
 echo $ar->id() . $ar->field1()
}

That’s the second mapped class:

class tbl1_Record
{
    /**
    * @param \Nov\Orm\Record $recordset
    * @return \Orm\test\tbl1_Record
    */
    static function factory($recordset)
    {
        return new tbl1_Record($recordset);
    }

    private $_recordset;
    function __construct($recordset)
    {
        $this->_isObject = $recordset instanceof Nov\Db\Orm\Record;
        $this->_recordset = $recordset;
    }

    function id()
    {
        return $this->_isObject ? $this->_recordset->{tbl1::ID} : $this->_recordset[tbl1::ID];
    }

    function field1()
    {
        return $this->_isObject ? $this->_recordset->{tbl1::FIELD1} : $this->_recordset[tbl1::FIELD1];
    }

    function field3()
    {
        return $this->_isObject ? $this->_recordset->{tbl1::FIELD3} : $this->_recordset[tbl1::FIELD3];
    }
}

And basically that’s all now with the library Nov\Orm we can create script like this:

// Setting up the library
require_once("Nov/Loader.php");
Nov\Loader::init();

// setting up namespaces
use Nov\Db\Orm\Instance;
use Orm\test;

echo "<pre>";
// New connection to dabase (lazy connection)
$db = Nov\Db::factory(NovConf::PG1);
$db->beginTransaction();

$values = array('id' => 'max(' . test\tbl1::ID . ')');
// Select to a table
$id = test\tbl1::factory($db)->select($values)->exec(Nov\Db::FETCH_ONE);
$id++;
$values = array(
    test\tbl1::ID => $id,
    test\tbl1::FIELD1 => "user_{$id}"
);
// Insert
test\tbl1::factory($db)->insert($values)->exec();

// Update
test\tbl1::factory($db)->update(array(test\tbl1::FIELD1 => 'xxx'))->where(array(test\tbl1::ID => $id))->exec();
$db->commit();

// another selec
$all = test\tbl1::factory($db)->select()->exec();

// Iterating over the recorset
foreach ($all as $reg) {
    $ar = new test\tbl1_Record($reg);
    echo $ar->id();
    echo "::";
    echo $ar->field1();
    echo "\n";
}

// Select to a view
$all = test\vw1::factory($db)->select()->exec();
foreach ($all as $reg) {
    $ar = new test\vw1_Record($reg);
    echo $ar->id();
    echo "::";
    echo $ar->field1();
    echo "\n";
}
echo "</pre>";

Triggers:

There’s also an extra feature. Triggers over ORM. If we create by hand a class like this we’ll have the traditional triggers post/pre Insert, update and delete:

namespace Orm\test\tbl1;
use Nov\Db\Orm\Instance\Interfaces;

use Orm\test;
use Nov;

class Triggers extends Nov\Db\Orm\Trigger
{
    function triggerPostUpdate($where, $values)
    {
        echo __CLASS__ . "::" . __FUNCTION__ . "\n";
        var_export($where);
        echo "\n";
        var_export($values);
        echo "\n";
        test\tbl1::factory($this->getDb())
            ->delete()
            ->where($where)
            ->exec();
    }

    function triggerPostInsert($values)
    {
        var_export($values);
        echo "\n";
        echo __CLASS__ . "::" . __FUNCTION__ . "\n";
    }

    function triggerPostDelete($where)
    {
        var_export($where);
        echo "\n";
        echo __CLASS__ . "::" . __FUNCTION__ . "\n";
    }
}

Scaffolding:

There is also a simple scaffolding to create the mapped classes.

require_once("Nov/Loader.php");
Nov\Loader::init();

use Nov\Db\Orm;
$scaffold = new Orm\Scaffold(Nov\Db::factory(NovConf::PG1), 'test');
$scaffold->buildAll(NovBASEPATH.'/Orm');

Summary:

Nov/Orm has those features:

  • Works over PDO extension. That’s means may be compatible with all databases compatible with PDO. The problem is to create the scaffold script to create the mapped classes in the selected database. The library now has only PostgreSql support. But with a little knowledge of the data dictionary of the database will be easy to implement Oracle and Mysql support.
  • Triggers over ORM
  • The autocompletion over IDE has been taken into account. The main purpose of the library is help the developer to write scripts.
  • Iterator helper
  • The main problem of this library is obvious. We need to create the Mapped classes if we want to use then. If we alter or add a new table we need to execute the scaffolding script.

The library with examples is available here. The examples are under document_root/tests/orm/

Using a stream wrapper to access CouchDb attachments with PHP

I’m still working in my filesystem with CouchDb. After creating a library to enable working with PHP and CouchDB (see the post here), and after using Monkey Patching to override standard PHP’s filesystem functions. I’ve created another solution now. Thanks to a comment in my last post (many thanks Benjamin) I’ve discovered that it’s possible to create a stream wrapper in PHP (I thought it was only available with a C extension).

It’s pretty straightforward to create the wrapper. Of course it’s only an approach. We can create more functionality to our stram wrapper but at least this example meets my needs.

namespace Nov\CouchDb\Fs;

class StreamWrapper {
    var $position;
    var $varname;
    /**
     * @var \Nov\CouchDb\Fs\Resource
     */
    var $fs;

    /**
     * @param string $path
     * @return \Nov\CouchDb\Fs
     */
    private static function _getFs($path, $_url)
    {
        return \Nov\CouchDb\Fs::factory(FSCDB, $_url['host']);
    }

    function stream_open($path, $mode, $options, &$opened_path)
    {
        $_url = parse_url($path);
        $_path = $_url['path'];
        $fs = self::_getFs($path, $_url);
        $_type = strtolower(substr($mode, 0, 1));
        switch ($_type) {
            case 'r':
                $this->fs = $fs->open($_path);
                break;
            case 'w':
                $this->fs = $fs->open($_path, true, true);
                break;
        }
        return true;
    }

    function stream_read($count=null)
    {
        if (is_null($count)) {
            return $this->fs->raw();
        } else {
            return $this->fs->raw(0, $count);
        }
    }

    function stream_write($data, $lenght=null)
    {
        if (is_null($lenght)) {
            $this->fs->write($data);
        } else {
            $this->fs->write(mb_substr($data, 0, $lenght));
        }
        return strlen($data);
    }

    public function unlink($path)
    {
        $_url = parse_url($path);
        $_path = $_url['path'];

        $fs = self::_getFs($path, $_url)->open($_path);
        $fs->delete();
    }

    public function url_stat($path , $flags)
    {
        $_url = parse_url($path);
        $_path = $_url['path'];
        $fs = self::_getFs($path, $_url)->open($_path);

        //tamaño en bytes
        $size = $fs->getLenght();
        $out[7] = $size;
        $out['size'] = $size;
        return $out;
    }

That’s very simple. It’s almost the same code than the Monkey Patching example.
And now if we want to use it:

use Nov\CouchDb;
use Nov\CouchDb\Fs;
require_once ("Nov/Loader.php");
Nov\Loader::init();
// I use FSCDB to set up the connection parameters to our couchDb database
define('FSCDB', \NovConf::CDB1);

stream_wrapper_register("novCouchDb", "Nov\CouchDb\Fs\StreamWrapper") or die("Failed to register protocol");
$file = "novCouchDb://fs/home/gonzalo/new.txt";

$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

unlink($file);
echo $a;

As we can see the URI is:

novCouchDb://fs/home/gonzalo/new.txt.

That’s mean [protocol]://[database]/[path]

The behaviour of this example is exactly the same than Monkey Patching one but I preffer this one. It looks like more “clean” than Monkey Patching approach.

The full source code is available here. And the example: document_root/tests/couchdb/test7.php

Using Monkey Patching to store files into CouchDb using the standard filesystem functions with PHP

Let’s go a little further in our CouchDb storage system with PHP. I will show you why. We have some files using PHP’s filesystem functions and we want to change them to the new interface with the Nov\CouchDb\Fs library. We’ve got the following code:

// Open a new file and write a string
$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

// read a file into a variable
$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

// Delete the file
unlink($file);

We can start to rewrite the code according to the new Nov\CouchDb\Fs interface. It’s not difficult but the syntax is very different so this piece of code needs almost a full rewrite. If we’ve got this kind of code split among files, we need to rewrite a lot. And rewrite always the same code is not funny so let’s do something different.

Since PHP5.3 a new design pattern is available for us: Monkey Patching. With this pattern we can override PHP’s core functions with a home-made functions in a different namespace (another example here). That’s means if I have fopen function in the above example, PHP uses the filesystem function “fopen” but if we set a namespace in our example, PHP will search first the function within the current namespace.

Using this idea I have created the following file:

<?php
namespace Nov\CouchDb\Fs\Monkeypatch;
use Nov\CouchDb\Fs;
use Nov\CouchDb;

function unlink($filename)
{
    $fs = CouchDb\Fs::factory(FSCDB);
    $fs->delete($filename);
}
/**
 * @param string $filename
 * @param string $type
 * @return \Nov\CouchDb\Fs\Resource
 */
function fopen($filename, $type='r')
{
    $out = null;
    $fs = CouchDb\Fs::factory(FSCDB);
    $_type = strtolower(substr($type, 0, 1));
    switch ($_type) {
        case 'r':
            $out = $fs->open($filename);
            break;
        case 'w':
            $out = $fs->open($filename, true, true);
            break;
    }
	return $out;
}
function filesize($filename)
{
    $fs = CouchDb\Fs::factory(FSCDB);
    $res = $fs->open($filename);
    return $res->getLenght();
}
function fwrite(\Nov\CouchDb\Fs\Resource &$f, $data, $lenght=null)
{
    if (is_null($lenght)) {
        $out = $f->write($data);
    } else {
        $out = $f->write(mb_substr($data, 0, $lenght));
    }
    $path = $f->getPath();
    $fs = CouchDb\Fs::factory(FSCDB);
    $f = $fs->open($path);
}
function fclose(\Nov\CouchDb\Fs\Resource $f)
{
}
function fread(\Nov\CouchDb\Fs\Resource $f, $lenght=null)
{
    if (is_null($lenght)) {
        return $f->raw();
    } else {
        return $f->raw(0, $lenght);
    }
}

As you can see this file redefines some function into Nov\CouchDb\Fs\Monkeypatch namespace. That’s means if we properly include this file into our project, when we’re under this namespace this namespace PHP will go to the new function set instead to the original one. It’s something like overriding main functions at runtime.

Maybe is more clear with an example. We’ll take back the first example and add the some extra lines at the beginning:

// new code
namespace Nov\CouchDb\Fs\Monkeypatch;
include ("Nov/CouchDb/Fs/Monkeypatch.php");
require_once ("Nov/Loader.php");
\Nov\Loader::init();
define('FSCDB', \NovConf::CDB1);
// end of new code

$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

// read a file into a variable
$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

// Delete the file
unlink($file);

Another possibility is to place all include code into a separate file:

index.php:

require_once ("Nov/Loader.php");
Nov\Loader::init();

define('FSCDB', \NovConf::CDB1);
include ("Nov/CouchDb/Fs/Monkeypatch.php");

include('main.php');

And main.php:

namespace Nov\CouchDb\Fs\Monkeypatch;

// Open a new file and write a string
$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

// read a file into a variable
$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

// Delete the file
unlink($file);

And that’s all. Thanks to PHP5.3’s namespaces we can change the default behaviour of our script into a new one only setting up a namespace.

The source code can be downloaded from here. The examples of this post are are: document_root/tests/couchdb/test3.php, document_root/tests/couchdb/test5.php and document_root/tests/couchdb/test6.php

Follow

Get every new post delivered to your Inbox.

Join 976 other followers