web-dev-qa-db-ja.com

PHP関数内でグローバル

グローバルキーワード のユーティリティは何ですか?

ある方法を別の方法よりも好む理由はありますか?

  • セキュリティ?
  • パフォーマンス?
  • 他に何か?

方法1:

function exempleConcat($str1, $str2)
{
  return $str1.$str2;
}

方法2:

function exempleConcat()
{
  global $str1, $str2;
  return $str1.$str2;
}

いつglobalを使用する意味がありますか?

私にとっては、それは 危険であるように見える ...ですが、知識が不足しているだけかもしれません。私は文書化された(例えば、コードの例、文書へのリンク...)技術的な理由に興味があります。

前もって感謝します!


バウンティ

これは、このトピックに関するニースの一般的な質問です。私(@Gordon)は、追加の回答を得るための報奨金を提供しています。あなたの答えが私の意見と一致するか、異なる視点を与えるかは問題ではありません。 globalトピックがときどき出てくるので、リンクするのに適切な「標準的な」回答を使用できます。

98
Pascal Qyy

グローバルは悪です

これは、globalキーワードと、ローカルスコープからグローバルスコープに到達する他のすべて(静的、シングルトン、レジストリ、定数)にも当てはまります。あなたはそれらを使いたくありません。関数呼び出しは、外部のものに依存する必要はありません。

_function fn()
{
    global $foo;              // never ever use that
    $a = SOME_CONSTANT        // do not use that
    $b = Foo::SOME_CONSTANT;  // do not use that unless self::
    $c = $GLOBALS['foo'];     // incl. any other superglobal ($_GET, …)
    $d = Foo::bar();          // any static call, incl. Singletons and Registries
}
_

これらはすべて、コードを外部に依存させます。つまり、これらのいずれかを確実に呼び出すには、アプリケーションが存在する完全なグローバル状態を知る必要があります。関数は、その環境なしでは存在できません。

スーパーグローバルの使用は明らかな欠陥ではないかもしれませんが、コマンドラインからコードを呼び出す場合、_$_GET_または_$_POST_はありません。コードがこれらからの入力に依存している場合、Web環境に制限されます。リクエストをオブジェクトに抽象化し、代わりにそれを使用します。

ハードコーディングされたクラス名(静的、定数)を結合する場合、そのクラスが使用可能でなければ関数も存在できません。同じ名前空間のクラスである場合は問題は少なくなりますが、異なる名前空間からミックスを開始すると、複雑な混乱が生じます。

上記のすべてにより、再利用が厳しく妨げられます。 ユニットテストも同様

また、グローバルスコープに結合すると、関数のシグネチャが嘘をつきます。

_function fn()
_

嘘をつきます。何も渡さずにその関数を呼び出すことができると主張しているからです。環境を特定の状態に設定する必要があることを学んだのは、関数本体を見るときだけです。

関数の実行に引数が必要な場合は、引数を明示的に指定して渡します:

_function fn($arg1, $arg2)
{
    // do sth with $arguments
}
_

署名から呼び出す必要があるものを明確に伝えます。特定の状態にあることは環境に依存しません。あなたはする必要はありません

_$arg1 = 'foo';
$arg2 = 'bar';
fn();
_

引き込む(グローバルキーワード)と押し込む(引数)の問題です。依存関係をプッシュイン/インジェクトすると、関数は外部に依存しなくなります。 fn(1)を実行する場合、外部のどこかに1を保持する変数を持つ必要はありません。ただし、関数内でグローバル_$one_をプルすると、グローバルスコープに結合され、その変数がどこかで定義されていることが期待されます。この場合、関数はもはや独立していません。

さらに悪いことに、関数内のグローバルを変更する場合、コードはすぐに完全に理解不能になります。関数がいたるところに副作用を持っているからです。

より良い例が不足している場合、考慮してください

_function fn()
{
    global $foo;
    echo $foo;     // side effect: echo'ing
    $foo = 'bar';  // side effect: changing
}
_

そして、あなたはやる

_$foo = 'foo';
fn(); // prints foo
fn(); // prints bar <-- WTF!!
_

_$foo_がこれらの3行から変更されたことを確認する方法はありません。同じ関数を同じ引数で呼び出すと、出力が突然変更されたり、グローバル状態の値が変更されたりするのはなぜですか?関数は、定義された入力Yに対してXを実行する必要があります。常に。

OOPはカプセル化に関するものであり、グローバルスコープに手を伸ばすことでカプセル化を解除しているため、OOPを使用するとさらに深刻になります。依存性注入を支持して削除する必要があります。コードを分離してください。

その他のリソース:

154
Gordon

