ほっぺログ

日々のことなんでも。

PHPの勉強まとめ

環境構築

  • PHPのversion管理をする..env的なのはあるが、brewで入れられないので少し面倒。。
  • phpbrewとphpenvというものが見つかった。
  • 適当に2つ調べてみるとphpenvのinstallでつまずいたときの対処法を書いた記事が多く出てきたため、phpbrew を採用した。

わたしの環境

  • macOS Mojave(10.14.5)
  • homebrew 2.2.1

    インストール方法

    phpbrewのインストール

    こちらからphpbrewのソースをダウンロード (わたしは2019/12/19時点での最新版 phpbrew 1.24.1 をダウンロードしました。)

$ curl -L -O https://github.com/phpbrew/phpbrew/raw/master/phpbrew

ダウンロードしたものを実行できるようにし、環境パスの通っているところに移動します。

$ chmod +x phpbrew
$ sudo mv phpbrew /usr/local/bin/phpbrew
$ phpbrew init

.zshrc(.bashrc)に下記を追加します。

source $HOME/.phpbrew/bashrc

ソースの読み込みます。

source ~/.zshrc

phpbrewの依存パッケージをダウンロード(phpbrew のgit にRequirementのページがあるのでそれを参照してください。)

$ brew install curl pcre mhash libtool icu4c gettext jpeg openssl libxml2 mcrypt gmp libevent

phpのインストール

とりあえず、実行してみてエラーが出たものを片っ端から+zlib=$(brew --prefix zlib)な感じで追加していけばそのうちinstallできました。

$ phpbrew install 5.6.40 +openssl=$(brew --prefix openssl) +zlib=$(brew --prefix zlib)

使うphpのversionを指定します。

$ phpbrew use 5.6.40

参考にしたページ * https://qiita.com/wisteria3221/items/4aa5ed1a9d106c3f7990 * https://qiita.com/da-sugi/items/60b3c7a6ba1634f3341c

PHPの起動する環境を作る

とりあえず、ApacheMysqlが必要らしい。

MAMPとは

macOSまたはWindows上で動作するWebデベロッパー向けのツール。ApacheMySQLPHPを一式管理してくれるものになります。アプリ内のhtdocsにphpのファイルを置いて起動ボタンを押せばlocalhostで出力が見れるので、学び始めに使うにはすごいちょうど良かったと思います。

ダウンロードした url →
https://pc-karuma.net/mamp-install-mac/

phpのバージョンが古くて選択できないとき

MAMPのところからphpのファイルを新しいのを適当に_とかつけて認識できないようにします。 すると再起動後選べるようになります。

PHPの仕組み

PHP<?phpから始まり、?>で終わります。 ファイルを解析してこのタグを探します。これに囲まれていないところはPHPパーサに無視されるので、あらゆるドキュメント内に埋め込むことができます。 * phpのみのファイルは>をつけない方が良いそう。空白や改行に弱く、終了タグ後にそれらがあるとおかしくなったりするそうです。

構文について

  • <?=<?php echoを意味します 。<?=の記法は5.4.0から使えるようになりました。
  • コメントアウト //#でできます。部分的コメントアウト/* */コメントアウトしたものはsourceとして画面には送られてきません。
  • 一つのタグ内で分は完結しなければならないわけではありません。むしろ一度書いた変数はそのファイル内ではいくらでも呼び出せます。
  • 文の終わりには;をつけましょう。
<?php
    echo "hello";
?>

このように。

変数

変数の宣言方法は

<?php
    $var = TRUE;
?>

と書きます。

この変数を呼び出すときは、同様に

$var

と書きます。 変数名は大文字小文字を区別します。使える文字は0-9,a-zA-Z,_となっています。ただし数字始まりはNGです。

参照の代入については&をつけることでできるようになる。注意点としては、名前のある変数のみが参照による代入ができるという点です。

    <?php
    $foo = 'Bob';              // 値'Bob'を$fooに代入する。
    $bar = &$foo;              // $fooを$barにより参照
    $hoge = $foo;              // $fooを$barの値を参照
    $bar = "My name is $bar";  // $barを変更...
    echo $bar;
    echo $foo;                 // $fooも変更される。
    echo $hoge;                // 変更されていない。
    ?>

スコープ

  • ローカルスコープ : メソッドやクラスを書いたときのその中身のこと。それらの中では、グローバルスコープのものは通常通り参照することはできません。
  • グローバルスコープ : ファイル直のところ。メソッドやクラス内ではないところ。このスコープはファイル内で他のファイルを読み込んだ場合、グローバルの領域は共通であり、相互にグルーバル変数を読み取ルことができます。

