307
333

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ざっくりPHPの基礎

Last updated at Posted at 2013-12-13

書いてる人

プログラミング学習サービスやら、ペットサロン予約サービス、風俗検索サービスなど色々とやっている「かずきち」です。
■運営サービス一部
http://crazy-wp.com/
http://webukatu.com/
新宿のホストから不動産・保険の営業を経て、HTMLって何?という状態から3ヶ月独学でプログラミングやデザインを学び、IT業界で1年間実務経験を積んで年収は1本超え。現在は起業家としてサービス運営やら不動産運営をしています。
Qiita内にそれ系の記事も書いてます。
エンジニアで稼ぐために大切な13のコト
WEBサービスで起業したい人に読んで欲しい18のコト

#基本的な仕組みに関して

<?php
 //処理内容
?>

で1ブロック。複数ブロックがあっても全て繋がってるのでブロックをまたいで処理可能
※「;」で終わるまでは改行や空白・スペースは無視される
※1つのファイルにphpのみ記述する(htmlなどない)なら最後の「?>」は省略してもいい

##phpのコンパイルと実行
phpプログラムは「レキサー」と呼ばれるものでphpプログラムを解析し、構文をばらばらにのトークンへ分解する。
そして、「パーサー」と呼ばれるものがトークンをオペコードへ変換。
そのオペコードが実行される。

##echoの仕組み
echoは評価値を文字列型へキャストしてから出力している
(なので、falseなど論理値は表示されない)(小数1.0は1になる)
※小数点も表示したいならprintfを使う

##issetとemptyの違い
emptyは、0、”"、nullがセットされていても、falseと評価。
issetは、nullの場合にfalseと評価

例)変数に0が入っていた場合
emptyだと変数の値が0だと空だと判断される。でもissetだとセットされていると判断される。

#エラー関係
エラーは大まかに3種類。
1.コンパイル出来ない(実行以前で止まる) ➡ シンタックス(パース)エラー(syntax error)
2.実行が停止するもの ➡ フェイタルエラー(fatal error)
3.実行は継続されるが警告のでるもの。 ➡ 警告(warning)、注意(notice)

1は文法の間違いなどでコンパイル出来ないなど。
2は定義してない関数を呼び出そうとした場合など。
3は初期化してない変数をいきなり使ったりなど。

###エラーの重要度高い順
E_PARSE
E_ERROR
E_WARNING
E_NOTICE
E_DEPRECATED

E_DEPRECATEDはコーディング規約に関しての問題や将来的に廃止になる(非推奨)機能が使われている場合に発生。

##ログを取る
ログというのはプログラムがどこまで動いたかを記録したもの。
エラーになった時にどこでエラーになっているのか判断しやすくなる。

<?php
error_reporting(E_ALL); //E_STRICTレベル以外のエラーを報告する
ini_set('display_errors','On'); //画面にエラーを表示させるか
ini_set('log_errors','On'); //ログを取るか
ini_set('error_log','php.log'); //ログの出力ファイルを指定
?>

##@「アットマーク」でエラー非表示
phpでの@(アットマーク)はエラー制御演算子で、関数や変数の前につける。
これを付けた関数や変数でエラーを出力しないようにする。

#変数に関して
変数名の前に「$」をつけて宣言する。変数名の大文字小文字は区別される。

$a = 1;

##可変変数
変数の中に変数がある感じのもの。

<?php
$a = 1;
$b = 'a';
echo $$b;

※$bが評価されて中に文字列「a」が入ってるので、「$a」になってまたそれが評価されて中身の「1」が表示される。

##変数のスコープ
javaでいうところの変数の前につくprotectedやprivateみたいなもの。
変数が参照(使える)範囲のこと。
###グローバルスコープ

のブロック中に書かれたものは別のブロックやファイルでも使える。

###ローカルスコープ
関数やクラスのメソッド内で宣言されたものはその中だけしか使えない。

###スーパーグローバル変数
どこからでも使える変数。PHP実行時に自動的に定義される。
GETやPOSTなどもこのスーパーグローバル変数。

##定数

define('MOMO','甘い');

