Phar

はじめに

phar 拡張モジュールは、PHP アプリケーション全体をひとつの "phar" (PHP Archive) ファイルにまとめてしまい、配布やインストールを容易にするためのものです。 それだけでなく、phar 拡張モジュールではファイルフォーマットを抽象化する機能も提供しており、 tar 形式や zip 形式のファイルを PharData クラスで扱うことができます。ちょうど PDO がさまざまなデータベースへの統一インターフェイスを用意しているのと同じようなものです。 PDO では異なるデータベース間の変換はできませんが、 phar では tar、zip、phar ファイルフォーマット間の変換がコード 1 行だけでできてしまいます。 例として Phar::convertToExecutable() を参照ください。

Phar って何? Phar アーカイブは、複数のファイルをひとつにまとめるための便利な仕組みです。 Phar アーカイブを使用すれば、PHP のアプリケーションをひとつのファイルとして配布できるようになります。 また、それをディスク上に展開しなくてもそのまま実行できるのです。 さらに、他のファイルと同様に PHP から phar アーカイブを実行することができます。 コマンドラインとウェブサーバー経由のどちらでも実行可能です。 phar は、いわば PHP アプリケーションにおける thumb drive のようなものです。

Phar はこの機能を実装するために ストリームラッパー を使用しています。通常は、PHP スクリプトから外部のファイルを使用するには include を用います。

例1 外部ファイルの使用

<?php
include '/path/to/external/file.php';
?>

PHP は、実際には /path/to/external/file.php をストリームラッパー file:///path/to/external/file.php とみなし、 内部的にはファイルストリームラッパーのストリーム関数でローカルファイルにアクセスしています。

Phar アーカイブ /path/to/myphar.phar の中に含まれるファイル file.php を使用する場合に構文は、 先ほどの file:// 構文と似ています。

例2 Phar アーカイブ内のファイルの使用

<?php
include 'phar:///path/to/myphar.phar/file.php';
?>

実際のところ、phar アーカイブは外部ディスクと同様に扱うことができます。 fopen() 系の関数や opendir()、そして mkdir() 系の関数を使用して、 phar アーカイブ内のファイルの読み書きや新しいファイルの作成が可能です。 これにより、完全な PHP アプリケーションを単一のファイルで配布して そのままの形式で直接実行できるようになるのです。

Phar アーカイブのもっとも一般的な使用法は、 完全なアプリケーションをひとつのファイルにまとめて配布することです。 たとえば、PHP に同梱されている PEAR インストーラは phar アーカイブとして配布されています。 このような形式の phar アーカイブを使用するには、 コマンドラインあるいはウェブサーバーからアーカイブを実行します。

Phar アーカイブの配布形式は tar アーカイブか zip アーカイブ、あるいは専用の phar 独自ファイルフォーマットのいずれかとなります。 それぞれの形式には利点と欠点があります。 tar 形式や zip 形式は、他のツールからでも内容を読んだり展開したりできるという利点がありますが、 それを実行する環境にも phar 拡張モジュールが必要となります。 phar 独自フォーマットは phar 拡張モジュール独自の形式です。 phar 拡張モジュールか PEAR パッケージ » PHP_Archive にしか作成できません。しかし、この形式で作成したアプリケーションは phar 拡張モジュールのない環境でも動作するという利点があります。

言い換えると、phar 拡張モジュールが無効になっている環境でも phar アーカイブを実行できるということです。 しかし、phar アーカイブ内の個別のファイルにアクセスするには、 (PHP_Archive で作成したものでない限り) phar 拡張モジュールが必要です。

phar 拡張モジュールには、tar ファイルを zip 形式や phar 形式に変換する機能もあります。

例3 phar 形式から tar 形式への phar アーカイブの変換

<?php
$phar
= new Phar('myphar.phar');
$pgz = $phar->convertToExecutable(Phar::TAR, Phar::GZ); // myphar.phar.tar.gz を作成します
?>