ローカルスコープでグローバル変数を使いたいときは以下のように書くことで使えるようになります。 使いたい場合は、global $b;と表記することで、使えるようになります。書き換えも同様にできます。スーパーグローバルである$GLOBALSを使用することでもこれは可能です。

    <?php
    $a = 1;
    function test1()
    {
        echo $a;
        $a = 2;
    }


    function test2()
    {
        $a = 2;
        echo $a;
    }

    function test3()
    {
        global $a;
        echo $a;
        $a = 2;
    }

    $b = 3;
    function test4()
    {
        echo $GLOBALS['b'];
        $GLOBALS['b'] = 4;
    }


    test1(); // 何も出ない
    echo $a; // 1
    test2(); // 2
    echo $a; // 1
    test3(); // 1
    echo $a; // 2
    test4(); // 3
    echo $b; // 4
    ?>

静的変数

staticをつけることで、ローカルスコープ内でクラスプロパティと同等に扱えるようになります。

<?php
function test()
{
    static $a = 0;
    echo $a;
    $a++;
}
test(); // 0
test(); // 1
test(); // 2
?>

グローバル変数と静的変数のリファレンス

ただし、リファレンス変数の修正子としてstatic、globalは実装されているため、これらに&をつけた参照を渡すと想定通りには動かないので、つけないようにしましょう。 試した感じ、globalの方は普通に代入できているようでした。 (version 5.6.40 )

<?php
    function test_global_ref() {
        global $obj;
        $obj = &new stdclass;
    }

    function test_global_noref() {
        global $obj;
        $obj = new stdclass;
    }

    test_global_ref();
    var_dump($obj);
    test_global_noref();
    var_dump($obj);


    function get_instance_ref()
    {
        static $obj;

        echo 'Static object: ';
        var_dump($obj);
        if (!isset($obj)) {
            // Assign a reference to the static variable
            $obj = &new stdclass;
        }
        $obj->property++;
        return $obj;
    }

    function get_instance_noref()
    {
        static $obj;

        echo 'Static object: ';
        var_dump($obj);
        if (!isset($obj)) {
            // Assign the object to the static variable
            $obj = new stdclass;
        }
        $obj->property++;
        return $obj;
    }

    $obj1 = get_instance_ref();
    $still_obj1 = get_instance_ref();
    $obj2 = get_instance_noref();
    $still_obj2 = get_instance_noref();

    ?>

可変変数

変数名を変数として持つことができます。以下のように、変数名を足したり、リストで持てたりできるので便利かもしれないですね。

<?php
class foo {
    var $bar = 'I am bar.';
    var $arr = array('I am A.', 'I am B.', 'I am C.');
    var $r   = 'I am r.';
}

$foo = new foo();
$bar = 'bar';
$baz = array('foo', 'bar', 'baz', 'quux');
echo $foo->$bar . "\n";
echo $foo->{$baz[1]} . "\n";

$start = 'b';
$end   = 'ar';
echo $foo->{$start . $end} . "\n";

$arr = 'arr';
echo $foo->{$arr[1]} . "\n";

?>

外部からの変数

htmlフォームからデータにアクセスする方法は二種類だけです。

<?php
echo $_POST['username'];
echo $_REQUEST['username'];
?>

bool

  • bool値の表記は、TUREまたはFALSEです。

また、下記のものは全てFALSEとみなされます。 * integer の 0 および -0 (ゼロ) * float の 0.0 および -0.0 (ゼロ) * 空の文字列、 および文字列の "0" * 要素の数がゼロである 配列 * 特別な値 NULL (値がセットされていない変数を含む) * 空のタグから作成された SimpleXML オブジェクト

cast は (bool) $varなどでもできますが、もっと簡単な方法があります。

<?php
    !!$var;
?>

と書くことです。!が一つであると、$varをboolにキャストした結果のNOTになってしまいますが、二つつけることで戻すということをしています。

整数

integerおよびfloatの上限はプラットフォーム依存です。 integerからfloatに変換するときにfloatの限界値を超えている場合は、undefinedとなる。 また、未知の端数を integer にキャストすると、予期しない結果となることがあります。

<?php
   echo (int) ( (0.1+0.7) * 10 ); // 7が出力されます!
?>

怖い。。。 実はこれPCの弱いところで7.999999...という数値になっていることが原因。お手軽な回避方法は以下。一回文字列に変換することです!?!?

<?php
   echo (int)(string) ((0.1 + 0.7) * 10); // 8
?>

文字列

文字列を指定する最も簡単な方法は、'で括ることです。この引用符をリテラルとして指定する場合は、バックスラッシュでエスケープする必要があります。また、バックスラッシュをリテラルとしてしているには、バックスラッシュを二つ書きます。 ただこの'で囲うときは改行の\nなどは認識できずそのまま出てきてしまいます。

このような特殊な文字列を認識させたい場合は"を使いましょう。ただしこちらも認識できる表記は限られています。

ヒアドキュメント