甘いという文字列が入ったMOMOという名前の定数を定義してる。
※定義可能なのは整数、小数、文字列のみ。配列やオブジェクトはダメ。
※定数名は大文字、小文字区別される。

##型
phpには8つの型がある。メソッドによって自動でキャストされたりする(動的型付け言語)ので、どのタイミングでどうなるのかを把握しとく必要がある。

###自動キャスト
・フォームから入力されてきた値は全て「文字列」なので注意!!
・if文などで比較する時も「$value = 1」とすると文字列の中身を整数へ自動キャストして比較している!!
・比較する時に数値っぽい文字列は数値へ自動キャストされる。
※"012345"と"12345"を「==」で比較すると「同じ」とみなされる

###ヒアドキュメント
長い文字列を""や''で変数に格納するのは面倒なので、そういう時に使える。
「変数 = <<<EOI」の後に文字を入れて、「EOI;」で閉じる。
文中に「{}」をつけて変数を書けば展開される。

<?php
$a = 1;
$value = <<<EOI
あいうえお
かきくけこ {$a}
さしすせそ。
EOI;
?>

※「EOI」じゃなくてもなんでもいい
※閉じる時の任意の文字列(この場合は「EOI;」)のある行には他の文字や空白などが含まれてるとエラーになる

###論理型
BOOLEAN。真偽値を扱うもの。
if文などで論理型と論理型以外を比較すると自動的に論理型へキャストされて比較される。
整数0や空文字''、文字列の0、要素数が0の配列、nullは「false」と同じ。

###null
nullが代入されてるもの、値が何もはいってないもの、unset()されてるものは「null」
nullが入っている変数は「定義された変数」とみなされる。

###文字列
「.」で連結できる

$a = 'aiu';
$b = 'eo';
$c = $a.$b;

#if文以外の条件式記述方法
if文を書くより分量少なくてすむ。

$value = isset($a) ? $a : 'none';

※もし、変数aが空じゃなければ変数aを代入。空なら文字列noneを代入する。

###ページを移動する
headerファンクションはhtmlが出力される前に実行しないとダメ。

ページを移動する
header('Location:移動先のURL');

###他のphpファイルを読み込む

//一度きりの読み込みなら
<?php include_once('check.php'); ?>

//何度も読み込めるもの
<?php include('check.php'); ?>

その他に

//一度きりの読み込みなら
<?php require_once('check.php'); ?>

//何度も読み込めるもの
<?php require('check.php'); ?>

もある。違いは、requireの場合は読み込みエラーになったら実行停止するが、includeは警告のみ。

check.phpを1回だけ読み込ませる。
なので、各ページで同じ処理をする場合は1つのPHPファイルにその処理を書いて、
各ページでそのファイルを読み込む様にすればいい。

<?php require('check.php'); ?>

でもいい。違いはよく分からない。

#配列
phpの配列はハッシュやマップなどの区別はなく、全部「連想配列」。
どんな型の値でも入れられる。
キーが重複してた場合は上書きされる。

###配列の初期化

$arr[];

ブラケット「[]」をつけると、その変数は「配列」として初期化される。

###色々な配列の書き方

1.
$array1[0] = "あいうえお"; 
$array1[1] = "かきくけこ"; 
$array1[2] = "さしすせそ"; 

2.
$array2[] = "あいうえお"; 
$array2[] = "かきくけこ"; 
$array2[] = "さしすせそ"; 

3.
$array3 = array("あいうえお", "かきくけこ", "さしすせそ"); 

4.
$array4["a"]  = "あいうえお"; 
$array4["ka"] = "かきくけこ"; 
$array4["sa"] = "さしすせそ"; 

5.
$array5 = array( 
                "a" => "あいうえお",  
                "ka" => "かきくけこ",  
                "sa" => "さしすせそ" 
               ); 

###配列への値の追加・削除

追加
$value = array('a','b',7);
//もしくは
&value[] = 'd';
配列の末尾に追加

array_push($value, 'a');
配列の末尾を削除

array_pop($value);

###連想配列を使う

連想配列として追加

$items = array('ケーキ'=>'あまい', 'レモン'=>'すっぱい');
連想配列の値を取り出す

$items['ケーキ'];
//「あまい」が取り出される
連想配列の値を全部取り出す