phar には、特定のファイルあるいはアーカイブ全体を gzip あるいは bzip2 で圧縮する機能があります。 また、アーカイブの整合性を MD5、SHA-1、SHA-256 あるいは SHA-512 シグネチャで自動的に検証します。

最後に、phar 拡張モジュールはセキュリティにも注意を払っており、 実行可能な phar アーカイブへの書き込みアクセスはデフォルトで無効にしています。 phar アーカイブを作成したり変更したりするには、php.ini の設定 phar.readonly をシステムレベルで無効にする必要があります。 通常の、実行可能属性がない tar アーカイブや zip アーカイブは、常に PharData クラスで作成、変更することができます。

アプリケーションを配布したい場合は Phar アーカイブのつくりかた を読みましょう。phar がサポートする各形式の違いをより詳しく知りたい場合は Phar、Tar そして Zip を読みましょう。

phar 形式のアプリケーションを使用したい場合は Phar アーカイブの使いかた が参考となるでしょう。

phar という単語は PHPArchive を組み合わせたもので、 Java 開発者ならおなじみの jar (Java Archive) を参考にしています。

Phar アーカイブの実装は PEAR の » PHP_Archive パッケージに基づいています。実装の詳細は同じですが、 Phar 拡張モジュールのほうがずっと強力です。 さらに、Phar 拡張モジュールで作成した PHP アプリケーションは ほとんどの場合は何も修正せずに動かすことができます。 PHP_Archive で作った phar アーカイブは、 動かす前に多少手を入れなければならないことがよくあります。

add a note

User Contributed Notes 6 notes

up
35
dava
12 years ago
Here is an apache2 htaccess example that prevents the downloading of phar-Archives by the user:

RewriteEngine on
RewriteRule ^(.*)\.phar$ - [F]

It triggers a "403 - Forbidden" message instead of delivering the archive.
up
17
bohwaz
14 years ago
If you get blank pages when trying to access a phar web-page, then you probably have Suhosin on your PHP (like in Debian and Ubuntu), and you need to ad this to your php.ini to allow execution of PHAR archives :

suhosin.executor.include.whitelist="phar"
up
6
v-mafick at microsoft dot com
12 years ago
Users should set the `sys_temp_dir` directive.

PHAR stores temporary files in either `sys_temp_dir` or the current working directory(CWD).

This is especially important if you're CWD is on a remote file system. Often web server clusters will share a common file system between each web server (using NFS, DFS, etc...). In such scenarios, if you don't set `sys_temp_dir` to a local directory, PHAR will be creating temporary files over the network, which will result in performance and functionality problems.
up
3
brunoczim
8 years ago
Here is a very simple program for creating phar files with CLI. Note that it requires phar.readonly in php.ini  set to false (Off).

<?php
$filename = "default";
$dir = "./";
$regex = "/^(?!.*build\\.php)(?:.*)$/";
$main = "main.php";
$shebang = "#!/usr/bin/env php";
$chmod = true;

for ($i = 0; $i < $argc; $i++) {
    switch ($argv[$i]) {
        case "-o":
            $i++;
            if ($i >= $argc) {
                echo "Missing output file name" . PHP_EOL;
                exit(1);
            }
            $filename = $argv[$i];
            break;
        case "-i":
            $i++;
            if ($i >= $argc) {
                echo "Missing input directory name" . PHP_EOL;
                exit(1);
            }
            $dir = $argv[$i];
            break;
        case "-p":
            $i++;
            if ($i >= $argc) {
                echo "Missing regular expression pattern" . PHP_EOL;
                exit(1);
            }
            $regex = $argv[$i];
            break;
        case "-m":
            $i++;
            if ($i >= $argc) {
                echo "Missing main file" . PHP_EOL;
                exit(1);
            }
            $main = $argv[$i];
            break;
        case "-b":
            $i++;
            if ($i >= $argc) {
                echo "Missing shebang of file" . PHP_EOL;
                exit(1);
            }
            $shebang = $argv[$i];
            break;
        case "--no-chmod":
            $chmod = false;
            break;
    }
}
if (file_exists($filename)) unlink($filename);
$phar = new Phar($filename);
$phar->buildFromDirectory($dir, $regex);
$phar->setStub(($shebang ? $shebang . PHP_EOL : "") . $phar->createDefaultStub($main));
if ($chmod) {
    chmod($filename, fileperms($phar) | 0700);
}
up
1
lemonkid from OpenToAll
7 years ago
when read via phar://, stream will unserialize object including phar file

