PHP ユーザー関数定義 引数 スコープ などなどメモ

##記事を書いた人
PHP勉強中の初心者です。
はじめてユーザー関数を使ってコードの短絡化に挑戦した時に混乱したことをまとめてみました。


###記事の対象者
自分と同じような初心者の方のお役に立てればと思います。

##開発環境
PHP 7.3.11
OS macOS Catalina ver. 10.15.3

##関数とは
変数を「値を入れる箱」というならば、関数は「一連の処理を入れておく箱」のようなイメージでしょうか。

関数には２種類あります。
１. 組み込み関数
2. ユーザー定義関数

組み込み関数とは、PHPに元々用意してある関数のこと。例えばmb_strlen()とかisset()とか。
膨大な数の関数が用意されています。

ユーザー定義関数とは、自分で作る関数です。
作成するメリットとしては
 1. 繰り返し登場する処理を何度も書かずに済む
 2. 繰り返し登場する処理に編集が必要になった際、関数化してあれば関数を編集するだけで済む
 3. コードの短絡化が出来て、可読性が上がる
といったところでしょうか。

```php:function.php
function echoHello(){
   echo "hello";
}
```
といった感じで作成します。

###関数化で躓いたところ(1)

####変数のスコープ
「スコープ」とは、参照可能な範囲のことを言います。
例えば、以下のユーザー定義関数には5つの登場人物がいます。
定数(1)(2)、変数(3)(4)(5)(6)、関数(7)です。

```php:function.php
const DSN='mysql:dbname=hage;host=localhost;charset=utf8mb4';
define("CONSTANT", "Hello world.");

$message = "Hello";
$_POST['message'] = "Hello";

function hage($x, $y){           //            ┐
  echo DSN . "\n";               //(1)         │
  echo CONSTANT. "\n";           //(2)         │
  echo $message. "\n";           //(3)         │  ☆
  echo $_POST['message'] ."\n";  //(4)         │
  echo $x. "\n";                 //(5)         │
  echo $y. "\n";                 //(6)         │
}                                //            ┘

hage(100, $message);     //(7)

``` 

この関数を実行するとどうなるでしょうか。

```php:qiita.php
mysql:dbname=hage;host=localhost;charset=utf8mb4//(1)
Hello world.//(2)

Notice: Undefined variable: message in /Applications/XAMPP/xamppfiles/htdocs/trial/trial.php on line 160  //(3)

Hello//(4)
100//(5)                                
Hello//(6)
```

(3)のところでNoticeエラーが発生します。`$message`変数は定義されていないと怒られます。
私はここで混乱しました。だって`$message`は定義したと思っていたからです。

ここで、先ほど言及したスコープの登場です。
それぞれの登場人物の「参照可能な範囲 = スコープ」を見ていきます。

`①定数のスコープ → グローバル。つまり、ソースコードのどこからでも呼び出し可能`
(1)(2)共に関数の外で定義しましたが関数内に呼び出す事ができました。

`②関数のスコープ → グローバル。つまり、ソースコードのどこからでも呼び出し可能`
(7)で、hageを呼び出すことに成功しています。