foreach($items as $key => $value {
 printf($key.'は'.$value.'です');
}

#時刻を取得する

print(date(G)..date(i)..date(s).);

またはもっと簡単にするなら

print(現在は.date(G時 i分 s秒).です);

という感じで、
dateの中にパラメータを「G」とか「M」とか入れれば、「年」や「月」が取得できる。

※Warningが表示された場合
PHP5.1.0から「タイムゾーン」を設定する必要が有るため。Dateを使う前に

を追加すればOK。

##時刻を日本時間に変更する
php.iniの設定を以下に変える

date.timezone = Asia/Tokyo

#フォーム関係
##フォームの値を取得する

###複数選択のチェックボックスやリストボックスの値を取得
値は2次元配列で入っている。

<input type=”checkbox” name=”reserve[]” value=”男”>
<input type=”checkbox” name=”reserve[]” value=”女”>

という形で、name属性を配列にして、下のように2次元配列の形で取得する。

複数選択のボックスの値を取得
$_POST['reserve'][0]
$_POST['reserve'][1]

##フォームに入力された値を表示する時のセキュリティ対策
フォームからjsなどのスクリプトをそのまま送られると実行されてしまうので、回避する。
ラジオボタンやチェックボックスにも使うこと!

htmlspecialcharsファンクション
$value = htmlspecialchars($_POST['name属性の名前', ENT_QUOTES,'UTF-8');

「ENT_QUOTES」はシングルクウォーテーションも変換して回避するよ。というもの。
定数なので、ただの「3」でもいい。

##フォームの値をチェック、変換する
###半角の「数字」かチェックする

半角数字かチェックする
if(is_numeric($age)){
 //処理
}

###全角英数字を半角英数字へ変換する

全角英数字を半角英数字へ変換する
mb_convert_kana($_POST['age'],'a','UTF-8');

「a」だと半角英数字へ。「n」にすると全角数字➡半角数字へ変換する。
他にも「」に何を入れるかで変換の仕方が違うので調べてみてね。

###フォームに入力された内容が正しい形式か、正規表現を使ってチェックする

例)郵便番号を正規表現を使ってチェックする
if(preg_match("/|A|d{3}¥-¥d{4}¥z/", $zip)){
 //処理
}else{
 $error['zip'] = "郵便番号の形式が違います。"; 
}

##チェックボックス・ラジオボタンやリストボックスの値を記憶しておく

<select name="prefecture" id="prefecture">
	<option value="">都道府県の選択</option>
	<?php foreach($prf as $key => $value){ ?>
	<option value="<?php print($value); ?>" <?php if (isset($_POST['prefecture']) && $_POST['prefecture'] == $value){ print " selected"; }?>><?php print($value); ?></option>
	<?php } ?>
</select></td>

という感じで、
もしPOSTに値が入っていて、かつ、その値がボックスのvalueと同じなら属性にselectedをつける。
とやればいい。
※チェックボックスやラジオボタンはselectedのかわりに「checked」をつければいい。

#次ページへ値を渡す
###aタグで渡す

<a href="hoge.php?text=".urlencode('テスト')." />

###headerファンクションで渡す

$url = 'negotiate.php?food_id='.$_GET['food_id'];
header("Location:$url");

※headerファンクションが実行されるまでに文字列などが出力されてしまっていると実行されない。
「Location : $url」という形で間空けてあると実行されないので注意!!

#Cookie(クッキー)を使う

###Cookieを使ってフォームに入力された値を記憶しておく
クッキーは時間指定をしてあげて、その時間が経ったらブラウザ側で破棄される仕組み。
1週間自動ログイン機能などに使える。
【処理順序】
1. まず、クッキーがあるかどうか確認
2. なければ、あげる(作る)
3. あれば、読み込む

Cookieを使ってフォーム内容を記憶
//ログインフォーム.php
if(isset($_COOKIE['名前'])){
 $value = $_COOKIE['名前'];
}else{

}
<input type="checkbox" name="save" value="on" /><label for="save">週間ログインしたままにする</label>

//ログイン後フォーム.php
if($save == 'on'){
 setcookie('クッキー名',保存する内容,破棄までの時間time()+60*60*24*7);
}else{
//空の値を入れる事でcookieを破棄する
setcookie('クッキー名','');
}

「setcookie」はヘッダーが送信される前に渡さないとエラーになるので注意。
timeは60秒×60分×24時間×7日で1週間になります。

#セッション(SESSION)を使う
セッションはブラウザが閉じられるまでの間だけ保存できる仕組み。
会員登録時のフォームデータ記憶などに使える。
クッキーを使って、IDを受け渡して、次回訪問時にそのIDを元にデータを引っ張ってくる。
クッキーがOFFになってると使えない!!

1.セッションスタートする(session_start();)
2.セッション(例えば、$_SESSION[‘counter’])があるかどうか(issetで調べる)
3.なければ、セッションを作る($_SESSION[‘counter’]=0などとして)
4.セッションが必要なければ削除する。(session_unset();)

※毎ページでセッションスタートさせてデータ取り出しする必要があるので、セッションスタートを省略したいならphp.iniファイルを書き換える。
※違うPCでセッションIDが読み取られれば、そのIDを使ってデータ取得出来てしまうので、
 他にもIPアドレスを調べたり、ブラウザ種類でマッチさせたりと確認して認証する必要が有る。

#データベースへのアクセス
PHPではMySQLデータベースへのアクセスには、PEAR DB、 PEAR MDB 、PEAR MDB2 、MySQL拡張モジュール、mySQLi 、PDO など色々な方法がある。

##PDOを使ったMySQL
pdoはjavaのやつみたいにDBがポスグレだったりmysqlだったりしても同じ形式で書けば、変換してクエリーを出してくれる仕組み。
Windowsで使うにはphp.iniの設定が必要。
Pdoを使うにはpdoクラスのオブジェクトを作成して使う。

##dbへの接続

$dsn = 'mysql:dbname=test;host=localhost;charset=utf8';
$user = 'root';
$password = '';
$options = array(
        // SQL実行失敗時に例外をスロー
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        // デフォルトフェッチモードを連想配列形式に設定
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        // バッファードクエリを使う(一度に結果セットをすべて取得し、サーバー負荷を軽減)
        // SELECTで得た結果に対してもrowCountメソッドを使えるようにする
        PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
    );
// PDOオブジェクト生成
$dbh = new PDO($dsn, $user, $password, $options);

※文字コードは「UTF-8」じゃなくて「utf8」だから注意!!

##SQL文(クエリー)作成

$stmt = $db->prepare('INSERT INTO message (name,body) VALUES (:name,:comment)');

##プレースホルダに値をセット

$stmt->bindParam(':name', $name, PDO::PARAM_STR);
$stmt->bindParam(':comment', $comment, PDO::PARAM_STR);

##SQL文実行

$stmt->execute(array($username,$message));

##結果を1件ずつ処理

while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
   //処理内容
}