Use this to create phar
<?php
// create new Phar
$phar = new Phar('lemon.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ? >');

// add object of any class as meta data
class AnyClass {}
$object = new AnyClass;
$object->data = 'Chanh';
$phar->setMetadata($object);
$phar->stopBuffering();
?>

and read it by:
<?php
class AnyClass {
    function __destruct() {
        echo $this->data;
    }
}
// output: Chanh
file_get_contents('phar://test.phar');
?>
up
1
t dot habenreich at web dot de
12 years ago
Here is a very simple class to build a phar file from a given source directory. You can use this for your own project and simple deployment.

But my main goal was to show how to use PHAR functions in a simple way.

<?php

class BuildPhar
{
  private $_sourceDirectory = null;
  private $_stubFile        = null;
  private $_outputDirectory = null;
  private $_pharFileName    = null;

  /**
   * @param $_sourceDirectory       // This is the directory where your project is stored.
   * @param $stubFile               // Name the entry point for your phar file. This file have to be within the source
   *                                   directory. 
   * @param null $_outputDirectory  // Directory where the phar file will be placed.
   * @param string $pharFileName    // Name of your final *.phar file.
   */
  public function __construct($_sourceDirectory, $stubFile, $_outputDirectory = null, $pharFileName = 'myPhar.phar') {

    if ((file_exists($_sourceDirectory) === false) || (is_dir($_sourceDirectory) === false)) {
      throw new Exception('No valid source directory given.');
    }
    $this->_sourceDirectory = $_sourceDirectory;

    if (file_exists($this->_sourceDirectory.'/'.$stubFile) === false) {
      throw new Exception('Your given stub file doesn\'t exists.');
    }

    $this->_stubFile = $stubFile;

    if(empty($pharFileName) === true) {
      throw new Exception('Your given output name for your phar-file is empty.');
    }
    $this->_pharFileName = $pharFileName;

    if ((empty($_outputDirectory) === true) || (file_exists($_outputDirectory) === false) || (is_dir($_outputDirectory) === false)) {

      if ($_outputDirectory !== null) {
        trigger_error ( 'Your output directory is invalid. We set the fallback to: "'.dirname(__FILE__).'".', E_USER_WARNING);
      }

      $this->_outputDirectory = dirname(__FILE__);
    } else {
      $this->_outputDirectory = $_outputDirectory;
    }

    $this->prepareBuildDirectory();
    $this->buildPhar();
  }

  private function prepareBuildDirectory() {
    if (preg_match('/.phar$/', $this->_pharFileName) == FALSE) {
      $this->_pharFileName .= '.phar';
    }

    if (file_exists($this->_pharFileName) === true) {
      unlink($this->_pharFileName);
    }
  }

  private function buildPhar() {
    $phar = new Phar($this->_outputDirectory.'/'.$this->_pharFileName);
    $phar->buildFromDirectory($this->_sourceDirectory);
    $phar->setDefaultStub($this->_stubFile);
  }
}
//END Class

//Example Usage:
$builder = new BuildPhar(
  dirname(__FILE__).'/_source',
  'my_default_stub.php',
  dirname(__FILE__).'/_output',
  'my-phar-file.phar'
);
To Top