質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • PHP

    20382questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Zend Framework

    72questions

    Zend Frameworkは、PHP5で記述されたWebアプリケーションフレームワークです。Zend Frameworkには守らなければならない開発の規定というものは存在せず、MVCなどの複数のコンポーネントを提供しています。

self::と$this->の使い分け

解決済

回答 3

投稿

  • 評価
  • クリップ 11
  • VIEW 12K+

macchaka

score 17

外部のパートナー企業から納めて頂いたコードで、疑問があります。

前提

  • PHP5
  • ZendFramework1
  • 普段はCakePHPを使っている :-)

MVCのコントローラにおいて、各メソッドで共通して呼び出すprivate関数の呼び出し方が、self::で書かれていました。

class TestController extends Zend_Controller_Action {
    public function FooAction()
    {
        //パートナーさんが書かれたコード
        $ex = self::commonExample();
    }
    public function BarAction()
    {
        //私の普段の書き方
        $ex = $this->commonExample();
    }
    private function commonExample()
    {
        /* メソッドで概ね共通する処理 */
    }
}

commonExampleは、コンボボックスの初期値のセットとかです。本当にメソッドごとに共通した部分をサブルーチンにしているだけです。function init()を使わないのは、全てのメソッドで呼び出すわけではないからですが、その点は今回の関係性がありません。

self:: は、呼び出された同じクラスの中にあるものを指すという理解をしているのですが、コントローラではstaticで呼び出されるわけでもなく、継承するわけでもないので、私は$this->で呼び出していました。

発生した疑問

今回においてはどちらでも同じ挙動をしますので、どちらが間違っているわけでもないと思いますが、メソッドを追加するにあたって、同じコードの中で、self::$this->が混在しているのは気持ちが悪く、かといって無理解のままself::に合わせるのも納得がいきません。

正直、どっちでもいい話だと思いますが、作法としてはどうなのかが気になって、質問させて頂きました。

私は、明確な必要性がない限りは、$this->でよいと思っていました。コントローラなので継承されることはないのですが、仮にサブルーチンがprivateではなくpublicのfunctionだったとして、継承を考えたとしても、明確な必要性がなく、擬似変数$this->を避ける理由があるようには認識していません。

staticで呼び出されるような場合には、擬似変数は使えませんが、コントローラがstaticで呼び出されることを考慮する必要はないと思いました。しかしその上で、例えば使い分けするよりサブルーチンの呼び出しはself::で統一するというのが一般的な作法、もしくは私の知識不足で非常識な疑問を抱いているのであるならば、今後はそのように是正させて頂こうとは思っています。

  • サブルーチンの呼び出しはself::で行うのが一般的かどうか
  • 私が認識不足しているself::を使うべき理由がないかどうか

この2点を論点にご教示頂けますと幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+5

まずはまとめというか私の見解から

  • self::xxxのメソッドは、static宣言されているのですか?されてないなら単純に作った人のミスです。
  • 今更質問するということは、クラスが2種類の働き方をする設計を意識したこと無いのでは?だから現在の設計はクラスはインスタンス化して使う前提で設計されているはずなので、$this->で統一してもいいのでは?
  • 2種類の働き方をするクラスは人によっては「気持ち悪い。別にクラス2つ作ればいいじゃん」と感じるようです。今までどおり$this->で統一するやり方も十分ありです。

下記に根拠、2種類の働き方をするクラスの紹介をします。
※この2種類の働き方をするクラスって正式名称みたいなものはあるのですかね…?


正直、どっちでもいい話だと思いますが、作法としてはどうなのかが気になって、質問させて頂きました。

前提としてはクラスに紐づくメソッドか、インスタンスに紐づくメソッドかで決まります。

下記のようなDBとそのモデルがあったとして、例を含めて説明します。
DBのテーブル「users」とそのユーザーが管理画面で操作するのに「projects」というテーブルを用意します。

  • テーブル「users」にアクセスする「model/user.php」と「class User」
  • テーブル「projects」にアクセスする「model/project.php」と「class Project」