`③スーパーグローバル変数のスコープ → グローバル。つまり、ソースコードのどこからでも呼び出し可能`
PHPが用意している特殊な変数です。`$_SESSION`, `$_REQUEST`などがあります。
(https://www.php.net/manual/ja/language.variables.superglobals.php)

`④変数のスコープ → それを定義したレベルでのみ呼び出し可能`
つまり、関数内で定義した変数は関数内だけで有効。関数外で定義した変数とは変数名が同じでも別物ということです。(3)の$messageは☆マークの範囲内でしか読み書き出来ません。
ここが躓きポイントでした。


###関数化で躓いたところ(2)
####引数について

引数とは、上のコードでいうと

```php:qiita.php
function hage($x, $y){ 
```
の`$x`, `$y` の事です。`$x`は第一引数、`$y`は第二引数と呼ばれます。
また厳密に言うと、`$x`,`$y`は「仮引数」と呼ばれます。
仮引数は必ず変数になります。その値は関数が呼び出される時に決まります。

上記のコードで言うと、以下のように関数の引数を設定して値を決めています。

```php:qiita.php
hage(100, $message);     //(7)
```
上記の100, `$message`を実引数と呼びます。
ここでは、「仮引数の`$x`に実引数100を、仮引数の`$y`に実引数の`$message`を渡している」、
または、「仮引数に実引数（100と`$message`）を指定して、関数hage()を呼び出している」
と、言えますね。



関数呼び出し時に引数を設定し忘れると致命的なエラーを吐きます。

```php:qiita.php
hage();          //(1)
hage($message);  //(2)
//Fatal error: Uncaught ArgumentCountError: Too few arguments to function hage(),...  (3)
```

(1)は実引数の指定忘れ、(2)は実引数が一つ足りません。hage関数では仮引数を2つ設定したので、実引数も2つ渡してあげないといけません。

では、関数に引数を設定しないとどうなるでしょうか。

```php:qiita.php
const DSN='mysql:dbname=hage;host=localhost;charset=utf8mb4';
define("CONSTANT", "Hello world.");
$message = "Hello";
$_POST['message'] = "Hello";

function hage(){                 //            ┐
  echo DSN . "\n";               //(1)         │
  echo CONSTANT. "\n";           //(2)         │
  echo $message. "\n";           //(3)         │  ☆
  echo $_POST['message'] ."\n";  //(4)         │
  echo $x. "\n";                 //(5)         │
  echo $y. "\n";                 //(6)         │
}                                //            ┘

echo hage();     //(7)
```

結果は以下のようになりました。

```php:qiita.php
mysql:dbname=hage;host=localhost;charset=utf8mb4//(1) 
Hello world. //(2)

Notice: Undefined variable: message in /Applications/XAMPP/xamppfiles/htdocs/trial/trial.php on line 160 //(3)

Hello //(4)

Notice: Undefined variable: x in /Applications/XAMPP/xamppfiles/htdocs/trial/trial.php on line 162 //(5)

Notice: Undefined variable: y in /Applications/XAMPP/xamppfiles/htdocs/trial/trial.php on line 163 //(6)
```

スコープが関数内に限る(3)の`$message`に加えて、(5)(6)の`$x`,`$y`も未定義のエラーとなりました。
どこにも定義していないので当然ですね。

####返り値について

上記のコードには登場しませんでしたが、関数は返り値(return)を返す事ができます。

```php:qiita.php
function sayHello($friend){               //(1)
  $message = "Hello," . $friend . ".";    //(2)
  return $message;                        //(3)
}
$message = sayHello('Haru');              //(4)
echo $message;                            //(5)

//出力結果
//Hello,Haru.
```

(1)でsayHello関数に仮引数である`$friend`を指定しています。
(2)で処理内容を記述してます。
(3)で`$message`を返り値に指定しています。(2)で格納された値が返されると言う事です。
(4)で返り値を受け取り、変数に格納します。実引数は'Haru'と指定しました。実引数に指定するのは変数でも文字列でも目的に合わせて指定できます。（仮引数は変数のみでした）。
※この時、関数内の`$message`と返り値を格納している関数の外の`$message`は全くの別人です。前述の「スコープ」の違いがありますね。
(5)で変数に格納した返り値を出力しています。

まとめると、
(1)〜(3)でsayHello関数は、「返り値(return値)」を返しています。。
呼び出し側である(4)では、関数の返り値を受け取っています。

関数の「スコープ」はグローバルですので、例えば、

```php:qiita.php
$message = sayHello('Haru');              //(4)

function sayHello($friend){               //(1)
  $message = "Hello," . $friend . ".";    //(2)
  return $message;                        //(3)
}
echo $message;                            //(5)
```

などと記述の順番が入れ替わっても、値を受け取る事ができます。

####引数名と変数名

さて、私が理解が浅く混乱していたのがこの変数名と引数名のところです。。。

再び、こちらのコードが登場です。

```php:qiita.php                          
function sayHello($friend){               //(1)
  $message = "Hello," . $friend . ".";    //(2)
  return $message;                        //(3)
}
$message = sayHello('Haru');              //(4)
echo $message;                            //(5)
```

`$friend`が仮引数です。
(1)のsayHello〜の記述から、「関数内」と言う事になります、
なので、仮引数と(２)の`$friend`変数名は必ず合わせる必要があります。
違う変数名を使えば、当たり前ですが違う変数として扱われます。

それに対して、「関数の外（すなわち関数の呼び出し側）の変数名」と「仮引数名」は合わせる必要はありません。

```php:qiita.php
function sayHello($friend){               //(1)
  $message = "Hello," . $friend . ".";    //(2)
  return $message;                        //(3)
}
$name = 'Haru';                           //※(3)
$message = sayHello($name);               //(4)
echo $message;                            //(5)

//出力結果　//Hello,Haru.
```
別に同じにしても結果は変わりません。

```php:qiita.php
function sayHello($friend){               //(1)
  $message = "Hello," . $friend . ".";    //(2)
  return $message;                        //(3)
}
$friend = 'Haru';                         //(※３）
$message = sayHello($friend);             //(4)
echo $message;                            //(5)
```
実引数には、仮引数と違う変数名を指定してあげても、文字列を指定してあげても大丈夫という事でした。

以上、長くなりましたが、私が初めてユーザー関数を使ったときに混乱したことをまとめてみました。

同じように初めて使う人の役に立てばと思って書きましたが、何か間違いやご指摘事項あればご教授いただけると力になります。