globalに対する1つの大きな理由は、関数が別のスコープに依存していることを意味します。これはすぐに面倒になります。

$str1 = 'foo';
$str2 = 'bar';
$str3 = exampleConcat();

vs.

$str = exampleConcat('foo', 'bar');

$str1および$str2を関数の呼び出しスコープで設定する必要があるということは、不要な依存関係が生じることを意味します。関数で変数の名前を変更しない限り、このスコープのこれらの変数の名前を変更することはできません。したがって、この関数を使用している他のすべてのスコープでも同様です。変数名を追跡しようとすると、これはすぐに混乱に変わります。

globalは、$dbリソースなどのグローバルなものを含める場合でも不適切なパターンです。そこにwill$dbの名前を変更したいが、名前を変更できない日が来るのは、アプリケーション全体が名前に依存しているためです。

変数のスコープの制限と分離は必須であり、複雑なアプリケーションを作成するために使用します。

35
deceze

グローバルは避けられません。

これは古い議論ですが、上記の回答で見逃しているので、いくつかの考えを追加したいと思います。これらの答えは、グローバルが多すぎるものを単純化し、問題に対するすべての解決策ではない解決策を提示します。問題は、グローバル変数とキーワードglobalの使用を処理する適切な方法は何ですか?そのためには、まずグローバルとは何かを調べて説明する必要があります。

Zendのこのコードを見てください。Zendの記述が不適切であることを示唆していないことを理解してください。

class DecoratorPluginManager extends AbstractPluginManager
{
/**
 * Default set of decorators
 *
 * @var array
 */
protected $invokableClasses = array(
    'htmlcloud' => 'Zend\Tag\Cloud\Decorator\HtmlCloud',
    'htmltag'   => 'Zend\Tag\Cloud\Decorator\HtmlTag',
    'tag'       => 'Zend\Tag\Cloud\Decorator\HtmlTag',
   );

ここには目に見えない依存関係がたくさんあります。これらの定数は実際にはクラスです。また、このフレームワークのいくつかのページでrequire_onceを見ることができます。 Require_onceはグローバルな依存関係であるため、外部依存関係が作成されます。それはフレームワークにとって必然です。依存する多くの外部コードなしでDecoratorPluginManagerのようなクラスを作成するにはどうすればよいですか?多くのエキストラなしでは機能しません。 Zendフレームワークを使用して、インターフェイスの実装を変更したことがありますか?インターフェースは、実際にはグローバルです。

別のグローバルに使用されるアプリケーションはDrupalです。彼らは適切な設計に非常に関心を持っていますが、他の大きなフレームワークと同様に、多くの外部依存関係があります。このページのグローバルをご覧ください。

/**
 * @file
 * Initiates a browser-based installation of Drupal.
 */

/**
 * Root directory of Drupal installation.
 */
define('DRUPAL_ROOT', getcwd());

/**
 * Global flag to indicate that site is in installation mode.
 */
define('MAINTENANCE_MODE', 'install');

// Exit early if running an incompatible PHP version to avoid fatal errors.
if (version_compare(PHP_VERSION, '5.2.4') < 0) {
  print 'Your PHP installation is too old. Drupal requires at least PHP 5.2.4. See the     <a     href="http://drupal.org/requirements">system requirements</a> page for more     information.';
  exit;
}

// Start the installer.
require_once DRUPAL_ROOT . '/includes/install.core.inc';
install_drupal();

ログインページへのリダイレクトを書いたことがありますか?それはグローバルな価値を変えています。 (そして、「WTF」と言っているのではありません。これは、アプリケーションの不適切なドキュメントに対する良い反応と考えています。)問題は、アプリケーション全体の複雑さであり、処理するのが悪夢です。セッションはグローバル、$ _ POSTはグローバル、DRUPAL_ROOTはグローバル、includes/install.core.inc 'は変更不可能なグローバルです。その機能を機能させるために必要な機能の外側には大きな世界があります。

Gordonの答えは間違っています。関数の独立性を過大評価しており、関数を嘘つきと呼ぶことで状況が単純化されているためです。関数は嘘をつかず、彼の例を見ると、関数は不適切に設計されています-彼の例はバグです。 (ところで、コードを分離する必要があるというこの結論に同意します。)decezeの答えは、状況の適切な定義ではありません。関数は常により広い範囲で機能し、彼の例はあまりにも単純すぎます。定数を返すので、その関数は完全に役に立たないことを彼はすべて同意します。その機能はとにかく悪い設計です。練習が悪いことを示したい場合は、関連する例をご用意ください。アプリケーション全体で変数の名前を変更することは、良いIDE(またはツール)を持つことは大したことではありません。問題は変数のスコープに関するものであり、関数とのスコープの違いではありません。関数がプロセスでその役割を実行する適切な時間(それが最初に作成される理由です)およびその適切な時間に、アプリケーション全体の機能に影響を与える可能性があり、したがってグローバル変数にも取り組んでいます。 xzyferの引数は引数なしのステートメントです。手続き関数またはOOP設計。グローバルの値を変更する次の2つの方法は、基本的に同じです。

function xzy($var){
 global $z;
 $z = $var;
}

function setZ($var){
 $this->z = $var;
}

どちらの場合も、特定の関数内で変更された$ zの値です。プログラミングの両方の方法で、コード内の他の場所でこれらの変更を行うことができます。 globalを使用すると、どこでも$ zを呼び出して変更できると言えます。はい、できます。しかし、あなたは?そして、不適切な場所で行われた場合、それはバグと呼ばれるべきではありませんか?

Bob Fangerはxzyferについてコメントしています。

誰もが何か、特にキーワード「グローバル」を使用する必要がありますか?いいえ。ただし、他のデザインと同様に、それが依存しているものと依存しているものを分析してみてください。それがいつ変化し、どのように変化するかを調べてみてください。グローバル値の変更は、すべての要求/応答で変更できる変数でのみ発生します。つまり、技術的な実装ではなく、プロセスの機能フローに属する変数にのみです。ログインページへのURLのリダイレクトは、技術的な実装へのインターフェイスに使用される実装クラスであるプロセスの機能フローに属します。後者はアプリケーションのさまざまなバージョンで変更できますが、要求/応答ごとに変更しないでください。

グローバルとキーワードglobalの操作が問題になる場合とそうでない場合をさらに理解するために、ブログについて書くときにWim de Bieから来る次の文を紹介します。関数が独自の機能のためにグローバル変数の値を変更している場合、グローバル変数とバグのプライベート使用を呼び出します。しかし、ユーザーのログインページへのリダイレクトなど、アプリケーション全体の適切な処理のためにグローバル変数の変更が行われる場合、それは私の意見では定義上悪いことではなく、確かに悪いことではありませんアンチパターン。

Gordon、deceze、xzyferの回答を振り返ってみると、これらはすべて例として「private yes」(およびバグ)を持っています。それが彼らがグローバルの使用に反対している理由です。私もやります。しかし、彼らは「個人的なはい、私的ないいえ」を持っていません-私がこの答えで数回やったような例。

34
Loek Bergman

簡単に言えば、globalの理由はめったになく、現代のPHPコードIMHOでは決して良い理由ではありません。特にPHP 5.を使用している場合、そしてオブジェクト指向コードを開発している場合は特に特別です。

グローバルは、コードの保守性、可読性、およびテスト可能性に悪影響を及ぼします。 globalの多くの使用法は、依存性注入または単にグローバルオブジェクトをパラメーターとして渡すことで置き換えることができます。

function getCustomer($db, $id) {
    $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id));
    return $row;
}
14
xzyfer