「削除済みのユーザー一覧」が欲しいという要望があったので、usersテーブルはdeletedというカラムを持ち、deletedに日付が入っているレコードは論理削除されたという設計にしました。
また、Userモデルの実装でUser::find_deleted_usersというメソッドを用意し、
select * from users where deleted is not null;というSQLが走り、帰ってきたデータをUsersのインスタンスの配列で返す設計にしました。

削除されたユーザーが作ったプロジェクトも論理削除したいですよね?
Userのインスタンス用のメソッドにprojectsを作成して、Project::find_by_user_id($id)というメソッドを用意して、そのインスタンスが帰るように設計しました。

// 削除されたユーザーに紐づくプロジェクトの配列を取得する
$target_projects = [];
foreach (User::find_deleted_users() as $user) {
  foreach ($user->projects as $project) {
    $target_projects[] = $project;
  }
}

以上の設計では、UserやProjedctというクラスが2つの役割を持っている事がわかります。
このケースでは$thisとselfは相互不干渉であることがわかります。


クラスの基礎では スタティックではないメソッドのスタティック呼び出し全般が非推奨とあります。
これは質問者さんのような「上記の前提を業務に置き換えた時、どっちが$this->でどっちがselfかもうわかんね!」となった人が多いのでしょう

PHP7.0は良いガイドラインで、PHP5.6以下でも上記の設計に従った方が良いでしょう。
上記の例に当てはめると、下記のようにメソッドを定義する時点で明文化して相互不干渉ということを宣言する必要があります。

class User {
  public $user;
  function __construct ($user) {
    $this->user = $user;
  }
  function projects() {
    return Project::find_by_user_id();
  }
  static function find_deleted_users() {
    $sql = "select * from users where deleted is not null;";
    // DBに接続
    return $users;
  }
}

追記:インスタンス化されたオブジェクトがself::xxxを使う意味について解説してなかったですね…
この辺は完全に設計者の好みになることをまず前提として書いておきます。

数学的な意味での関数ある変数に依存して決まる値あるいはその対応を表す式の事であった。と説明されています。
クラス定数なんかもPHPでは宣言出来ますが、staticのメソッドはその関数版として扱う事も考えられます。

この辺は関数型プログラミングの話になってきますので端折りますが、
staticメソッドは≒参照透過性のメソッドという宣言なわけで、
単体テストのコードによる自動テストが非常にしやすく、正常な動作を保証しやすい事がメリットとして挙げられます。

とりあえず、出来ればそのself::を使った方と一度話してみてください。
(説明が要領得なかったり、staticのないメソッドをselfで叩いているなら論外ですが)
この考えが気に入ればstaticなメソッドを増やせないかを考えて出来るだけselfで統一していっても良いですし、
別に気に入らなければ今まで通り$this->を使っていっても良いかと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/17 14:09

    > staticのないメソッドをselfで叩いているなら論外ですが
    残念ながらそれに相当します。今回でいえば『単純に作った人のミスです』ね。すっきりしました。

    ただ私の根本的な認識間違いは、self::xxxが、静的コール「のみ」を意味し、staticのないメソッドで使用するのが誤りであることを理解していなかったことです。

    私自身はself::xxxは「感覚的に」静的コールでしか使いませんが、自分の中でこの点が正しく(根拠をもって)理解できておらず、またエラーになっていなかったために、「そんな書き方もあるのか」と驚き(結果としては無かった)、「もしかしてそれが普通なのか?」という誤解(結果としては単なるミス)を抑えられず、質問に至りました。

    要は、staticで無い限りはスコープ定義演算子を使って呼び出すことはないということですよね。


    > 今更質問するということは、クラスが2種類の働き方をする設計を意識したこと無いのでは?

    原理は理解しているつもりでしたが、仰る通り、フレームワークに則るだけで、静的メソッドを使う設計を意識したことはほとんどありません(静的コールはフレームワークでそうするから使い、応用する範囲)。なので、スコープ演算子についても浅い理解だったのだと思います。

    感覚的には静的メソッドの意義は分かったように思います。今回の件でいえば
    ・self::を$this->にするか、functionにstatic宣言を付けるかは『好み』であろうと思いますが、コントローラはインスタンス化を前提としているので、前者でよい(好み)。
    ・静的メソッドはテストがしやすいので、スコープ演算子を使って静的コールする方法も取り込むのがよい。(もちろんsingletonパターンの使い方としてもですが)
    といった理解になりました。

    私が知りたかったこと、私が間違って理解していることが、エビデンスを含め、明確にして頂きました。
    的を射た回答ありがとうございました。

    キャンセル

  • 2017/01/17 15:25

    的外れでないか回答文作りながらヒヤヒヤしましたが、ちゃんと的に刺さったようでよかったです。

    > staticで無い限りはスコープ定義演算子を使って呼び出すことはない
    古いバージョンではこれでも良かった書き方ですから、はいだめーということはなさそうですね。
    PHPの公式サイトは良くメンテされていますので、
    作業担当者さんが古い書き方のまま覚えてしまい、このアナウンスをまだ見てなかっただけかと思います。

    古いバージョンのPHPで皆が好き勝手にコーディングしていて分かりづらかったので、公式がstaticのみ::でアクセスしようねと正式にアナウンスしたと予想しています。
    今後は仮にPHP5.6以下を使っている現場でもアナウンスを見習ってこの書き方を取り入れていきましょう!
    (しっかし、PHPの公式サイトは情報量が多すぎて分かりづらい…)

    > クラスが2種類の働き方をする設計
    間違って理解しているという程でもないように感じました。
    別にぜーんぶインスタンス化してしまう設計もキレイに表現出来ますよね。
    対応しているフレームワークも少ないですし、書きにくいですし、インスタンス化して使う人の方が一般的なんじゃないかなぁと思います。

    キャンセル

