Linux(Ubuntu) + Apache2 + PHP
のWebアプリケーション開発環境構築
その2


PHP文法編

前の記事

前回Ubuntu上にApache2とPHPをセットアップした環境を整えました。次回からはいよいよPHPで簡単なアプリケーションを作りますが、その前にPHPの文法について軽く触れておきます。。

今回の目標は・・・

  1. PHPの書式とか仕様とか基本的なことを知る
  2. PHPの文法を知る

以上。

構築環境

Install Package/Tools

  • WSL2(Ubuntu)
    • Apache2
    • PHP8.3
      • php8.3-mbstring
      • php8.3-pdo
      • php8.3-pgsql
      • php8.3-gd
      • php8.3-mysqlnd
      • php8.3-xml
      • php8.3-curl
      • php8.3-fpm
      • etc…

PHPの書式とか仕様とか基本的なこと

そもそもPHPはHTMLを出力する言語として登場しました。そしてPHPは歴史が長いため、書き方も新旧合わせて様々です。特に最近の進化速度は速く、他の言語の効率的な機能を貪欲に取り込んでいる印象があります。Webで資料を探すとき、バージョンにも気を付けてください。非推奨を経てエラーとなった書式も多数存在します。
今回は現状の安定板の最新であるPHP8.3時点での文法を記載します。

まずは基本的な書き方です。
PHPは<?phpから?>までをPHPのコードとして処理し、その外はテキストの出力として扱われるという特徴があります。このテキストの出力は、WebであればHTMLとして、cliであれば単なる出力として機能します。
そのため以下のようにWebにおける.phpファイルはHTMLとPHPが混在することが可能です。

index.php

<?php 
    // PHPはここに書く
    function page_title() {
        // ページ名を返す関数を定義
        return 'ページ名';
    }
?>
<html>
    <body>
        <h1>
            <?php echo page_title(); //ページのタイトルを出力 ?>
        </h1>
    </body>
</html>
<?php
    // HTML出力後の処理 ちなみに ?> は省略可能

ここが他の言語にはない最大の特徴であり、Webに秀でていると言われる所以です。
.phpファイルでは、HTMLのLinterとphpのLinterだけでなく、HTML内部に記載出来るJavascriptやCSSのLinterが何れも動作する可能性があります。

.phpファイルを新規に作成する場合、HTML主体のテンプレートファイルなのか、PHPのメイン処理やモジュールのファイルなのか意識することがお勧めです。
よくPHPのプロジェクトはディレクトリを分けることでそれを明確にします。

これは実際のアプリの開発時にまた説明します。

PHPの文法

PHPの基本的な文法はCとの類似が多くあります。演算子、キャスト、ifやfor/while等のloop、関数/クラス宣言等は一般的な文法形式で、インデントは自由、セミコロンを末尾に付ける、コードブロックは{~}などなじみのあるものです。
ですが、一部独自性を持つものがあります。
以下他の言語では珍しい特徴的な構文を項目別にざっくり例として紹介します。

1.変数や型、関数の宣言
<?php

// 変数には$を付ける。変数に型宣は言出来ない(8.3時点)。
$hoge = 'ほげ';

// 定数はconstかdefine
const hoge2 = 'ほげ2';
define('hoge3','ほげ3');
 


// 関数の引数には型指定可能/省略可。ただしクラス型指定は最近出来るようになって日が浅い。プリミティブ型はもう少し前から指定可能。
function moge(string $hoge) {
    return $hoge;
}
 


// 参照渡しは&を先頭に付ける
function toHoge(&$moge)  {
    global $hoge;
    $moge = $hoge;
    return $moge;         // ほげ
}
 

// 無名関数宣言と変数への代入
$hogemoge_cut_func = function($value)  {
    return preg_replace('/(h|m)oge/','', $value);
};

// 変数から関数の実行
echo $hogemoge_cut_func('aaahogeaaa');         // aaaaaa