PHPの関数内でグローバルキーワードを使用することをためらわないでください。特に、グローバルがどのように「悪」であり、何であるかを異常に説教/叫んでいる人々を連れてはいけません。

第一に、使用するものは状況と問題に完全に依存するため、コーディングで何かを行う解決策/方法はありません。 「悪」のような定義できない、主観的な、宗教的な形容詞の誤equationを完全に捨てて方程式に入れます。

適例 :

Wordpressとそのエコシステムは、その機能にグローバルキーワードを使用しています。コードOOPまたはOOPではない。

そして現在、Wordpressは基本的にインターネットの18.9%であり、ReutersからSony、NYT、CNNまで、無数の巨人の巨大なメガサイト/アプリを実行しています。

そして、それはうまくいきます。

関数内でのグローバルキーワードの使用は、巨大なエコシステムを考えると起こるだろうMASSIVE bloatからWordpressを解放します。すべての関数が、別のプラグイン、コア、およびプラグインの相互依存性により、悪夢のような変数、または変数として渡された悪夢のような結果になります。追跡する地獄、デバッグする地獄、開発する地獄。書くのも難しい。

Wordpress、そのエコシステム、彼らの慣行、およびそれらの部分で何が起こっているかを批判してくる人々がいるかもしれません。

このエコシステムはインターネット全体のほぼ20%であるため、意味がありません。どうやら、それは仕事をし、仕事をします。つまり、グローバルキーワードでも同じです。

別の良い例は、「iframeは悪」原理主義です。 10年前、iframeを使用することは異端でした。そして、インターネットの周りで何千人もの人々が彼らに対して説教をしました。その後、facebookが登場し、ソーシャルも登場します。iframeは、「いいね」ボックスから認証、そして出来事までどこにでもあります。まだ黙っていない人がいます-正当に、または誤って。しかし、あなたは知っています、そのような意見にもかかわらず人生は続きます。10年前にiframeに反対して説教していた人でさえ、今ではそれらを使用してWordを言わずに組織のアプリケーションにさまざまなソーシャルアプリを統合する必要があります。