##dbを閉じる
PHPスクリプトが終了すればデータベースへの接続は閉じられますが、明示的に閉じる場合には作成したPDOクラスのオブジェクトに"NULL"を代入します。

dbを閉じる
$dbh = null;

##SQL文書いて処理を行う
1回だけ使用するようなSQL文をデータベースへ送信するにはPDOクラスで用意されている"query"メソッドを使います。

1回だけ使う場合のSQL文
$sql = 'select id, name from shouhin';
$stmt = $dbh->query($sql);

##preparedstatementを使ったSQL文
繰り返し使いそうなSQL文はプリペアードステートメントというSQL文のひな形をまず作ってから、
値を入れて実行する。
値の入れ方は2通り。「?」を使うものと「:name」などの名前つきパラメータを使う方法がある。

?を使う場合
$sql = 'select id, name from mypdo where id > ? AND id < ?';
$stmt = $dbh->prepare($sql);
$stmt->execute(array(2, 4));
名前つきパラメータを使う場合
$sql = 'select id, name from mypdo where id > :kagen AND id < :jyougen';
$stmt = $dbh->prepare($sql);
$stmt->execute(array(':kagen'=>2, ':jyougen'=>4));

##SELECTで結果を取得する
PDO::FETCH_NUMを指定した場合は下記のようになります。

