PHPの関数とメソッドは別物

:::note
関数はメソッドの気取った言い換えではありません
:::

今年に入ってから6回くらい別の人に個別に説明したので記事に書きます。

## 一目でわかる関数とメソッド

```php
$array = [1, 2, 3];
// これは関数呼び出し
$length = count($array);

$object = new ArrayObject($array);
// これはメソッド呼び出し
$lenth = $object->count();
```

```php
$datetime1 = date_create_immutable('2022-09-03'); // ← 関数呼び出しです
$datetime2 = DateTimeImmutable::createFromFormat('Y-m-d', '2022-09-03'); // ← (静的)メソッド呼び出しです
$datetime3 = $datetime2->createFromFormat('Y-m-d', '2022-09-03'); // ← メソッド呼び出しです
$datetime4 = new DateTimeImmutable('now'); // ← new式(インスタンス生成式)です
```

厳密に言うなら呼び出し構文だけで関数かメソッドかを判別は**できない**のですが、9割の呼び出しはこのパターンです。

もちろんこれはまったく網羅できていないのですが、この記事では便宜上このような名前で説明しておきます。

:::note warn
こんな曖昧な説明の分類に納得いかない人は[言語仕様](https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#function-call-operator)を読んでみてください。
:::

### PHPにおける関数とは何か

**大雑把に言うと、`hoge($arg)`や`Foo\Bar\buz($arg)`のように呼び出せるものです。**

以下のようなファイルで宣言した関数はグローバル名前空間に属します。

```hoge.php
<?php

function hoge(): void
{
    var_dump("hoge");
}
```

以下のようなファイルで宣言した関数は`Foo\Bar`名前空間に属します。

```Bar.php
<?php
namespace Foo\Bar;

function buz(): void
{
    var_dump("buz");
}

function buzbuz(): void
{
    var_dump("buzbuz");
}
```

これらの関数は以下のように呼び出せます。

```User.php
<?php
namespace Foo\Bar;
use function Foo\Bar\buzbuz;

class User
{
    public function test(): void
    {
        // 現在の名前空間の定義状況によって呼び出しが不定になるパターン
        hoge(); // Foo\Bar\hoge() 関数が定義されていればそれが、なければグローバルの hoge() が呼び出される
        buz();  // Foo\Bar\buz() 関数が定義されているので、それが呼び出される

        // 現在の名前空間の定義状況に関わらず決まった関数を呼び出しできるパターン
        buzbuz(); // ファイル冒頭でインポートされているので、確定で Foo\Bar\buzbuz() が呼び出される
        \hoge(); // グローバル空間に定義された関数だと明示しているので確定で hoge() が呼び出される
        \Foo\Bar\buz();  // ネームスペース含めてフル指定しているので確定で Foo\Bar\buz() が呼び出される
    }
}
```

### PHPにおけるメソッドとは何か

クラスに属する手続き(関数のようなもの)です。

**大雑把に言うと、`$obj->foo($arg)`や`Foo\Bar\User::create($arg)`のように呼び出せるものです。**


メソッドには以下の2パターンがあります。

 * インスタンスオブジェクトから`->`で呼び出せるメソッド
 * インスタンス化なしに`::`(スコープ定義演算子)からも呼び出せるメソッド **(静的メソッド)**

:::note
ややこしいのでこの記事では、静的ではないメソッドを**インスタンスメソッド**と呼ぶことにしましょう。
ただし正確な定義ではないので気をつけてください。
:::

静的メソッドは[`static`](https://www.php.net/manual/language.oop5.static.php)というキーワードで定義できます。

:::note warn
静的メソッドは静的にしか呼び出せないメソッド(Rubyでいうクラスメソッド)**ではありません**。
:::

メソッドは以下のように呼び出します。

```php
$datetime = DateTimeImmutable::createFromFormat('Y-m-d', '2112-09-03');
//                             ~~~~~~~~~~~~~~~~
echo $datetime->format('Y-m-d H:i:s');
//              ~~~~~~
```

PHPにおいてはプロパティとメソッドは別の空間に属しています。

```php
class X {
    public string $v = 'property';
    public function v(): string {
        return 'method';
    }
}

$x = new X();
echo "This is a ", $x->v, ".", PHP_EOL;
echo "This is a ", $x->v(), ".", PHP_EOL;
```

静的メソッドは`::`で呼び出し、インスタンスメソッドは`->`で呼び出す… という理解は96%くらいのユースケースにおいては正しいですが、正確な理解ではありません。静的メソッドを`->`で呼び出すこともできますし、インスタンスメソッドを`::`で呼ぶこともできる文脈もあります。あくまで`::`は[スコープ定義演算子](https://www.php.net/manual/language.oop5.paamayim-nekudotayim.php)です。

https://www.php.net/manual/ja/language.oop5.paamayim-nekudotayim.php

:::note warn
ここまでの記述を覆すようですが、PHP内部でも[`debug_backtrace()`](https://www.php.net/debug_backtrace)と[`Throwable::getTrace()`](https://www.php.net/manual/throwable.gettrace.php)では呼び出し方法に**関係なく**、呼び出されたメソッドが静的メソッドかという識別のための文字列として`'::'`と`'->'`が使い分けられています。動作確認: https://3v4l.org/QjBWV
:::

さきほどPHPの静的メソッドはクラスメソッドではないと述べましたが、PHPでは同じクラスで `public function foo()` と `public static function foo()`を同時定義することは**できません**。これはRubyのクラスメソッドと異なる重大なポイントです。

### PHPにおけるクラスとコンストラクタとは何か

**大雑把に言うと、`new User()`のようにインスタンスを生成したあとに暗黙的に呼び出されるものです。**


```php
class User extends AbstractUser
{
    public function __construct()
    {
        $this-> // ← この時点で既に $this にアクセス可能になっている
        parent::__construct();; // ← 親のコンストラクタを直接起動する必要がある
    }
}
```

PHPでは親クラスのコンストラクタは暗黙的に呼び出されることはないので、クラスを継承した場合は親クラスを正常に機能させるには明示的に呼び出さなければなりません。

PHPでは関数とクラスは別の空間に属しています。つまり、`class User`とは別に`function User`を定義できます。

```php
<?php
namespace Foo\Bar;

function User()
{
    echo 'Called ', __FUNCTION__, PHP_EOL;
    return new User();
}

class User
{
    public function __construct()
    {
        echo 'Called ', __METHOD__, PHP_EOL;
    }

    public static function new()
    {
        echo 'Called ', __METHOD__, PHP_EOL;
        return new User();
    }
}

echo '[user1]', PHP_EOL;
$user1 = new User();
echo '[user2]', PHP_EOL;
$user2 = User::new();
echo '[user3]', PHP_EOL;
$user3 = User();
```

これはPythonのオブジェクト指向機能を知っていると混乱するかもしれませんね。

### 予約語 vs メソッド

多くのプログラミング言語と同じように、PHPの構文に使用する[キーワード](https://www.php.net/manual/reserved.keywords.php)(予約語, reserved words)は関数名や定数名としては利用できませんが、PHP 7.0からはメソッドとクラス定数として[すべてのキーワードが許可されました](https://wiki.php.net/rfc/context_sensitive_lexer)。

https://www.php.net/manual/ja/reserved.keywords.php

上記のページに掲載されているキーワードは関数名としては利用できませんが、メソッド名としては合法です。`User::new`のようなメソッドが存在できるのはこのためです。

:::note warn
ただし[その他の予約語の一覧](https://www.php.net/manual/reserved.other-reserved-words.php)に含まれるキーワード(主に型名)は関数として定義可能です。
クラス名にはできません。
:::

### callable (呼び出し可能型)

PHPでは変数も関数のように呼び出し可能です。

```php
$f1 = 'DateTimeImmutable::createFromFormat';
$result = $f1('Y-m-d', '2022-09-03');
var_dump($result);

$f2 = [DateTimeImmutable::class, 'createFromFormat'];
$result = $f2('Y-m-d', '2022-09-03');
var_dump($result);
```

関数のように呼び出せる値を[`callable`](https://www.php.net/callable)として型宣言できます。

`callable`は単一のデータ型ではなく、要件を満たす値には以下のようなものがあります

 * `string`
   * 関数名文字列
   * 静的メソッド名文字列
 * `array`
   * `[$object, 'method']` のような組の配列
   * `['クラス名', 'method']` のような組の配列
 * `object`
   * [`Closure`](https://www.php.net/Closure)
   * [`__invoke()`](https://www.php.net/manual/ja/language.oop5.magic.php#object.invoke)を定義したクラスのインスタンスオブジェクト

ただし現在となっては`callable`型宣言をそのまま使うのではなく、以下の方法で`Closure`に変換した方がよいでしょう。

```php
// 愚直に無名関数でくるむ方法、パラメータ宣言のコピペが必要なのでめんどい
$c1 = function (string $format, string $datetime, ?DateTimeZone $timezone = null) { 
    return DateTimeImmutable::createFromFormat($format, $datetime, $timezone);
};
$result = $c1('Y-m-d', '2022-09-03');
var_dump($result);

// メソッド名文字列からClosureに変換する
$c2 = Closure::fromCallable('DateTimeImmutable::createFromFormat');
$result = $c2('Y-m-d', '2022-09-03');
var_dump($result);

// PHP 8.1から追加された第一級callableを生成する記法
$c3 = DateTimeImmutable::createFromFormat(...);
$result = $c3('Y-m-d', '2022-09-03');
var_dump($result);
```

https://www.php.net/manual/ja/functions.first_class_callable_syntax.php

:::note
今回の記事の本題ではないので触れませんが、PHPStanでは`Closure`や`callable`にも詳細に型付けできます
:::

## なぜあなたは混乱するのか

ほかの言語についての知識が前提にあり、PHPとの違いを把握できていない可能性があります。
特に、メソッドだけがあって関数が存在しない言語に触れた経験があると、すべてをメソッドと呼んでしまいがちです。

:::note warn
PHPの「**無名関数**」(Anonymous functions, Closure)に相当するものはC#では「**匿名メソッド**」(Anonymous methods)と呼ばれる、などの事例が混乱に拍車を掛けます。
:::

### Rubyの場合

Rubyのメソッド呼び出しは基本的には `obj.method(arg)` のような形式です。`obj`の部分は「レシーバ」と呼ばれますが、さまざまな場面で `obj.` が省略できます。また、メソッド呼び出し`method(arg)`はメソッドチェーンが不要な場合は`method arg`と書かれることも多いです。

```rb
require 'pp'
#~~~~~~

class Foo
  attr_reader :name
# ~~~~~~~~~~~

  def hoge
    @name = 'name'.upcase
#                 ~~~~~~~
    p bar
#   ~ ~~~
  end

  private def bar
# ~~~~~~~
    puts self.name
#   ~~~~     ~~~~~
    'success'
  end
   :bar
end
```

Rubyでは `require` や `private`, `exit`, `raise` (PHPの`throw`に相当)のような、ほかの言語では言語構文の一部であるようなものもメソッド呼び出しです。また、`p` や `puts`, `printf`, `rand` のような標準関数のようなもの、PHPではマジック定数として提供されている `__method__`, `__dir__` のようなものも[すべてカーネルモジュールで定義されています](https://docs.ruby-lang.org/ja/latest/class/Kernel.html#M_P)。

これらの関数的に使われるメソッドはRuby用語では[**モジュール関数**](https://docs.ruby-lang.org/ja/latest/method/Module/i/module_function.html)といい、`Object`には`Kernel`モジュールが暗黙的にincludeされているので、これらのメソッドはあたかも標準関数や言語の構文かのように使うことができるのです。

よってRubyでは「関数⊂メソッド」です。PHPでいうプロパティのような`self.name`ですらも、`attr_accessor`や`attr_reader`のようなアクセサメソッドが間に入ることで提供されています。


PHPにおけるメソッドはクラスに属するものですが、Rubyは[特異メソッド](https://docs.ruby-lang.org/ja/latest/doc/spec=2fdef.html#singleton_method)といって、インスタンスオブジェクトひとつひとつに固有のメソッドを定義できます。Rubyはクラスそのものも`Class`クラスのインスタンスオブジェクトであり、クラスメソッドはクラスの特異メソッドに過ぎません。

:::note info
これらの機能がどうやって実現されているか、PHPの静的メソッドとRubyのクラスメソッドの**決定的な差異**など、Rubyのオブジェクトシステムには語りたいことがたくさんあるのですが、この記事の趣旨からは逸れるのでこのあたりでやめておきます。気になる人は[**Rubyソースコード完全解説**](https://i.loveruby.net/ja/rhg/book/)の[第1章 Ruby言語ミニマム](https://i.loveruby.net/ja/rhg/book/minimum.html)や[第4章 クラスとモジュール](https://i.loveruby.net/ja/rhg/book/class.html)を読んでください。
PHP固有の事情についてもややこしい話があるので、また別の記事の機会に譲りましょう。
:::


### Javaの場合

PHPやC++には関数とメソッドの両方がありますが、**Javaに関数はありません**。

PHPやRubyで標準ライブラリ的に用意されている`printf()`はJavaでは[`System.out`](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/System.html) ([`PrintStream`](https://docs.oracle.com/javase/jp/8/docs/api/java/io/PrintStream.html))のメソッドとして提供されています。

Javaで関数呼び出しのように見えるものは、`this.`を省略したメソッド呼び出しです。

```java
class Hello {
    public static void main(String[] args) {
        sayhello();
    }
    
    public static void sayhello() {
        System.out.printf("%s", "Hello");
    }
}
```

PHPではこのような暗黙的な`$this->`呼び出しはありません。

```php
<?php

function sayhello(): void
{
    echo __FUNCTION__, ' function', PHP_EOL;
}

class Hello
{
    public __construct()
    {
        sayhello();
        $this->sayhello();
    }

    public static function sayhello: void
    {
        echo __METHOD__, ' method', PHP_EOL;
    }
}

$hello = new Hello();
```

メソッドと同名の関数があろうがなかろうが、`sayhello()`は関数呼び出し、`$this->sayhello()`はメソッド呼び出しであり、混同されることはありません。

### Pythonの場合

Pythonはここまでに挙げた二つの言語とは異なり、関数とメソッドをよく区別する言語です。

たとえばPythonの`sorted`と`list.sort`の使い分けは初心者には意味不明なほどPythonの思想を表しています。

```python
# 順不同の fruits リストを作る
>>> fruits = ['orange', 'apple', 'strawberry', 'banana']
# sorted 関数はソートされた新しいリストを返す
>>> sorted_fruites = sorted(fruits)
>>> sorted_fruites
['apple', 'banana', 'orange', 'strawberry']

# fruits は順不同のまま
>>> fruits
['orange', 'apple', 'strawberry', 'banana']
# list.sort メソッドを呼ぶと、オブジェクト自体がソート済みに変化する
>>> fruits.sort()
>>> fruits
['apple', 'banana', 'orange', 'strawberry']
```

Pythonでは文字列やリストなどのような「長さ」という性質を持ったオブジェクト(シーケンス)の長さを取得する際は`len(v)`、文字列に変換できるオブジェクトを文字列化するときは`str(v)`のように、汎用の操作は[組み込み関数](https://docs.python.org/ja/3/library/functions.html)の呼び出しを通じてアクセスするのが一般的な習慣です。その一方でクラスに[特殊メソッド](https://docs.python.org/ja/3/reference/datamodel.html#basic-customization)を定義することで、オブジェクトのさまざまな振る舞いを柔軟にカスタマイズできる設計になっています。

:::note warn
PHPにもPythonの特殊メソッドに相当する[マジックメソッド](https://www.php.net/manual/language.oop5.magic.php)が定義できますが、`__construct()`を除けば一般に活用されているものはあまり多くないでしょう。PHPではPythonの`len`に相当する`count()`関数で、長さを公開しているオブジェクトは[`Countable`](https://www.php.net/Countable)インターフェイスとして分離されています。
`(string)`キャストなどで文字列化するときの振る舞いを制御する`__toString()`マジックメソッドもありますが、これもPHP 8.0以降は`Countable`と同様に[Stringableインターフェイス](https://www.php.net/Stringable)に分離し、`Stringable::__toString()`として明示的に実装する方向に移りつつあります。
PHPの文字列は`count()`ではなく、バイト数を取得する`strlen()`と文字コードごとの文脈での文字列長(コードポイント数)を返す[`mb_strlen()`](https://www.php.net/mb_strlen)を使い分ける必要がありますが、そもそもPHPはPythonとは異なりバイナリ列とUnicode文字列を型レベルで区別できないため、現状においては妥当な区別と言えるでしょう。もっとも現状においては[Symfony String](https://symfony.com/doc/current/components/string.html)のようなユーザーランド実装があるほか、PHPコア開発者のDerick Rethans氏は[The PHP Foundationのコア開発者の所信表明](https://thephp.foundation/blog/2022/05/06/interview-with-core-developers/)にて`UString`を導入したいと意欲を示しているので、PHP 10くらいの頃には状況が変わってるのではないのでしょうか。
:::

これらのポイントを除いてもPythonとPHPのクラスはそもそもいろいろ違う(メソッドのパラメータに明示的に`self`を含める必要がなく最初から`$this`が特殊な変数として束縛されている、`@classmethod`と`@staticmethod`の使い分けがない、関数とクラスが別の名前空間なので`Foo()`関数と`Foo`クラスが別に存在できるなど)ので、Python経験者が無造作にPHPを書こうとするとひっかかる点は多いのではないかと思います。

## まとめ

カタカナ語が多くて覚えるの大変だとは思うのですが、技術者としては言葉の意味を大切にして仕事していきたいですね。