......

コーダーファンダメンタリズムは非常に悪いものです。情報技術の絶え間ない変化と、競争、時間、予算、その他の考慮事項に関する圧力に耐えるのに十分な影響力を持っている堅実なモノリシック企業の快適な仕事に恵まれている人もいます。原理主義と知覚された「悪魔」または「財」の厳格な遵守。占領者が若い場合でも、これらは古い時代を連想させる快適な位置です。

ただし、大半の場合、i.t。世界は常に変化する世界であり、彼らはオープンマインドで実用的である必要があります。原理主義の場所はありません。情報技術の最前線の溝に「悪」のようなとんでもないキーワードを置いておきます。

問題に対して最も意味のあるものを使用してくださいAT HAND、近、中、および長期の将来を適切に考慮してください。イデオロギーがramp延しているため、機能やアプローチの使用をためらわないでください任意のコーダーサブセットの中で、それに対する敵意。

彼らはあなたの仕事をしません。あなたはするであろう。状況に応じて行動してください。

8
unity100

Globalキーワードを使用してconcat関数を作成しても意味がありません。

データベースオブジェクトなどのグローバル変数にアクセスするために使用されます。

例:

function getCustomer($id) {
  global $db;
  $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id));
  return $row;
}

Singleton pattern のバリエーションとして使用できます

6
Bob Fanger

誰もがグローバルのマイナス面についてかなり説明していると思います。したがって、グローバルを適切に使用するための手順と同様に、プラスを追加します。

  1. グローバルの主な目的は、機能間で情報を共有することでした。クラスのようなものがなかった頃、PHPコードは一連の関数で構成されていました。機能間で情報を共有する必要がある場合があります。通常、グローバルを使用してこれを行うと、データをグローバルにすることでデータが破損するリスクがあります。

    幸運な幸運なシンプトンが依存性注入についてコメントを始める前に、例get_post(1)のような関数のユーザーが関数のすべての依存性をどのように知るかを尋ねたいと思います。また、依存関係が異なる場合があることも考慮してください
    バージョンからバージョン、サーバーからサーバー。依存性注入の主な問題は、依存関係を事前に知っておく必要があることです。これが不可能な場合、または望ましくないグローバル変数がこの目標を達成する唯一の方法でした。

    クラスの作成により、一般的な機能をクラスに簡単にグループ化してデータを共有できるようになりました。メディエーターのような実装により、無関係なオブジェクトでも情報を共有できます。これはもう必要ありません。

  2. グローバルのもう1つの用途は、構成のためです。ほとんどの場合、オートローダーが読み込まれる前、データベース接続が行われる前などのスクリプトの開始時。

    リソースのロード中に、グローバルを使用してデータを構成できます(つまり、ライブラリファイルが置かれている場所で使用するデータベース、サーバーのURLなど)。これを行う最良の方法は、define()関数を使用することです。これらの値は頻繁に変更されず、構成ファイルに簡単に配置できるためです。

  3. グローバルの最終的な用途は、共通データ(CRLF、IMAGE_DIR、IMAGE_DIR_URL)、人間が読めるステータスフラグ(ITERATOR_IS_RECURSIVE)を保持することです。ここで、グローバルは、アプリケーション全体で使用される情報を保存するために使用され、それらを変更し、それらの変更をアプリケーション全体で表示することができます。

  4. シングルトンパターンは、オブジェクトの各インスタンスがメモリを占有するphp4の間にphpで一般的になりました。シングルトンは、オブジェクトのインスタンスを1つだけ作成できるようにすることで、RAMを節約するのに役立ちました。参照がなければ、依存性の注入でさえ悪い考えでした。

    PHP 5.4+からのオブジェクトの新しいphp実装は、これらの問題のほとんどを処理し、ペナルティなしでオブジェクトを安全に渡すことができます。これはもはや必要ありません。

    シングルトンのもう1つの用途は、オブジェクトのインスタンスが一度に1つだけ存在する特別なインスタンスです。そのインスタンスはスクリプト実行の前後に存在し、そのオブジェクトは異なるスクリプト/サーバー/言語などで共有されます。ソリューションは非常によく。

したがって、結論として、ポジション1、2、または3にいる場合は、グローバルを使用するのが妥当です。ただし、他の状況では方法1を使用する必要があります。

グローバルを使用する必要がある他のインスタンスを自由に更新してください。

6
mAsT3RpEE