+1

意味を理解して使いましょう、としかいえません。

参考:
PHP: クラスの基礎 - Manual
PHPで「self::」と「$this」の違いを理解する。
sto's Blog: php 「$this」 , 「self::」について

余談ですが、この件を調べていて、こういうトピックもあったのでご参考まで。

PHP: 遅延静的束縛 (Late Static Bindings) - Manual

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/16 21:04

    お示し頂いたリンク先は、概ね参照済でした。
    staticに関しては、今回はMVCのコントローラ内でのことなので、関係ないと思っています。
    m6uさんは、今回のケースはどちらで書きますか?

    キャンセル

  • 2017/01/16 21:15 編集

    コントローラであっても、基底クラスがあって継承しているわけですから、
    メソッドの呼び方には拘るべきかと存じます。

    メソッドに関しては、どこで宣言されているメソッドにアクセスするのか、
    が判断基準になるかと思います。
    自クラスでの宣言なのか、親クラスでの宣言なのか、など。

    プロパティ(や内部変数)であれば紛れもなく $this を使うでしょうね。
    自インスタンスに備わるデータにアクセスするので。
    インスタンス化せずにアクセス可能なものは self:: を使うでしょうけど。

    (と書いてみて、マニュアル通りの回答だなと。)

    キャンセル

  • 2017/01/16 21:45

    今回は自クラスでの宣言の場合をお尋ねしているわけですね。親クラスであれば、呼び方には留意が必要だという認識はあります。コントローラはディスパッチャが呼び出すので、そこからサブクラスに継承されることもないと思います。

    今回は内部関数(変数とありましたが、phpにおいてはメンバ変数とプロパティに違いはないと認識しています)ですし、コントローラがインスタンス化せずに呼び出されることはないと思いますので(privateですし)、私の認識である`$this->`を使うというのが正しいということでよろしいでしょうか?

    マニュアルには、インスタンスで同じクラスの関数を呼び出すのに、`self::`を使ってはいけないとは書かれていないし、現に`self::`でも動作するので確信が持てませんでしたが、「紛れもなく」とコメント頂いたので、少し安心しました。

    パートナーさんが`self::`で書いているのを、認識間違いとまで言えるのかどうか。。。

    キャンセル

+1

つい先日同じ質問を持ってました。
ここの表が理解しやすかったです。

PHPオブジェクト指向入門(前半):: 静的プロパティ / 静的メソッド / クラス定数

合わせて前後を読むと理解が深まるかと。