$sql = 'select id, name from shouhin';
$stmt = $dbh->query($sql);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
print($result[0]);
print($result[1]);

PDO::FETCH_ASSOCを指定した場合は下記のようになります。

$sql = 'select id, name from shouhin';
$stmt = $dbh->query($sql);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
print($result['id']);
print($result['name']);

PDO::FETCH_BOTHを指定した場合(又は引数を省略した場合)は、どちらの形式でも利用が可能です。

##取得したデータをフェッチしてループで使う

<?php while($table2 = $stmt2->fetch(PDO::FETCH_ASSOC)){ ?>

内容

<?php } ?>

##INSERTで挿入したデータのIDを取得する

$id = $stmt->lastInsertId();

##ページング機能をつける
SELECTのクエリーの最後に「LIMIT 10」という形でつけると、1ページに表示される件数が10件になる。

ページング
<?php
$page = $_GET['page'];
//最初のページで10件表示させるため
if($page == ''){
  $page = 1;
}
$page = max($page, 1);  //maxはどっちか大きい方を返すファンクション。この場合1より小さければ1が返る。
//最終ページを取得する
$sql = 'SELECT COUNT(*) AS cnt FROM テーブル名';
$stmt = $dbh->prepare($sql);
$table = mysql_fetch_assoc($stmt);
$maxPage = ceil($table['cnt'] / 10);
$page = min($page, $maxPage);  //端数が出ても切り上げさせる。2.4ページ➡3ページになる。

$start = ($page -1) * 10;
$sql = 'SELECT * FROM テーブル名 ORDER BY date LIMIT'.$start.',10';

?>

//html部分
//1ページ目なら前ページへは出さず、maxページなら次へページは出さないようにする
<?php if($page > 1){ ?>
<a href="現在のページパス?page=<?php print($page -1); ?>">前のページへ</a>
<?php } ?>
<?php if($page < $maxPage){ ?>
<a href="現在のページパス?page=<?php print($page +1); ?>">次のページへ</a>
<?php } ?>

LIMITが「1,5」「5,5」「10,5」と増えていって表示する件数を指定できる。

#文字列操作
##文字を切り取る・切り抜く・抜き出す

$text = 'abcdef';
$result = substr($text, -2);    // "ef" を返す
$result = substr($text, -3, 1); // "d" を返す

「-(マイナス)」で指定すれば、後ろから何文字を抜き出す。
引数を二つ指定すれば、「何文字目から」「何文字分を」抜き出す。

##文字を丸める(一定文字以上は...にして省略するとか)

mb_strimwidth($a,0,10,'...','UTF-8')

※変数aの中の文字列を0から10まで(日本語は2つで1文字)表示し、それ以上のものは..にする。
文字化けする場合は、最後に文字コードをつける。
※Mysqlと違って’UTF-8’と書くので注意!!(''を忘れないこと。大文字で書くこと。)

#ファイル操作
###ファイル読み込み

$data = file_get_contents("読み込むファイルパス");
print($data); //そのままファイルの中身が見れる

###読み込みと表示を同時に行う場合

readfile("パス");

###ファイルに追記

$data = file_get_contents('読み込むファイルパス'); //getでファイル読み込んで
$data .= '内容'; //dataに追記して
file_put_contents('書き込むファイルパス',$data); //putして保存する

###RSSやWebAPIなどで使われているXMLファイルを読み込む
※PHP5から使えるようになったファンクション

$xml = simplexml_load_file('読み込むxmlファイルのURL');
//今回の場合、ファイル内の入れ子になった要素を指定してる
foreach($xml -> channnel -> item as $item){
//処理内容
};

###ファイル書き込み

$success = file_put_contents('書き込むパス','書き込む内容');

※パスワードなど見られたら困るファイルはドキュメントルート(httpdocs,htdocsなど)の外に設置すること。
(URL打ち込むと見れちゃうので)

##画像ファイルなどのアップロード
###アップロードされた画像の受信

//1.フォームを用意(methodは必ずpost。enctypeは下記のようにする)
<form action="" method="post" enctype="multipart/form-data">

//2.フォームから送られたファイルを受信
$file = $_FILES['my_img'];