// 外部変数を関数内で使う場合はuse宣言が必要
2.文字列操作の演算子
<?php
$hoge = 'ほげ';
$moge = 'もげ';

// 文字列結合はドット。結合時は自動キャスト
$hoge_moge = $hoge.'_'.$moge;        //結果 ほげ_もげ
$hoge2 = $hoge.2;                    //結果 ほげ2
 


// ダブルクォートの文字列宣言は変数展開が発生するのでちょっと遅め。
// 展開対象の変数は{$変数名}と記載。
$hoge_moge = "{$hoge_moge}_もが";    //結果 ほげ_もげ_もが
 


// 複数行の文字列宣言。HTMLやメール文面を記載する時便利。
$moge = <<<EOL
<p>
    ほげ<br>
    もげ<br>
    {$hoge_moge}
</p>
EOL;

echo $moge;                    // PHPでの明確な出力は echoキーワードを使う
3.配列の操作記法

PHPの配列は値渡しです。

// 宣言
$hoge = array('h','o','g','e'); // 旧
$hoge = ['h','o','g','e'];     
 


// 追加
$hoge[] = 'moge';
echo $hoge[4];                  // 'moge'
 


// 展開
$moge = [...$hoge];             // 配列のコピー
$moge = ['m','o','g','e'];
$concat = [...$hoge, ...$moge]; // 'h','o','g','e','m','o','g','e'
 


// 引数の数を可変長として関数を宣言
function paramToStr(...$param) {
    foreach($param as $v) {
        echo $v,'!';
    }
}
 
echo paramToStr('h','o','g','e');    // h!o!g!e!
 


// 連想配列の宣言
$hoge = [
    'name' => 'hoge',
    'age' => 15
];

echo $hoge['age']; // 15
4.判定と条件分岐
<?php

// null判定はisset()
$hoge = '';
$dumy = null;
isset($hoge);             // true
isset($hoge,$dumy);       // false 一度に複数の変数を検査可能
 
// 空文字や0判定はempty()
empty($hoge)              // false;
$hoge = '';
empty($hoge)              // true;
 
// issetとemptyは関数ではなく、キーワード扱いなので超高速
 


// Null合体演算子
$hoge = $moge??'';        // hogeにmogeを代入するが、mogeがnullの場合空文字代入
 


// switchは比較に自動キャストが発生
switch('1') {
    case 1:
        echo 'ほげ';      // 結果
        break;
    case 2:
        echo 'もげ';
        break;
    default:
        echo 'どちらでもありません';
}
 


// matchで厳密な型比較(PHP8~)
$hoge = match ('1') {
    1 => 'もげ',
    '1' => 'ほげ'
}
echo $hoge;               // ほげ

echo ($hoge === 'ほげ')? '〇' : '✕';   // 三項演算子
5.配列走査/foreach
<?php

$out = '';
$hoge = ['h','o','g','e'];

// 配列走査の書式が少々独特
foreach($hoge as $char) {
    $out .= $char;
}
echo $out;                //hoge
 
 
 
$out = '';
// 走査時にインデックス番号やキーも参照する書き方
foreach($hoge as $index => $char) {
    $out .= $index.':'.$char.' ';
}
echo $out;                //1:h 2:o 3:g 4:e
 
 
 
 
$hoge = [
    'name' => 'hoge',
    'age' => 15
]; // 連想配列
 
$out = '';
// 連想配列でもforaechは同じように機能
foreach($hoge as $index=> $value) {
    $out .= $index.':'.$value.' ';
}
echo $out;                //name:hoge age:15
6.クラスインスタンスメンバ
<?php
// クラスのメンバの呼び出し方
class hoge() {
    private $i_var = 'i_hoge';
    public function __construct(){}  // コンストラクタ
    public function i_func() {
        return $this->s_var;         // $thisで自インスタンス参照 アロー演算子で呼び出し
    }
}
$hoge_ins = new hoge();
echo $hoge_ins->i_func();            // i_hoge