文字列を区切る方法としてヒアドキュメント構文<<<があります。この後に適当なIDを指定し、改行した後からの文字列を認識します。文字列の終端には、次行に先ほどのIDを書きます。 非常に重要なことですが、終端 ID がある行には、セミコロン (;) 以外の他の文字が含まれていてはならないことに注意しましょう。

<?php
$bar = <<<EOT
bar,
EOT;
?>
文字の組み込み

単純に変数をそのまま出力する場合は、そのまま変数を書き込めば組み込めますが、複雑になる場合は{}で囲うことで組み込めるようになります。 変数名の終わりを正しく認識してもらいたい場合は、${var}というように囲みましょう。

    <?php
    $great = 'fantastic';

    // うまく動作しません。出力: This is { fantastic}
    echo "This is { $great}";

    // うまく動作します。出力: This is fantastic
    echo "This is {$great}";
    ?>
文字列から数値への変換
<?php
$foo = "10.1times " + 1.0;
echo $foo; // 11.1
?>

配列

phpでは配列はarray()または[]と書くことで作れます。 また配列とはいうものの、pythonでいう辞書、javaではMapと呼ばれているものと同様にkeyを設定することもできる。

<?php
$array = array(
    "foo" => "bar",
    "bar" => "foo",
    100   => -100,
    -100  => 100,
);
var_dump($array);
/*
array(4) {
   ["foo"]=>
   string(3) "bar"
   ["bar"]=>
   string(3) "foo"
   [100]=>
   int(-100)
   [-100]=>
   int(100)
 }

*/
?>

primitiveなものであればkeyとして書くことはできるものの、実際にkeyとなりうるのは文字列またはint型なので、float型で書くと丸められてしまったり、bool値であるとTRUEは1にFALSEは0になってしまいます。 また、文字列であっても"1" はintの1と認識されてしまいます。

<?php
$array = array(
    1    => "a",
    "1"  => "b",
    1.5  => "c",
    true => "d",
);
var_dump($array);
/*
array(1) {
 [1]=> string(1) "d" 
}
*/
?>

もちろんkeyを書かなくても表記できます。その場合は、0始まりで順番に数値が振られていきます。また、部分的に数値のkeyを指定した場合にはその数字から1加算されて続くこととなります。

<?php
$array = array(
         "a",
         "b",
    6 => "c",
         "d",
);
var_dump($array); 
/*
  array(4) {
      [0]=>
      string(1) "a"
      [1]=>
      string(1) "b"
      [6]=>
      string(1) "c"
      [7]=>
      string(1) "d"
    }
*/
?>

要素の追加、削除は以下のように行います。

<?php
$arr = array("a" => 1, "b" => 2);

$arr[] = 56;    // このスクリプトのこの位置に記述した場合、
                // $arr[13] = 56; と同じです
var_dump($arr);

$arr["x"] = 42; // キー"x"の新しい要素を配列に追加します
                
unset($arr[5]); // 配列から要素を削除します

unset($arr);    // 配列全体を削除します
?>

配列の中身を削除してもindexは以前の続きから振られます。

<?php
    // 簡単な配列を生成します。
    $array = array(1, 2, 3, 4, 5);
    print_r($array);

    // 全てのアイテムを削除しますが、配列自体は削除しないでおきます。
    foreach ($array as $i => $value) {
        unset($array[$i]);
    }
    print_r($array);

    // アイテムを追加します(新しい添え字は0ではなく
    // 5となることに注意)
    $array[] = 6;
    print_r($array);

    // 添え字を振りなおします。
    $array = array_values($array);
    $array[] = 7;
    print_r($array);

    ?>

注意事項: 文字列に引用符がないと、未知の定数として見なされます。未定義の定数は同じ名前の文字列に自動的に変換されるのでコードは動作します。

<?php
    $foo = array();
    $foo[bar] = 'enemy';
    print_r($foo); // Array ( [bar] => enemy ) 
    print_r($foo[bar]); // enemy
    ?>

オブジェクト

クラスの定義方法はjsなどとあまり変わらないようです。呼び出しが一風変っていて、$bar->do_foo();と呼び出すようです。javapythonなど多くの言語では$bar.do_foo();.でつなげているから違和感を感じます。

<?php
class foo
{
    function do_foo()
    {
        echo "foo を実行します。";
    }
}

$bar = new foo;
$bar->do_foo();
?>

コールバック

<?php 

// コールバック関数の例
function my_callback_function() {
    echo 'hello world!';
}

// コールバックメソッドの例
class MyClass {
    static function myCallbackMethod() {
        echo 'Hello World!';
    }
}

// タイプ 1: 単純なコールバック
call_user_func('my_callback_function'); 

// タイプ 2: 静的クラスメソッドのコール
call_user_func(array('MyClass', 'myCallbackMethod')); 