//3.受信したファイルからいろいろな情報が取れるので、いろいろやる
$file['name'] //ファイル名
$file['type'] //ファイルタイプ(拡張子) ※アバウトなので拡張子判定には使わない方がいい
$file['tmp_name'] //アップロードした一時保管ファイル場所
$file['error'] //エラー内容
$file['size'] //ファイルサイズ

//4.自分のサーバへ送る
$filepath = '送り先ファイルパス';
move_uploaded_file($file['tmp_name'],'/picture/'.$filepath); //移動元のファイルパスと移動先パス
//上のファンクション結果はboolean値で返ってくる

###画像ファイルの移動
フォーム画面から確認画面へ移る際、指定された画像を一時保管からサーバへ移すが、
確認画面から「戻る」を押した場合、ファイルが残ってしまうので、
いったん、一時保管からサーバ上の一時保管へ移し、登録完了時にサーバ一時保管からサーバの画像フォルダへ移動し、
戻るボタンを押した際には、サーバの一時保管ファイル内画像を消去する時などに使える。

rename("/tmp/file.txt", "/home/hoge/dir/file.txt");

移動元パスと移動先パスを入れる。

ユーザ登録画面
$("#form_email").keyup(function(){
			var email = $('#form_email').val();
		    $.ajax({
		      type: "POST",
		      url: "json.php",
		      data: 'email='+email,
		      //成功時のコールバック
		      success: function (data, textStatus, xhr) {
		        if(data[0].count == 0){
		        	$("#email_rst").text('OK');
		        }else{
			        $("#email_rst").text('NG');
		        }
		      }
		    });
		});

#カレンダー作成

			                          		
<?php
 
// 現在の年月を取得
$year = date('Y');
$month = date('n');
 
// 月末日を取得
$last_day = date('j', mktime(0, 0, 0, $month + 1, 0, $year));
 
$calendar = array();
$j = 0;
 
// 月末日までループ
for ($i = 1; $i < $last_day + 1; $i++) {
 
    // 曜日を取得
    $week = date('w', mktime(0, 0, 0, $month, $i, $year));
 
    // 1日の場合
    if ($i == 1) {
 
        // 1日目の曜日までをループ
        for ($s = 1; $s <= $week; $s++) {
 
            // 前半に空文字をセット
            $calendar[$j]['day'] = '';
            $j++;
 
        }
 
    }
 
    // 配列に日付をセット
    $calendar[$j]['day'] = $i;
    $j++;
 
    // 月末日の場合
    if ($i == $last_day) {
 
        // 月末日から残りをループ
        for ($e = 1; $e <= 6 - $week; $e++) {
 
            // 後半に空文字をセット
            $calendar[$j]['day'] = '';
            $j++;
 
        }
 
    }
 
}
 
?>

<?php echo $year; ?>年<?php echo $month; ?>月のスケジュール
<br>
<br>
<table class="calender">
    <tr class="week">
        <th class="sunday">日</th>
        <th>月</th>
        <th>火</th>
        <th>水</th>
        <th>木</th>
        <th>金</th>
        <th class="saturday">土</th>
    </tr>
 
    <tr>
    <?php $today = date('j'); ?>
    <?php $cnt = 0; ?>
    <?php foreach ($calendar as $key => $value): ?>
				
        <td <?php if($value['day'] == $today) echo 'class="today"'; ?>>
	        <?php $cnt++; ?>
					<?php if(!empty($value['day'])): ?>
					<a href="">
		        <span class="day"><?php echo $value['day']; ?></span>
					</a>
					<?php endif; ?>
        </td>
 
    <?php if ($cnt == 7): ?>
    </tr>
    <tr>
    <?php $cnt = 0; ?>
    <?php endif; ?>
 
    <?php endforeach; ?>
    </tr>
</table>

#WEBプログラミングから起業までを一貫して学べるオンライン動画総合学習サービス『ウェブカツ!!』
様々なプログラミング学習サイトやサービスでは正直な所、自分でWEBサービスを作れるようにはならないため、「自分でWEBサービスを作れるようになる!」をゴールとしたオンライン動画総合学習サービス『ウェブカツ!!』を立ち上げました。興味ある方は登録よろしくお願いいたします。
http://webukatu.com/

307
333
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
307
333

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?