//次のようにクラス関数は変数化が可能(コールバック用)
$hoge_i_func = [$hoge_ins,'i_func']; // クラス関数の変数化
$hoge_i_func();                      // 実行 publicであることが条件
7.クラス静的クラスメンバ
<?php
// staticメンバの呼び出し方
class hoge() {
    private static $s_var = 's_hoge';
    public static function s_func() {
        return self::$s_var;          // selfで自クラス参照。 :: でメンバにアクセス。静的クラス変数参照には$が必要。
    }
}
echo hoge::s_func(); //s_hoge



//次のように静的クラス関数は文字列として表現可能(コールバック用)
$hoge_s_func = 'hoge::i_func';        // 静的クラス関数の文字列表現
$hoge_i_func();                       // 実行 publicであることが条件
8.名前空間
<?php
namespace feriarize\core;       // 名前空間宣言 バックスラッシュで区切れる
class main {
    public static function run() {
    }
}
\feriarize\core\main::run();    // 名前空間を指定しての実行



// 名前空間付クラスの文字列表現
class_exists('feriarize\core\main'); // true

※namespaceキーワードは一つのファイルの先頭に一つしか宣言出来ません

9.PHPファイルのモジュール読み込み

require句で外部ファイルをPHPとして読み込みます。
同様にrequire_onceという句もありますが、require_onceは既に読み込まれているファイルは二度読み込まずスキップします。モジュールとして読む場合require_onceを使います。
逆にrequire句は何度でも同じファイルを出力するので、html出力用のテンプレートとして作られたphpファイルを読むのに適します。

index.php

<?php 
require_once(__DIR__.'/func.php');    // func.php読み込み
?>
<html>
    <?php require(__DIR__.'/head.php');// head.php読み込み ?>
    <body>
        <h1><?php echo site_title(); ?></h1>
    <body>
</html>

func.php

<?php
// サイトのタイトルを返す関数定義
function site_title() {
    return 'hpge';
}

head.php

<?php 
require_once(__DIR__.'/func.php');    // func.php読み込み
?>
<head>
    <title><?php echo site_title();    // サイトタイトル出力 ?></title>
</head>

オブジェクト指向言語としての機能

  • クラスは継承出来ますし、オーバーライド等にも対応しています。
  • 厳密な型宣言はないため、オーバーロードはありません。
  • 大多数のオブジェクト指向言語と同じようにクラスの多重継承は出来ません。
  • 多重継承の代わりにトレイトが使用できます。
  • C#のような getter / setterの省略記法も可能となっています。

その他高度な機能としてAttributeの宣言と付与にもphp8から対応しています。

PHPDoc / エディターの補助

PHPは型宣言機能が弱いため、VisualStudioファミリーのIntelliSenseのようなエディター補助機能が十全に機能しません。これは型の宣言が不要だったり出来ない変数や関数の引数、戻り値等々に対しエディターが型を憶測出来ないからです。
これを解決するのがPHPDocです。

<?php
class hoge {
    /**
     * 名前
     * @var string
     */
    public $name;
    /**
     * 年齢
     * @var int
     */
    public $age;
    /**
     * シングルトンインスタンス
     * @var \hoge
     */
    private static $instance;
    /**
     * インスタンス取得
     * @return \hoge
     */
    public static function getInstance() {
        return self::$instance??new hoge();
    }
    /**
     * コンストラクタ
     */
    private function __construct() {
        $this->name = 'hoge';
        $this->age = 16;
    }
    /**
     * 配列変換
     * @return array{age: int, name: string}
     */
    function toArray() {
        return [
            'name' => $this->name,
            'age' => $this->age,
        ];
    }
}

上記のようなPHPDocsを記載することで主要なエディターでは以下のようにコードヒントを的確に表示してくれます。
例では連想配列およびその中のキーのリストと値の型を推論してくれています。

連想配列のキーリスト
こちらはVSCode+PHP Intelephenseの場合

以上が大まかなPHPの文法になります。
次回は簡単なアプリを開発していきます。