// タイプ 3: オブジェクトメソッドのコール
$obj = new MyClass();
call_user_func(array($obj, 'myCallbackMethod'));

// タイプ 4: 静的クラスメソッドのコール (PHP 5.2.3 以降)
call_user_func('MyClass::myCallbackMethod');

// タイプ 5: 相対指定による静的クラスメソッドのコール (PHP 5.3.0 以降)
class A {
    public static function who() {
        echo "A\n";
    }
}

class B extends A {
    public static function who() {
        echo "B\n";
    }
}

call_user_func(array('B', 'parent::who')); // A

// タイプ 6: __invoke を実装したオブジェクトを callable として用いる (PHP 5.3 以降)
class C {
    public function __invoke($name) {
        echo 'Hello ', $name, "\n";
    }
}

$c = new C();
call_user_func($c, 'PHP!');
?>

構文

定数

define()関数を使用することにより、定数を定義することができます。定数は一度定義されると変更、または未定義とすることはできません。また、5.3.0のバージョンからはconstが使えるようになりました。 constによる定数で指定できるものはスカラ一式となっています。arrayもできますが、defineで書くのはバージョン7以上。ただし、リソースを定数として指定はできますが、不具合が発生する可能性があるのでお勧めできません。 定数の名前を動的に得ることもでき、その場合はconstant()を使用することもできます。 ちなみに定数は$なしで書けます。

制御構文

if, for, foreach, while, do..while, switchなども同様に使えます。制御構文の本文は{}の中に書きます。 またphpは、if, while, for, foreach, switchに関する別の構文を提供しています。{:に、 }をそれぞれ endif;, endwhile;, endfor;, endforeach;, endswitch;にすることができます。

if

<?php
// {}で囲う、elseif, else if どちらでも可
if ($var) {
// 内容
}elseif ($var){

} else if ($var){
}else{
    
}
// 一行だけの場合は:でも書ける
if ($var):
    echo "s";
    echo "aa";
endif;
?>

for

<?php
for ($i=0 ; $i<10 ; $i++){
    echo $i;
}

$arr=array(1,2,3,4);
foreach($arr as $value) {
    echo $value;
}

?>

for文の中で値を変更することは基本的にできないが、以下のように&をつけるとできるようになります。ただし、使用後にはunset($arr)を忘れずにしましょう。でないと、最後の要素が書き換えられてしまいます。

    <?php
    $arr = array(1, 2, 3, 4);
    foreach ($arr as &$value) {
        $value = $value * 2;
    }
    // この時点で、$arr は array(2, 4, 6, 8) となります

    // unset($value) しなければ、$value は今でも最後の要素 ($arr[3]) を指したままです

    foreach ($arr as $key => $value) {
        // $arr[3] が、$arr の各要素で上書きされて...
        echo "{$key} => {$value} ";
        print_r($arr);
    }
    // ...つまり、二番目から最後までの値が最後の値にコピーされていきます

    // 出力
    // 0 => 2 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 2 )
    // 1 => 4 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 4 )
    // 2 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 )
    // 3 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 )
    ?>

配列の要素を読むときに&つけると、$valueの参照先をその配列の要素に変更します。ゆえに、このループが終わった後の$valueの参照先は、配列の最後の要素の参照先となります。 ここで、&をつけないで$valueをforeachで呼び出してしまうと、この配列の最後の要素の参照先に今見ようとしている要素の中身を入れてしまうため、配列の最後の要素が書き換えられてしまうのです。

コード規約

PSR-1 : http://www.infiniteloop.co.jp/docs/psr/psr-1-basic-coding-standard.html

PSR-2 : http://www.infiniteloop.co.jp/docs/psr/psr-2-coding-style-guide.html

  • <?php または <?= で始めましょう。
  • phpだけのファイルには?>は書きません。
  • indent は空白4文字
  • 全てのphpファイルは最後に空行を入れましょう。
  • 改行コードはLF
  • 文字コードUTF-8 (BOMなし)
  • 宣言的なものと副作用のある(echo,require,includeなど)文はファイルを分けましょう。

  • Class, Namespace : StudlyCase(PascalCase)

  • method, property : camelCase
  • const : UPPER_SNAKE_CASE

  • アクセス修飾子は必ずつけましょう。

  • abstract, finalは先頭に、staticはアクセス修飾子後につけましょう。
  • useはnamespaceの後に書きましょう。
  • class, method の開き括弧は改行。その他制御文は改行しません。
  • クロージャの開き括弧は改行しません。
  • 開き括弧、閉じ括弧の後に空白は入れません。
  • else if はelseifと一文字にしましょう。
  • namespace, use, classの間は空行を入れましょう。
  • extendsとimplementsはクラス名と同じ行に定義されなければなりません。
  • switch, caseにて、breakしない場合はコメントをしましょう。
  • php予約語は小文字で使用しなければなりません。true/false/null