お作法的な選択もありそうですが、そちらは私では回答できないので、他の方から回答があるとイイのですが。。。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/16 21:29

    確かにこの表は分かりやすいですね。私の認識とも大きな差違はないと思います。

    これによれば、「(インスタンスとして呼び出される)コントローラ内では、同じクラス内のメソッドを呼び出すのは`self::`ではなく、`$this->`を使う」という私の認識は間違っていない…ということになるでしょうか。
    ただ、この表だと、インスタンスメソッドからインスタンスメソッドをスコープ演算子を使って呼ぶには「$this::name()」ということになっていますが、`self::name()`で呼び出せますよね(現にパートナーさんのコードはそうなっている)。私も`self::`がstaticでしか使えないとは思っていなかったので、このあたりは謎です。
    しかしながら、一つ自分の中でも根拠が持てました。ありがとうございます。

    キャンセル

  • 2017/01/17 08:28

    正直、理解が浅いので、コメントする立場にないのですが、(インスタンスとして呼び出される)コントローラ内で同じクラス内のメソッドを「静的メソッド」として呼び出すケースもあると認識しています。

    間違った認識の可能性もあるので、識者のツッコミ待ちですが、インスタンス内での呼び出しに関しては「self::」と「$this->」の使い分けをしなくて良いケースも多々あるのではないかと思っています。この場合の選択基準は、お作法によるものなのではないかと。

    ただし、本来意味の違うものなので当然ですが、インスタンス化されたものと、静的な呼び出しをしたものの挙動が違う場合、使い分けが必要です。

    私もトリガーが「self::と$this->が混在しているのは気持ちが悪く」だったのでよく分かるのですが、「本来意味の違う」事を理解するとワリとすんなりと受け入れられます。
    むしろ混在している方が、「これは何を意識しているんだろう」って感じですね。

    同じ挙動をする時の、使い分けの仕方は、私も非常に気になるところです。イイ回答がつくといいのですが。

    キャンセル

  • 2017/01/17 09:19

    > (インスタンスとして呼び出される)コントローラ内で同じクラス内のメソッドを「静的メソッド」として呼び出すケースもある

    このケースがぴんときませんでした。

    静的というのは、インスタンス化しないで使うということだと理解しているのですが、一旦インスタンス化したオブジェクト(コントローラ)の中で同一オブジェクト内のメソッドを静的メソッドとして呼び出すというのは、どういうことでしょうか。

    他のコントローラ(クラス)を静的メソッドとして呼び出すということなら分からなくもないのですが、CakePHPでは、複数のコントローラから共用するものは、componentとしてまとめるので、その発想はありませんでした。

    今回の例では、継承されないことが前提なので、self::と$thisの違いは、スコープ演算子を使うか擬似変数を使うかの違いしかないと認識しているのですが、「parent::と同様に、self::は特に静的呼び出しに限るわけではない」という認識が間違っているのでしょうか。

    キャンセル

  • 2017/01/17 11:24

    完全に理解してない者同士のコメントに意味があるのか微妙ですが^^;

    例えば、
    class Hoge {
    ・・・
    public static function fuga($str) {
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
    }
    }
    のように、インスタンス化して使う前提の Hoge の中に単なる静的メソッドとしても利用できるモノが入っていたとして、その呼出はどちらで呼び出してもイイのではないかと考えています。
    miyabi-sun の回答を今読み解いてる最中ですが、「気持ち悪い。別にクラス2つ作ればいいじゃん」の事を言っているのだと思います。

    今回の例で正しい使い方なのかどうかはちょっと判断がつきません。

    キャンセル

  • 2017/01/17 13:38

    クラスの中に静的メソッドが入ることについては同意です。「コントローラ」の中に入るのがピンと来ませんでしたが、どのフレームワークを使うかによっても違うのかもしれません。CakePHPは「設定より規約」が強いフレームワークなので。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • PHP

    20382questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • Zend Framework

    72questions

    Zend Frameworkは、PHP5で記述されたWebアプリケーションフレームワークです。Zend Frameworkには守らなければならない開発の規定というものは存在せず、MVCなどの複数のコンポーネントを提供しています。