この記事 is 何
RubyエンジニアがPHPを勉強し始めるときに見る、PHPの入門記事です。
動機
今までRubyをやってきていて、PHPを使うことになったというケースは少なくないと思っています。
特に実務未経験の方々では、このケースが多いのではないでしょうか?
というのは、実務未経験が学習している言語は、Rubyがファーストチョイスである一方で、PHPを使う企業は少なくないためです。
ちなみに、僕もそのケースでした。
そういう方々が過度に不安を持ったりせず、スムーズに転向できるようにしたいと思い、この記事を書きました。
この記事の主なターゲット
- 今までRubyを勉強してたが、PHPを使うことになった人
- というか半年前の自分
この記事で書くつもりのないこと
- 技術選定時・初学者が学ぶ言語としてRuby or PHPはどっちが良いのかということ
- 網羅的・辞書的にRubyとPHPを比較した結果
- RubyとPHPの文法の正確な対応関係
学習のし始め
RubyからPHPへの転向コスト
Rubyをちゃんと習得できた方であれば、転向・学習コストにおいて問題はほぼ起きないと思います。
文法を調べながらとりあえず書き始められる、というレベルに達するには半日〜3日程度の勉強で済むと思います。
文法を調べるときに何を使うか
php.net
PHPは公式ドキュメントが神なので、基本的にこのサイトを見ればPHPの文法的な解決してくれるはずです。
ググラビリティも良好で、「array_merge php」などと検索すれば公式ドキュメントが先頭に出てくれます。
本題
$ php -v
PHP 7.3.11 ...
$ ruby -v
ruby 2.6.3p62 ...
基本的なオブジェクトの概念
出力
Ruby | PHP | |
---|---|---|
改行なし出力 | echo | |
型情報付き出力 | p | var_dump() |
配列に対し改行付き出力 | puts | echo, PHP_EOL |
Rubyのprint
は、PHPでいうとecho
に近いです。
Rubyのp
は、PHPでいうとvar_dump
に近いです。
Rubyのputs
は?と言われると、対応したものはありません。
echo
とPHP_EOL
を組み合わせる必要があります。
print 'sample!'
# sample!
sample_array = [1, 'sample']
print sample_array
# [1, "sample"]
p sample_array
# [1, "sample"]
puts sample_array
# 1
# sample
<?php
echo 'sample!';
// sample!
$sample_array = [1, 'sample'];
echo $sample_array;
// Array
var_dump($sample_array);
// array(2) {
// [0]=>
// int(1)
// [1]=>
// string(6) "sample"
// }
foreach ($sample_array as $value) {
echo $value . PHP_EOL;
}
// 1
// sample
プリミティブ型のクラス
Ruby | PHP | |
---|---|---|
プリミティブ型は | クラス | ただの型 |
文字列への変換 | to_s | "{$変数}" |
数値への変換 | to_i | intval() |
RubyではString
, Integer
, Array
といったプリミティブ型も全てがオブジェクトです。
一方、PHPにはプリミティブ型においてはクラスの概念はありません。
PHPではstring
への変換は、ダブルクウォートで囲った上で変数展開することによって実現します。
また、int
に変換するにはintval()
を使います。
int3 = 3
string3 = int3.to_s
p string3
# "3"
p string3.class
# String
re_int3 = string3.to_i
p re_int3
# 3
p re_int3.class
# Integer
<?php
$int3 = 3;
$string3 = "{$int3}";
var_dump($string3);
// string(1) "3"
get_class($string3);
// Warning: get_class() expects parameter 1 to be object, string given in ...
$re_int3 = intval($string3);
var_dump($re_int3);
// int(3)
get_class($re_int3);
// Warning: get_class() expects parameter 1 to be object, int given in ...
値・オブジェクトに対する基本的な操作・変換
Ruby | PHP | |
---|---|---|
基本的な操作・変換は | オブジェクトメソッド | 標準関数の引数に渡す |
文字数を調べる | length / size | mb_strlen() |
配列を合計する | sum | array_sum() |
Rubyでは値・オブジェクトに対する基本的な操作・変換はオブジェクトメソッドで実現します。
参考: class Object(Ruby 2.7.0 リファレンスマニュアル)
一方、PHPは標準関数が充実しており、その標準関数の引数として対象を渡すのが一般的です。
address = '千代田区千代田1-1'
puts address.length
# 10
fibonacci_numbers = [1, 1, 2, 3, 5, 8, 13]
puts fibonacci_numbers.sum
# 33
<?php
$address = '千代田区千代田1-1';
echo mb_strlen($address);
// 10
$fibonacci_numbers = [1, 1, 2, 3, 5, 8, 13];
echo array_sum($fibonacci_numbers);
// 33
配列・ハッシュと、連想配列
Ruby | PHP | |
---|---|---|
配列の概念 | 配列 | 連想配列 |
連想配列の概念 | ハッシュ | 連想配列 |
配列の要素の取得 | 配列[インデックス] | 連想配列[インデックス] |
連想配列の要素の取得 | 配列[シンボル] | 連想配列[キー] |
Rubyでは配列と連想配列(ハッシュ)を使い分けます。
一方で、PHPはちょっと特殊で、連想配列しかありません。PHPにおける(連想配列でない)配列は、キーが数値である連想配列です。
また、Rubyでは連想配列のキーにはシンボルを使うことが多いと思いますが、PHPでは文字列をキーとすることが多いです。
days_of_week = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
p days_of_week
# ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
puts days_of_week[0]
# Monday
fruits = {red: 'apple', purple: 'grape', yellow: 'lemon'}
p fruits
# {:red=>"apple", :purple=>"grape", :yellow=>"lemon"}
puts fruits[:red]
# apple
<?php
$days_of_week = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
var_dump($days_of_week);
// array(7) {
// [0]=>
// string(6) "Monday"
// [1]=>
// string(7) "Tuesday"
// [2]=>
// string(9) "Wednesday"
// [3]=>
// string(8) "Thursday"
// [4]=>
// string(6) "Friday"
// [5]=>
// string(8) "Saturday"
// [6]=>
// string(6) "Sunday"
// }
echo $days_of_week[0];
// Monday
$fruits = ['red' => 'apple', 'purple' => 'grape', 'yellow' => 'lemon'];
var_dump($fruits);
// array(3) {
// ["red"]=>
// string(5) "apple"
// ["purple"]=>
// string(5) "grape"
// ["yellow"]=>
// string(5) "lemon"
//}
echo $fruits['red'];
// apple
配列への操作
Ruby | PHP | |
---|---|---|
配列への追加 | 配列 << 新要素 | 配列[] = 新要素 |
連想配列への追加 | 配列[シンボル] = 新要素 | 配列[キー] = 新要素 |
繰り返し処理 | each | foreach |
繰り返し処理(返り値使用) | map | array_map() |
繰り返し処理内での行いたい処理 | ブロック | コールバック関数 |
配列への追加は、Rubyは<<
によってできる一方、PHPでは配列[] = 新要素
という書き方を使います。
連想配列への追加は、書き方的にほぼ違いは無いかと思います。
手続き的な繰り返し処理は、Rubyならeach
, PHPならforeach
です。
返り値を使用したいときはRubyならmap
, PHPならarray_map()
を使います。
繰り返し処理中に行いたい処理を表現するのは、Rubyならブロック、PHPならコールバック関数を使います。
array_map()
はよく使う割にはちょっと複雑な見た目をしていますね。
sequence_numbers = [1, 2, 3, 4, 5]
sequence_numbers << 6
p sequence_numbers
# [1, 2, 3, 4, 5, 6]
fruits = {red: 'apple', purple: 'grape', yellow: 'lemon'}
fruits[:orange] = 'pumpkin'
p fruits
# {:red=>"apple", :purple=>"grape", :yellow=>"lemon", :orange=>"pumpkin"}
sequence_numbers.each do |number|
multiple_of_3 = number * 3
puts multiple_of_3
end
# 3
# 6
# 9
# 12
# 15
# 18
multiples = sequence_numbers.map do |number|
number * 3
end
p multiples
# [3, 6, 9, 12, 15, 18]
<?php
$sequence_numbers = [1, 2, 3, 4, 5];
$sequence_numbers[] = 6;
var_dump($sequence_numbers);
// array(6) {
// [0]=>
// int(1)
// [1]=>
// int(2)
// [2]=>
// int(3)
// [3]=>
// int(4)
// [4]=>
// int(5)
// [5]=>
// int(6)
// }
$fruits = ['red' => 'apple', 'purple' => 'grape', 'yellow' => 'lemon'];
$fruits['orange'] = 'pumpkin';
var_dump($fruits);
// array(4) {
// ["red"]=>
// string(5) "apple"
// ["purple"]=>
// string(5) "grape"
// ["yellow"]=>
// string(5) "lemon"
// ["orange"]=>
// string(7) "pumpkin"
// }
foreach ($sequence_numbers as $number) {
$multiple_of_3 = $number * 3;
echo $multiple_of_3 . PHP_EOL;
}
// 3
// 6
// 9
// 12
// 15
// 18
$multiples = array_map(
function ($number) {
return $number * 3;
},
$sequence_numbers
);
var_dump($multiples);
// array(6) {
// [0]=>
// int(3)
// [1]=>
// int(6)
// [2]=>
// int(9)
// [3]=>
// int(12)
// [4]=>
// int(15)
// [5]=>
// int(18)
// }
クラス関連
クラスは重要なので、重点的に解説します。
インスタンス変数=プロパティ
Ruby | PHP | |
---|---|---|
メンバ変数 | インスタンス変数 | プロパティ |
privateなメンバ変数の定義 | 不要 | private $変数; |
publicなメンバ変数の定義 | @変数に代入し、attr_accessor | public $変数; |
コンストラクタ | def initialize | function __construct() |
メンバ変数への代入 | @変数に代入 |
$this->変数 に代入 |
メンバ変数の呼び出し | . | -> |
Rubyのインスタンス変数は、PHPのプロパティに該当します。
Rubyはインスタンス変数の宣言は特に必要なく、コンストラクタで@変数
に代入してしまえば完了です。
一方、PHPだと宣言が必要で、private $変数;
のように定義します。
また、メンバ変数を公開可能にしたいのであれば、Rubyではattr_accessor
を使う一方、PHPではpublic $変数
によって定義します。
class Person
attr_accessor :name
def initialize name, age
@name = name
@age = age
end
end
person = Person.new '一郎', 10
p person
# #<Person:0x00007ff39f158750 @name="一郎", @age=10>
puts person.name
# 一郎
puts person.age
# Traceback (most recent call last):
# classes/Person.rb:16:in `<main>': undefined method `age' for #<Person:0x00007ff39f158750 @name="一郎", @age=10> (NoMethodError)
<?PHP
class Person
{
public $name;
private $age;
public function __construct($name, $age)
{
$this->name = $name;
$this->age = $age;
}
}
$person = new Person('一郎', 10);
var_dump($person);
// object(Person)#1 (2) {
// ["name"]=>
// string(6) "一郎"
// ["age":"Person":private]=>
// int(10)
// }
echo $person->name;
// 一郎
echo $person->age;
// Fatal error: Uncaught Error: Cannot access private property Person::$age in ...
クラスの定数
Ruby | PHP | |
---|---|---|
定数の定義 | 大文字 | const 大文字 |
クラス外からの定数の呼び出し | クラス::定数 | クラス::定数 |
クラス内からの定数の呼び出し | 定数 | self::定数 |
PHPだと定数はconst
を付けて宣言します。
クラス外からの呼び出しは、RubyでもPHPでも同じ書き方です。
PHPだとクラス内からの呼び出しではself::
をつけます。
class Sample
TEST_CODE = 'test_code'
def print_code
puts "コードは#{TEST_CODE}です。";
end
end
puts Sample::TEST_CODE
# test_code
sample = Sample.new
sample.print_code
# コードはtest_codeです。
<?PHP
class Sample
{
const TEST_CODE = 'test_code';
public function printCode()
{
$code = self::TEST_CODE;
echo "コードは「{$code}」です。";
}
}
echo Sample::TEST_CODE;
// test_code
$sample = new Sample();
$sample->printCode();
// コードは「test_code」です。
インスタンスメソッド
Ruby | PHP | |
---|---|---|
publicメソッドの定義方法 | ただのdef | public function() |
privateメソッドの定義方法 | privateの下でdef | private function() |
Rubyではクラス内で何も行わずにdef
をするとpublicメソッドとなり、private
の下で定義されるとprivateメソッドになります。
一方、PHPではメソッドの定義時に必ずpublic
/private
を定義します。
(本当はもっと色々種類がありますが割愛)
class Product
def initialize price, cost
@price = price
@cost = cost
end
def print_judgment
if calc_profit > 0
puts 'この商品は利益が出ます!どんどん売りましょう!';
else
puts 'この商品は利益が出ません...売るべきではないかもしれません...'
end
end
private
def calc_profit
@price - @cost
end
end
product = Product.new 120, 100
product.print_judgment
# この商品は利益が出ます!どんどん売りましょう!
product = Product.new 90, 100
product.print_judgment
# この商品は利益が出ません...売るべきではないかもしれません...
product.calc_profit
# Traceback (most recent call last):
# classes/Product.rb:30:in `<main>': private method `calc_profit' called for #<Product:0x00007fc9f19dc408 @price=120, @cost=100> (NoMethodError)
<?php
class Product
{
private $price;
private $cost;
public function __construct($price, $cost)
{
$this->price = $price;
$this->cost = $cost;
}
public function printJudgment()
{
if ($this->calcProfit() > 0) {
echo 'この商品は利益が出ます!どんどん売りましょう!';
} else {
echo 'この商品は利益が出ません...売るべきではないかもしれません...';
}
}
private function calcProfit()
{
return $this->price - $this->cost;
}
}
$product = new Product(120, 100);
$product->printJudgment();
// この商品は利益が出ます!どんどん売りましょう!
$product = new Product(90, 100);
$product->printJudgment();
// この商品は利益が出ません...売るべきではないかもしれません...
$product->calcProfit();
// Fatal error: Uncaught Error: Call to private method Product::calcProfit() from context '' in ...
クラスメソッドとstaticメソッド
Ruby | PHP | |
---|---|---|
静的メソッド | クラスメソッド | staticメソッド |
インスタンスメソッド | インスタンスメソッド | staticでないメソッド |
静的メソッドの定義方法 | self | static function() |
静的メソッドの呼び出し | クラス::メソッド | クラス::メソッド |
Rubyにおけるクラスメソッドは、PHPではstaticメソッドに該当します。
逆に、インスタンスメソッドはstaicでないメソッドということになります。
Rubyでクラスメソッドを定義するときは、class << self
の中で定義することが多いと思います。
一方で、PHPではメソッドの定義ごとにstatic
を付与することで定義します。
また、public
/private
はどんなメソッドでも付けるので、staticメソッドにもpublic
/private
を付けて定義します。
静的メソッドの呼び出しの書き方は、RubyとPHPとでほぼ差は無いかと思います。
class VertebrateAnimal
class << self
def is_vertebrate_animal group
get_vertebrate_animals.include? group
end
private
def get_vertebrate_animals
%w[fishes amphibians reptiles birds mammals]
end
end
end
is_vertebrate_animal1 = VertebrateAnimal::is_vertebrate_animal 'fishes'
puts is_vertebrate_animal1
# true
is_vertebrate_animal2 = VertebrateAnimal::is_vertebrate_animal 'insects'
puts is_vertebrate_animal2
# false
<?PHP
class VertebrateAnimal
{
public static function isVertebrateAnimal($group)
{
$vertebrate_animals = self::getVertebrateAnimals();
return in_array($group, $vertebrate_animals, true);
}
private static function getVertebrateAnimals()
{
return ['fishes', 'amphibians', 'reptiles', 'birds', 'mammals'];
}
}
$is_vertebrate_animal1 = VertebrateAnimal::isVertebrateAnimal('fishes');
var_dump($is_vertebrate_animal1);
// bool(true)
$is_vertebrate_animal2 = VertebrateAnimal::isVertebrateAnimal('insects');
var_dump($is_vertebrate_animal2);
// bool(false)
ゲッターとセッター
Ruby | PHP | |
---|---|---|
ゲッター | attr_reader | getXX() |
セッター | attr_writer | setXX() |
Rubyにおけるattr_reader
およびattr_writer
に該当するような文法は、PHPにはありません。
よってgetXX
, setXX
という命名で自作することになります。
class Capsule
attr_reader :element
attr_writer :element
def initialize element
@element = element
end
end
capsule = Capsule.new '有効成分'
puts capsule.element
# 有効成分
p capsule
# #<Capsule:0x00007f8d909ac660 @element="有効成分">
capsule.element = '毒'
p capsule
# #<Capsule:0x00007f8d909ac660 @element="毒">
<?PHP
class Capsule
{
private $element;
public function __construct($element)
{
$this->element = $element;
}
public function getElement()
{
return $this->element;
}
public function setElement($element)
{
$this->element = $element;
}
}
$capsule = new Capsule('有効成分');
echo $capsule->getElement();
// 有効成分
var_dump($capsule);
// object(Capsule)#1 (1) {
// ["element":"Capsule":private]=>
// string(12) "有効成分"
// }
$capsule->setElement('毒');
var_dump($capsule);
// object(Capsule)#1 (1) {
// ["element":"Capsule":private]=>
// string(3) "毒"
// }
その他ハマりポイント
ライブラリの対応関係
Ruby | PHP | |
---|---|---|
ライブラリ管理 | Gem | Composer |
テスト | Rspec | PHPUnit |
Linter | Rubocop | PHP-CS-Fixerなど |
コメント | RDoc | PHPDoc |
フレームワーク | Ruby on Rails | Laravel, CakePHPなど |
有名どころのライブラリなどの対応関係のみを記載しました。
Rubyと言ったらRails、Railsと言ったらRubyという印象ですが、PHPではフレームワークは複数の選択肢があります。
シェアが高いという意味であればLaravelが近く、MVCの思想に忠実という意味ではCakePHPに近いかなという印象を持っています。
真偽判定・nil/null判定
Ruby | PHP | |
---|---|---|
== | そこそこ使う | 絶対に使わない |
Null | nil | null |
オブジェクトのNull確認 | ! | empty() |
PHPの==はあまりにもヤバいことで有名だと思いますので、比較のときには必ず===
を使うようにします。
Rubyでのnil
は、PHPだとnull
に該当します。
Rubyでは全てがオブジェクトであり、真偽値判定・nil
判定がうまく設計されていて、オブジェクトのみで直感的に真偽値判定・nil
判定をできることも多いです。
一方、PHPの場合は直感的に真偽値判定・null
判定を行うのは危険なので、型を意識して判定した方が安全です。
オブジェクトがnullや空かどうかを確認するなら、empty()
を使うのが有効です。
class User
class << self
def find_by_id id
# IDが1のときはユーザオブジェクトを返し、それ以外のときはユーザオブジェクトを返さないとします
if id == 1
User.new '最初のユーザ'
else
nil
end
end
end
def initialize name
@name = name
end
end
def print_user user
# ユーザオブジェクトが存在するかを確認する
if !user
puts 'ユーザはいません'
else
p user
end
end
user2 = User::find_by_id 2
print_user user2
# ユーザはいません
p user2
# nil
user1 = User::find_by_id 1
print_user user1
# #<User:0x00007fe70f173f88 @name="最初のユーザ">
<?php
class User
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
static function findById($id)
{
/** IDが1のときはユーザオブジェクトを返し、それ以外のときはユーザオブジェクトを返さないとします */
if ($id === 1) {
return new static('最初のユーザ');
} else {
return null;
}
}
}
function printUser($user)
{
// ユーザオブジェクトが存在するかを確認する
if (empty($user)) {
echo 'ユーザはいません';
} else {
var_dump($user);
}
}
$user2 = User::findById(2);
printUser($user2);
// ユーザはいません
var_dump($user2);
// NULL
$user1 = User::findById(1);
printUser($user1);
// object(User)#1 (1) {
// ["name":"User":private]=>
// string(18) "最初のユーザ"
// }
if文の返り値
Ruby | PHP | |
---|---|---|
if文の返り値 | 最後の値 | なし |
分岐によって値を生成する場合 | ifの返り値に入れる | 一度値を初期化し、再代入する |
PHPはif
文の返り値が返ってきません。
そのため、分岐によって値を生成させるケースでは一度値を初期化するなどしてから再代入する、みたいな方法を取る必要があります。
どうしても再代入がいやだ、イミュータブルにしたい、という場合は三項演算子という方法もあります。
ただし、三項演算子は多くの場合は処理が分かりにくくなるだけなので、非推奨です。
def print_ymd is_beginning_of_month
today = Time.now
datetime = if is_beginning_of_month
Time.new today.year, today.month, 1 # is_beginning_of_month = falseでは現在日を返す
else
today # 月初を返す
end
puts datetime.strftime "%Y年%m月%d日です"
end
print_ymd false
# 2020年12月07日です
print_ymd true
# 2020年12月01日です
<?php
function printYmd($is_beginning_of_month)
{
$today = new DateTimeImmutable();
$datetime = null;
if ($is_beginning_of_month) {
// 月初を返す
$datetime = new DateTimeImmutable($today->format('Y-m-01'));
} else {
// $is_beginning_of_month = falseでは現在日を返す
$datetime = $today;
}
echo $datetime->format('Y年m月d日です');
}
printYmd(false);
// 2020年12月07日です
printYmd(true);
// 2020年12月01日です
/**
* 別の書き方。コード量は少ないが分かりにくい
*/
function printYmd2($is_beginning_of_month)
{
$today = new DateTimeImmutable();
$datetime = $is_beginning_of_month ? new DateTimeImmutable($today->format('Y-m-01')) : $today;
echo $datetime->format('Y年m月d日です');
}
名前付き引数がない(PHP7以前)
Ruby | PHP7以前 | PHP8以後 | |
---|---|---|---|
名前付き引数 | 引数名: | なし | 引数名: |
PHP7以前では名前付き引数がありません。
よって、オプション引数と言いながらも順番を意識して代入する必要があります(辛い)。
このため、依存性注入が凄まじくやりづらいです。
PHP8には名前付き引数が導入されています。
【PHP8.0】PHPに名前付き引数が実装される
def calc_goku_scouter original_power, kaio_ken: 1, is_suppressed: false, is_super_saiyan: false
power = original_power * kaio_ken
if power > 5000 && is_suppressed
power = 5000
end
if is_super_saiyan
power += 100000000
end
power
end
puts calc_goku_scouter 85000
# 85000
puts calc_goku_scouter 85000, kaio_ken: 2
# 170000
puts calc_goku_scouter 85000, is_suppressed: true
# 5000
puts calc_goku_scouter 85000, kaio_ken: 20, is_super_saiyan: true
# 101700000
<?php
function calcGokuScouter($original_power, $kaio_ken = 1, $is_suppressed = false, $is_super_saiyan = false)
{
$power = $original_power * $kaio_ken;
if ($power > 5000 && $is_suppressed) {
$power = 5000;
}
if ($is_super_saiyan) {
$power += 100000000;
}
return $power;
}
echo calcGokuScouter(85000);
// 85000
echo calcGokuScouter(85000, 2);
// 170000
echo calcGokuScouter(85000, 1, true);
// 5000
echo calcGokuScouter(85000, 20, false, true);
// 101700000
interfaceとabstract
Ruby | PHP | |
---|---|---|
インターフェース | なし | interface |
抽象クラス | なし | abstract |
Rubyには無い概念として、PHPにはinterface
, abstract
というものがあります。
特にinterface
は重要なのですが、中々概念の理解が難しいかもしれません。
とても勉強になった記事があるので、貼っておきます。
脱PHP初心者!インターフェイスを理解しよう
abstract
はGoFのデザインパターンでいうTemplate Methodパターンを実装するのに必要です。
参考になった記事を貼っておきます。
PHPによるデザインパターン入門 - TemplateMethod〜処理を穴埋めする - Do You PHP はてブロ
結び
いかがでしたか?
もし誤記・間違い・より良い書き方などあれば、ご指摘いただければと思います。
また他にこんなこと困ったよ!みたいなのもあれば、教えて頂けると嬉しいです。
一応GitHubにもコードをまとめておきました。
https://github.com/kumackey/php-ruby
他参考になりそうなもの
PHPer向けのRuby入門
この記事の逆パターンです。演習問題が付いてる優しさ。
PHPerがRubyを勉強するのに役に立つTips
同じくこの記事の逆パターン。
元PHPエンジニアがPHPとRubyを比較してみた
PHPとRubyの文法の違いなど。
PHPからRubyへ
Rubyの公式が出している、PHPとの違いというドキュメント