20200219のPHPに関する記事は15件です。

本格的なTwitterクローンを作る

Twitterのクローンアプリを10日間で作った話

今回、Web上で「CloudDiary」という交流サイトを作りました。
デモは以下をご覧ください。

スマホ版(GIF画像)
スマホ用

PC版
PC用

このアプリについて

主な機能はTwitterに似ており、デザインはGoogleが推奨するマテリアルUIを採用しました。言語はHTML,CSS,JavaScript,PHP,Ajax(非同期でいいね)です。メインページはSPAで、フッターのナビゲーションで画面遷移しても、ページバックした際は、前のページに戻ります。

機能一覧

  • ログイン/ログアウト
  • パスワードの不可逆暗号化(解読不可能な暗号としてDBに登録)
  • 文章投稿
  • 画像投稿
  • いいね(Ajaxで非同期処理)
  • コメント機能
  • Twitter共有
  • フォロー/フォロワー
  • アイコン画像
  • プロフィール変更

主な機能の仕組み

データベースは、usersテーブルpostsテーブルlikesテーブルrepliesテーブルfollowsテーブルの5つから成り立っております。例えば何か投稿したら、usersテーブルのpost数が更新され、postsテーブルに詳細カラムが追加されます。このようにデータベースが連携し合っています。

PHP

基礎土台はこんな感じです。これを応用すれば、ユーザーごとのいいね数,コメント内容等を取り出して表示できます。

index.php
<!--postsテーブルのnameを取り出す-->
<?= foreach($posts as $row): ?>
<?= echo $row['name']; ?>
<?= endforeach; ?>

<!--セッションがあればユーザーの名前を表示-->
<?= if(isset($_SESSION['user'])): ?>
<div class= "user-name"><?php echo $_SESSION['user']['name']; ?></div>
<?= else: ?>
<div class= "user-name">User name</div>
<?= endif; ?>

Ajax

いいねボタンが押された場合、Ajaxで非同期でlikesテーブルに新情報を挿入します。メリットとしては、例えば下にスクロールしている際にいいねボタンを押した場合、ページがリロードされてトップに戻ることがなくなります。また、感覚としては瞬時にいいねボタンを押せるため、UXが向上します

JavaScript

特定のボタンが押されたら、ポップアップ表示を出したり、部品をクリックしたときに波紋を出したりしています。

おわりに

今回はPHPを使ってこのような交流サイトを作ってみました。
何かアドバイス等あれば教えていただければと思います。
そして、この記事が皆様にお役に立てれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

天気予報を取得するアレを使用してみた(Openweathermap_api)。

天気予報を取得するアレを使用してみた(Openweathermap_api)。

ご自由にお使いください。尚、openweathermap.orgより
アカウントを作成後、APIKEYを取得しお使いください。
下記のソースコードをコピーしての可変等は可能です。

余談

天気予報の絵文字が合致していていないかもしれません。
また都道府県のIDもズレている場合、お好みで変更ください。
ZIPコードで情報を取得するとより正確な天気予報となるそうです。

下記のコードを実行するとこんな感じになります。

2020-02-19_1931.png

原文:天気予報を取得するアレを使用してみた(Openweathermap_api)。

Openweathermap_api.php
<?php
class Openweathermap_api{
    public $url = "https://api.openweathermap.org/data/2.5/weather?id=";
    public $appid = "apikey";
    public $ken ="";
    public $response ="";
    public $icon = array(
        "01d"=>"☀",
        "02d"=>"⛅",
        "03d"=>"☁",
        "04d"=>"?",//☁
        "09d"=>"?",
        "010d"=>"?",
        "011d"=>"⛈",
        "013d"=>"❄",
        "050d"=>"?",
    );
    function __construct()
    {
        $this->ken = (object)(json_decode(@file_get_contents("ken.json"),true));
    }

    function api(){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $this->response =  (object)json_decode(curl_exec($ch),true);
        curl_close($ch);
    }
}

$tenki = New Openweathermap_api();
$tenki->url = "https://api.openweathermap.org/data/2.5/weather?id=".$tenki->ken->kochi["id"]."&appid=".$tenki->appid;
$tenki->api();
print($tenki->icon[str_replace("n","d",$tenki->response->weather[0]["icon"])]);
var_dump($tenki->response);

ken.json
{
    "hokkaido": {
        "id": "2130037",
        "kenmei": "北海道",
        "kenfurigana": "ほっかいどう"
    },
    "aomori": {
        "id": "2130656",
        "kenmei": "青森県",
        "kenfurigana": "あおもり"
    },
    "iwate": {
        "id": "2112518",
        "kenmei": "岩手県",
        "kenfurigana": "いわて"
    },
    "miyagi": {
        "id": "2111888",
        "kenmei": "宮城県",
        "kenfurigana": "みやぎ"
    },
    "akita": {
        "id": "2113124",
        "kenmei": "秋田県",
        "kenfurigana": "あきた"
    },
    "yamagata": {
        "id": "2110554",
        "kenmei": "山形県",
        "kenfurigana": "やまがた"
    },
    "fukushima": {
        "id": "2112923",
        "kenmei": "福島県",
        "kenfurigana": "ふくしま"
    },
    "ibaraki": {
        "id": "2112669",
        "kenmei": "茨城県",
        "kenfurigana": "いばらき"
    },
    "tochigi": {
        "id": "1850310",
        "kenmei": "栃木県",
        "kenfurigana": "とちぎ"
    },
    "gunma": {
        "id": "1863501",
        "kenmei": "群馬県",
        "kenfurigana": "ぐんま"
    },
    "saitama": {
        "id": "1853226",
        "kenmei": "埼玉県",
        "kenfurigana": "さいたま"
    },
    "chiba": {
        "id": "2113014",
        "kenmei": "千葉県",
        "kenfurigana": "ちば"
    },
    "tokyo": {
        "id": "1850144",
        "kenmei": "東京都",
        "kenfurigana": "とうきょう"
    },
    "kanagawa": {
        "id": "1860291",
        "kenmei": "神奈川県",
        "kenfurigana": "かながわ"
    },
    "niigata": {
        "id": "1855429",
        "kenmei": "新潟県",
        "kenfurigana": "にいがた"
    },
    "toyama": {
        "id": "1849872",
        "kenmei": "富山県",
        "kenfurigana": "とやま"
    },
    "ishikawa": {
        "id": "1861387",
        "kenmei": "石川県",
        "kenfurigana": "いしかわ"
    },
    "fukui": {
        "id": "1863983",
        "kenmei": "福井県",
        "kenfurigana": "ふくい"
    },
    "yamanashi": {
        "id": "1848649",
        "kenmei": "山梨県",
        "kenfurigana": "やまなし"
    },
    "nagano": {
        "id": "1856210",
        "kenmei": "長野県",
        "kenfurigana": "ながの"
    },
    "gifu": {
        "id": "1863640",
        "kenmei": "岐阜県",
        "kenfurigana": "ぎふ"
    },
    "shizuoka": {
        "id": "1851715",
        "kenmei": "静岡県",
        "kenfurigana": "しずおか"
    },
    "aichi": {
        "id": "1865694",
        "kenmei": "愛知県",
        "kenfurigana": "あいち"
    },
    "mie": {
        "id": "1857352",
        "kenmei": "三重県",
        "kenfurigana": "みえ"
    },
    "shiga": {
        "id": "1852553",
        "kenmei": "滋賀県",
        "kenfurigana": "しが"
    },
    "kyoto": {
        "id": "1857907",
        "kenmei": "京都府",
        "kenfurigana": "きょうと"
    },
    "osaka": {
        "id": "1853909",
        "kenmei": "大阪府",
        "kenfurigana": "おおさか"
    },
    "hyogo": {
        "id": "1862047",
        "kenmei": "兵庫県",
        "kenfurigana": "ひょうご"
    },
    "nara": {
        "id": "1855608",
        "kenmei": "奈良県",
        "kenfurigana": "なら"
    },
    "wakayama": {
        "id": "1848938",
        "kenmei": "和歌山県",
        "kenfurigana": "わかやま"
    },
    "tottori": {
        "id": "1849890",
        "kenmei": "鳥取県",
        "kenfurigana": "とっとり"
    },
    "shimane": {
        "id": "1852442",
        "kenmei": "島根県",
        "kenfurigana": "しまね"
    },
    "okayama": {
        "id": "1854381",
        "kenmei": "岡山県",
        "kenfurigana": "おかやま"
    },
    "hiroshima": {
        "id": "1862413",
        "kenmei": "広島県",
        "kenfurigana": "ひろしま"
    },
    "yamaguchi": {
        "id": "1848681",
        "kenmei": "山口県",
        "kenfurigana": "やまぐち"
    },
    "tokushima": {
        "id": "1850157",
        "kenmei": "徳島県",
        "kenfurigana": "とくしま"
    },
    "kagawa": {
        "id": "1860834",
        "kenmei": "香川県",
        "kenfurigana": "かがわ"
    },
    "ehime": {
        "id": "1864226",
        "kenmei": "愛媛県",
        "kenfurigana": "えひめ"
    },
    "kochi": {
        "id": "1859133",
        "kenmei": "高知県",
        "kenfurigana": "こうち"
    },
    "fukuoka": {
        "id": "1863958",
        "kenmei": "福岡県",
        "kenfurigana": "ふくおか"
    },
    "saga": {
        "id": "1853303",
        "kenmei": "佐賀県",
        "kenfurigana": "さが"
    },
    "nagasaki": {
        "id": "1856156",
        "kenmei": "長崎県",
        "kenfurigana": "ながさき"
    },
    "kumamoto": {
        "id": "1858419",
        "kenmei": "熊本県",
        "kenfurigana": "くまもと"
    },
    "oita": {
        "id": "1854484",
        "kenmei": "大分県",
        "kenfurigana": "おおいた"
    },
    "miyazaki": {
        "id": "1856710",
        "kenmei": "宮崎県",
        "kenfurigana": "みやざき"
    },
    "kagoshima": {
        "id": "1860825",
        "kenmei": "鹿児島県",
        "kenfurigana": "かごしま"
    },
    "okinawa": {
        "id": "1854345",
        "kenmei": "沖縄県",
        "kenfurigana": "おきなわ"
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】日付処理ならCarbonにお任せ!!

PHPでは現在時刻を取得したり、一年後の日付を取得したりすることができる
「DateTimeクラス」がありますが、これがまた少し使いにくい、、、
そんな時に出てくるのがCarbonです!!
CarbonはDateTimeクラスを継承しており、言うなればDateTimeをもっと使いやすくしたクラスです。
しかも、PHPで人気なフレームワークLaravelには標準でインストールされています
Laravel内でupdated_atやcreated_atはCarbonインスタンスでできているらしい!!
というわけで、Laravelを使っているのであれば、使うしかないという状況です。

使用可能なクラスはCarbon、CarbonImmutableの2つ

こちらの章は細かいことについて書いているので読み飛ばしてもらっても大丈夫です

使用可能なクラスはCarbonとCarbonImmutableです
どちらのクラスとも同じメソッドを使えます
しかし、インスタンスからメソッドを呼び出した時の挙動が少し違います
下記の例で確認しましょう

//インスタンス作成
$mutable = Carbon::now();
$immutable = CarbonImmutable::now();
//1日後の日付を取得する
$modifiedMutable = $mutable->add(1, 'day');
$modifiedImmutable = $immutable->add(1, 'day');

//Carbonクラスの場合、返り値と元のインスタンスの値が同じになる
var_dump($modifiedMutable === $mutable);             // bool(true)
var_dump($mutable->isoFormat('dddd D'));             // string(11) "Saturday 15"
var_dump($modifiedMutable->isoFormat('dddd D'));     // string(11) "Saturday 15"

//CarbonImmutableクラスの場合、返り値と元のインスタンスの値は異なる
var_dump($modifiedImmutable === $immutable);         // bool(false)
var_dump($immutable->isoFormat('dddd D'));           // string(9) "Friday 14"
var_dump($modifiedImmutable->isoFormat('dddd D'));   // string(11) "Saturday 15"

Carbonクラスはメソッドを呼び出したインスタンスが書き換えられる
CarbonImmutableクラスはメソッドを呼び出したインスタンスはそのままになる

Carbon、CarbonImmutableは相互変換可能

Carbon、CarbonImmutableは相互に変換可能です
下記のようにtoMutable()toImmutable()メソッドを使用することで相互変換できます

toMutable()メソッドはCarbonImmutableクラスをCarbonクラスに変換します
toImmutable()メソッドはCarbonクラスをCarbonImmutableクラスに変換します

ちなみに
isMutable()メソッドはCarbonかどうかを判定します
isImmutable()メソッドはCarbonImmutableかどうかを判定します

<?php
use Carbon\Carbon;

$mutable = CarbonImmutable::now()->toMutable();
var_dump($mutable->isMutable());                     // bool(true)
var_dump($mutable->isImmutable());                   // bool(false)

$immutable = Carbon::now()->toImmutable();
var_dump($immutable->isMutable());                   // bool(false)
var_dump($immutable->isImmutable());                 // bool(true)

CarbonクラスとCarbonImmutableクラスは同じメソッドが使えるようなので、
CarbonImmutableクラスでも同じ方法で使えるはずです

以下に使い方の例を記載するので、活用してください

インスタンス作成方法

<?php
use Carbon\Carbon;

$carbon = new Carbon();
$now = Carbon::now();
$nowInLondonTz = Carbon::now('Europe/London');
$date = Carbon::now('+13:30');
$today = Carbon::today();
$tomorrow = Carbon::tomorrow();
$yesterday = Carbon::yesterday();

$tomorrow = Carbon::tomorrow('Europe/London');

// オリンピックの開会式の時間を変数に設定
$year = 2020; $month = 7; $day = 24;
$hour = 20; $minute = 00; $second = 00; $tz = 'Asia/Tokyo';

Carbon::createFromDate($year, $month, $day, $tz);
Carbon::createMidnightDate($year, $month, $day, $tz);
Carbon::createFromTime($hour, $minute, $second, $tz);
Carbon::create($year, $month, $day, $hour, $minute, $second, $tz);

$dt = new Carbon('2020-02-15');
echo $dt->copy()->subDay(); // 2020-02-15 00:00:00
echo $dt->subDay(); // 2020-02-15 00:00:00

ゲッター

<?php
use Carbon\Carbon;

$dt = Carbon::parse('2012-10-5 23:26:11.123789');

$dt->year;
    // int(2012)
$dt->month;
    // int(10)
$dt->day;
    // int(5)
$dt->hour;
    // int(23)
$dt->minute;
    // int(26)
$dt->second;
    // int(11)
$dt->micro;
    // int(123789)
$dt->dayOfWeek;
    // int(5)
$dt->dayOfWeekIso;
    // int(5)
$dt->englishDayOfWeek;
    // string(6) "Friday"
$dt->shortEnglishDayOfWeek;
    // string(3) "Fri"
$dt->locale('de')->dayName;
    // string(7) "Freitag"
$dt->locale('de')->shortDayName;
    // string(3) "Fr."
$dt->locale('de')->minDayName;
    // string(2) "Fr"
$dt->englishMonth;
    // string(7) "October"
$dt->shortEnglishMonth;
    // string(3) "Oct"
$dt->locale('de')->monthName;
    // string(7) "Oktober"
$dt->locale('de')->shortMonthName;
    // string(3) "Okt"
$dt->localeDayOfWeek;
    // string(7) "Freitag"
$dt->shortLocaleDayOfWeek;
    // string(2) "Fr"
$dt->localeMonth;
    // string(7) "Oktober"
$dt->shortLocaleMonth;
    // string(3) "Okt"
$dt->dayOfYear;
    // int(279)
$dt->weekNumberInMonth;
    // int(1)
$dt->weekOfMonth;
    // int(1)
$dt->weekOfYear;
    // int(40)
$dt->daysInMonth;
    // int(31)
$dt->timestamp;
    // int(1349479571)
Carbon::now()->timezoneName;
    // UTC
Carbon::now()->tzName;
    // UTC

比較

$first->equalTo($second);
$first->notEqualTo($second);
$first->greaterThan($second);
$first->greaterThanOrEqualTo($second);
$first->lessThan($second);
$first->lessThanOrEqualTo($second);

Carbon::create(2012, 9, 5, 5)->between($first, $second, false);

$dt = Carbon::now();
$dt2 = Carbon::createFromDate(1987, 4, 23);

$dt->isSameAs('w', $dt2);
$dt->isFuture();
$dt->isPast();

$dt->isSameYear($dt2);
$dt->isCurrentYear();
$dt->isNextYear();
$dt->isLastYear();
$dt->isLeapYear();
$dt->isCurrentMonth();
$dt->isNextMonth();
$dt->isLastMonth();
$dt->isWeekday();
$dt->isWeekend();
$dt->isMonday();
$dt->isTuesday();
$dt->isWednesday();
$dt->isThursday();
$dt->isFriday();
$dt->isSaturday();
$dt->isSunday();
$dt->isLastOfMonth();

$dt->is('Sunday');
$dt->is('June');
$dt->is('2019');
$dt->is('12:23');
$dt->is('2 June 2019');
$dt->isSameDay($dt2);
$dt->isCurrentDay();
$dt->isYesterday();
$dt->isToday();
$dt->isTomorrow();
$dt->isNextWeek();
$dt->isLastWeek();

$dt->isSameHour($dt2);
$dt->isCurrentHour();
$dt->isSameMinute($dt2);
$dt->isCurrentMinute();
$dt->isSameSecond($dt2);
$dt->isCurrentSecond();
$born->isBirthday($noCake);
$born->isBirthday($yesCake);
$overTheHill->isBirthday();

足し算、引き算

<?php
use Carbon\Carbon;

$dt = Carbon::create(2012, 1, 31, 0);

echo $dt->toDateTimeString();            // 2012-01-31 00:00:00

echo $dt->addCenturies(5);               // 2512-01-31 00:00:00
echo $dt->addCentury();                  // 2612-01-31 00:00:00
echo $dt->subCentury();                  // 2512-01-31 00:00:00
echo $dt->subCenturies(5);               // 2012-01-31 00:00:00

echo $dt->addYears(5);                   // 2017-01-31 00:00:00
echo $dt->addYear();                     // 2018-01-31 00:00:00
echo $dt->subYear();                     // 2017-01-31 00:00:00
echo $dt->subYears(5);                   // 2012-01-31 00:00:00

echo $dt->addQuarters(2);                // 2012-07-31 00:00:00
echo $dt->addQuarter();                  // 2012-10-31 00:00:00
echo $dt->subQuarter();                  // 2012-07-31 00:00:00
echo $dt->subQuarters(2);                // 2012-01-31 00:00:00

echo $dt->addMonths(60);                 // 2017-01-31 00:00:00
echo $dt->addMonth();                    // 2017-03-03 00:00:00
echo $dt->subMonth();                    // 2017-02-03 00:00:00
echo $dt->subMonths(60);                 // 2012-02-03 00:00:00

echo $dt->addDays(29);                   // 2012-03-03 00:00:00
echo $dt->addDay();                      // 2012-03-04 00:00:00
echo $dt->subDay();                      // 2012-03-03 00:00:00
echo $dt->subDays(29);                   // 2012-02-03 00:00:00

echo $dt->addWeekdays(4);                // 2012-02-09 00:00:00
echo $dt->addWeekday();                  // 2012-02-10 00:00:00
echo $dt->subWeekday();                  // 2012-02-09 00:00:00
echo $dt->subWeekdays(4);                // 2012-02-03 00:00:00

echo $dt->addWeeks(3);                   // 2012-02-24 00:00:00
echo $dt->addWeek();                     // 2012-03-02 00:00:00
echo $dt->subWeek();                     // 2012-02-24 00:00:00
echo $dt->subWeeks(3);                   // 2012-02-03 00:00:00

echo $dt->addHours(24);                  // 2012-02-04 00:00:00
echo $dt->addHour();                     // 2012-02-04 01:00:00
echo $dt->subHour();                     // 2012-02-04 00:00:00
echo $dt->subHours(24);                  // 2012-02-03 00:00:00

echo $dt->addMinutes(61);                // 2012-02-03 01:01:00
echo $dt->addMinute();                   // 2012-02-03 01:02:00
echo $dt->subMinute();                   // 2012-02-03 01:01:00
echo $dt->subMinutes(61);                // 2012-02-03 00:00:00

echo $dt->addSeconds(61);                // 2012-02-03 00:01:01
echo $dt->addSecond();                   // 2012-02-03 00:01:02
echo $dt->subSecond();                   // 2012-02-03 00:01:01
echo $dt->subSeconds(61);                // 2012-02-03 00:00:00

echo $dt->addMilliseconds(61);           // 2012-02-03 00:00:00
echo $dt->addMillisecond();              // 2012-02-03 00:00:00
echo $dt->subMillisecond();              // 2012-02-03 00:00:00
echo $dt->subMillisecond(61);            // 2012-02-03 00:00:00

echo $dt->addMicroseconds(61);           // 2012-02-03 00:00:00
echo $dt->addMicrosecond();              // 2012-02-03 00:00:00
echo $dt->subMicrosecond();              // 2012-02-03 00:00:00
echo $dt->subMicroseconds(61);           // 2012-02-03 00:00:00

echo $dt->add(61, 'seconds');                      // 2012-02-03 00:01:01
echo $dt->sub('1 day');                            // 2012-02-02 00:01:01
echo $dt->add(CarbonInterval::months(2));          // 2012-04-02 00:01:01
echo $dt->subtract(new DateInterval('PT1H'));      // 2012-04-01 23:01:01

<?php
use Carbon\Carbon;

echo Carbon::now('America/Vancouver')->diffInSeconds(Carbon::now('Europe/London')); // 0

$dtOttawa = Carbon::createMidnightDate(2000, 1, 1, 'America/Toronto');
$dtVancouver = Carbon::createMidnightDate(2000, 1, 1, 'America/Vancouver');
echo $dtOttawa->diffInHours($dtVancouver);                             // 3
echo $dtVancouver->diffInHours($dtOttawa);                             // 3

echo $dtOttawa->diffInHours($dtVancouver, false);                      // 3
echo $dtVancouver->diffInHours($dtOttawa, false);                      // -3

$dt = Carbon::createMidnightDate(2012, 1, 31);
echo $dt->diffInDays($dt->copy()->addMonth());                         // 31
echo $dt->diffInDays($dt->copy()->subMonth(), false);                  // -31

$dt = Carbon::createMidnightDate(2012, 4, 30);
echo $dt->diffInDays($dt->copy()->addMonth());                         // 30
echo $dt->diffInDays($dt->copy()->addWeek());                          // 7

$dt = Carbon::createMidnightDate(2012, 1, 1);
echo $dt->diffInMinutes($dt->copy()->addSeconds(59));                  // 0
echo $dt->diffInMinutes($dt->copy()->addSeconds(60));                  // 1
echo $dt->diffInMinutes($dt->copy()->addSeconds(119));                 // 1
echo $dt->diffInMinutes($dt->copy()->addSeconds(120));                 // 2

echo $dt->addSeconds(120)->secondsSinceMidnight();                     // 120

人との違い

<?php
use Carbon\Carbon;

$dt = CarbonImmutable::create(2017, 1, 31, 0);

echo $dt->addMonth();                    // 2017-03-03 00:00:00
echo $dt->subMonths(2);                  // 2016-12-01 00:00:00

定数

<?php
use Carbon\Carbon;

var_dump(Carbon::SUNDAY);                          // int(0)
var_dump(Carbon::MONDAY);                          // int(1)
var_dump(Carbon::TUESDAY);                         // int(2)
var_dump(Carbon::WEDNESDAY);                       // int(3)
var_dump(Carbon::THURSDAY);                        // int(4)
var_dump(Carbon::FRIDAY);                          // int(5)
var_dump(Carbon::SATURDAY);                        // int(6)

var_dump(Carbon::YEARS_PER_CENTURY);               // int(100)
var_dump(Carbon::YEARS_PER_DECADE);                // int(10)
var_dump(Carbon::MONTHS_PER_YEAR);                 // int(12)
var_dump(Carbon::WEEKS_PER_YEAR);                  // int(52)
var_dump(Carbon::DAYS_PER_WEEK);                   // int(7)
var_dump(Carbon::HOURS_PER_DAY);                   // int(24)
var_dump(Carbon::MINUTES_PER_HOUR);                // int(60)
var_dump(Carbon::SECONDS_PER_MINUTE);              // int(60)

以上です!!!
ここまで読んでいただきありがとうございました!!
疑問、気になるところがございましたら、質問、コメントよろしくお願いします!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】就職活動の為の、webアプリ(ポートフォリオ)設計図公開!|未経験からエンジニアへ

web系エンジニアを目指し、PHPを学習している初学者です。
就職活動の武器とすべく着手した、webアプリ(ポートフォリオ)作成。
MENTAを利用し、本格的に学習開始してから4ヶ月で、デプロイに漕ぎ着けました!
派手さもモダンな感じもないですが、バリデーションや不正アクセス防止など、地味な部分を丁寧にコーディングしました。
採用担当の方、同じエンジニアを目指す初学者の方、エンジニアを目指そうか迷っている方の目に留まれば幸いです。

記事の目的

  • 面接に備えた補助資料として
  • 同じ境遇の方の参考(になれば幸い)

作成した【内定者懇親サイト】について、
1. カタログ
2. 仕様書
3. 気を付けた点
4. 後悔している点
をまとめました。

1.カタログ

作成したサイトの紹介。

〜内定者懇親サイト〜

内定承諾してから、入社するまでの期間。
「他の内定者は何人位?年齢は?どんな人?」
「内定式で会う前に知って欲しい・知っておきたい、話のネタを仕込んでおきたい。」
そんな願いを叶える、簡易的な懇親サイト。

概要

ユーザー種類

  • 管理者
  • メンバー

機能

  • 管理者|メンバー全員の編集、削除。他の管理者の新規登録、編集、削除
  • メンバー|自身の登録、編集、削除。他のメンバー 一覧、詳細の閲覧

開発環境

MAMPでローカル環境構築。
VScodeでコーディング→Githubでコード管理→herokuでデプロイ

  • PHP 7.3.11
  • HTML/CSS、Bootstrap4
  • MySQL 5.7.26

2.仕様書

サイトの設計図です。

機能一覧

  • メンバーのCRUD機能(新規登録、プロフィール編集、削除)
  • 管理者のCRUD機能
  • 管理者権限付与(メンバーの編集、削除)
  • メンバー 一覧表示機能
  • メンバー 詳細表示機能
  • レスポンシブ対応(Bootstrap)

ER図

データベースの設計図。リレーションは慎重に決める。
NaiteiKonshinDB1.png

ステートチャート図

NaiteiKonshin_ステートチャート図.png

画面遷移図

NaiteiKonshin_画面遷移図.png
これらは無料で使えるLucidchartを利用し、作成しました。

3.気を付けた点

基本スキルを示す

  • フレームワークを使わない、スクラッチ開発(フロント部分はBootstrap使用)
  • CRUD
  • 一通りの入力形式(テキストボックス、チェックボックス、ラジオボタン、ファイルのアップロード)実装
  • 各項目に対するバリデーションを丁寧に
  • パスワードのハッシュ化 (PASSWORD_DEFAULT使用)
  • 不正アクセス防止

fraud_access.gif

ログイン

  1. メンバーログインと新規登録、管理者ログインを1つのログイン画面にまとめ、login_check.phpにて、移行画面を変更した
  2. バリデーションはメソッドを呼び出し、返り値はboolean型とした
login_check.php
class Login
    {
    public function __construct()
    {
        2.validation
        $email_validation = new emailValidation();
        $is_email = $email_validation->isEmail($this->clean['email']);
        //パスワードも同様に
        //validationエラーなら、エラーメッセージを取得し、ログイン画面へ戻る
    }

    public function main()
    {
        1.DB接続しメンバーテーブル管理者テーブルの情報と入力された情報を照合
            メール 一致なし              =>新規登録画面へ
            メールパスワード
                メンバーと一致           =>メンバーログイン
                管理者と一致            =>管理者ログイン
            メール 一致パスワード 一致なし =>エラーメッセージを取得しログイン画面へ戻る
    }
}
$login = new Login();
$login->main();

login.gif

新規登録

  1. パスワードの再入力は、ログインユーザーの邪魔にならないよう新規登録画面で行った スクリーンショット 2020-02-19 16.13.56.png
  2. 再入力が必要な(バリデーションエラー、やり直し選択)時、入力履歴を残し、ユーザー負担を減らした
    スクリーンショット 2020-02-19 16.16.36.png

  3. 入力値をhtmlspecialchars処理する為に、hsc.phpにデータを渡すが、配列(3つ選択させるチェックボックス)も同様の処理が出来るようにした

hsc.php
class Hsc
{
    public static function clean($posts)
    {
        foreach ($posts as $post => $value) {
            3.配列の場合
            if(is_array($value)) {
                foreach($value as $key => $val) {
                    $clean[$post][$key] = htmlspecialchars($val, ENT_QUOTES, 'UTF-8');
                }
            } else {
                $clean[$post] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
            }
        }
        return $clean;
    }
}

アラート表示

$statusで、前画面での処理内容を表示した

list.phpの場合
<?php
    if(!empty($status)):
?>
<div class="alert alert-success" role="alert">
    <?php
        switch ($status) {
            case 'login':
                echo 'ログイン成功しました。';
            break;
            case 'new':
                echo '新規登録が完了しました。';
            break;
            case 'edit':
                echo '編集が完了しました。';
            break;
            case 'delete':
                echo '削除が完了しました。';
            break;
        }
    ?>
</div>
<?php endif; ?>

スクリーンショット 2020-02-19 16.09.42.png

4.後悔していること

  • 開発履歴を、Githubのブランチやissuesを使用して残せば良かった(ずっとmasterにpushし、コメントも「third commit」など無意味なものばかり^^;)
  • 自身の困りごとを元に、実用性の高いものにすれば良かった
  • クラスをあまり活用出来なかった
  • Twitter的な交流(コメント、イイね!)がない
  • ソートがない
  • 変数の付け方、仕様書を定めずに着手してしまった
  • 後から「アラート付けよう!」など追加していったので、無駄な修正作業や、抜け・漏れで新たなエラーを生み出してしまった

以上です。
Twitter的な機能やソートについては、追加実装するかもしれませんが、ひとまずはこれでデプロイ。
コーディングして画面が出来上がってからは、夢中で作成することが出来ました。
ぜひ、サイトにアクセスしてみてください!
https://github.com/kunitani920/BullentinBoard

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

php-fpmとfastcgiを使ってphpを実行する

恒例

FastCGIに関して
https://ja.wikipedia.org/wiki/FastCGI

FPMに関して
http://php.net/manual/zh/install.fpm.php
https://php-fpm.org/

php-fpm Installation

Yumでインストールする

yum install php55w-fpm #php5.5の場合
yum install php56w-fpm #php5.6の場合
yum install php70w-fpm #php7.0の場合

自分でcompileでインストールする
--enable-fpmというペラメタつけてphpをコンパイルする
例えば

./configure --prefix=/usr/local/php --with-apxs2=/usr/local/httpd/bin/apxs --with-zlib --with-mysql=mysqlnd --with-mysqli --enable-mbstring --enable-ftp --with-xsl --with-gd --with-jpeg-dir=/usr/local/libjpeg --with-png-dir=/usr/local/libpng --with-freetype-dir --with-pdo-mysql --with-openssl --enable-soap --with-mcrypt --enable-zip --with-tidy --with-curl --enable-gd-native-ttf --enable-bcmath --enable-sockets --enable-exif --with-gettext --with-ldap --enable-fpm --with-fpm-user=fpm-user --with-fpm-group=fpm-group

--enable-fpm
--with-fpm-user=fpm-user
--with-fpm-group=fpm-group

fpm-userとfpm-groupをサーバーのユーザーに替えてください

FastCGI Installation

Yumでインストールする
bash
yum install fcgi-devel fcgi

自分でcompileでインストールする
https://httpd.apache.org/mod_fcgid/ からmod_fcgidをダウンロードする

tar zxf mod_fcgid-2.3.9.tar.gz
cd mod_fcgid-2.3.9/
APXS=/usr/local/httpd/bin/apxs ./configure.apxs
make
make install

httpd.confを編集する

#追加する
LoadModule fcgid_module modules/mod_fcgid.so 
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

#追加する
<IfModule fcgid_module>
 AddHandler fcgid-script .php
 FcgidWrapper "/usr/local/php/bin/php-cgi" .php  #自分のphpのpathに替えてください
</IfModule>

#追加する
<IfModule fcgid_module>
 AddHandler fcgid-script .php
 AddHandler fcgid-script .fcgi
 FcgidWrapper "/usr/local/php/bin/php-cgi" .php  #自分のphpのpathに替えてください
 <FilesMatch \.php$>
  SetHandler "proxy:fcgi://127.0.0.1:9000"
 </FilesMatch>
</IfModule>

php-fpm.confを編集する

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
; will be used.
user = fpm-user
group = fpm-group

listen = 127.0.0.1:9000
; listen = /usr/local/php/var/run/socket

#socket と port 2つlisten方法がある、一つを選んでください

php-fpmをスタート

/usr/local/php/sbin/php-fpm

phpinfo()で確認する

Server API は FPM/FastCGIで表示されると成功!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laradoc環境作成でmysql接続でつまずいた話

laradock環境にて::ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock'

事象

php artisan migrate

上記コマンドを流すと、タイトルのエラーが発生。。。

いやぁまいったな。。。。

とりあえずググるか。。。

某記事1 DB_HOST=localhostとしてください。

ふむふむやってみよう。。。

数十秒後

できねぇな、、、

某記事2 DB_HOST=127.0.0.1としてください。

ほんとか。。!?

数十秒後

やっぱりできんやないかい。。。

解決策

どうやら立ち上がっているコンテナに紐付けられている
HOSTにしなければいけないみたい。。。
なるほどね。
ってことで下記手順

プロジェクト配下で

docker-compose exec mysql /bin/bash

下記で一番下に出てきたIPをDB_HOSTに記載!

cat /etc/hosts

んで改めてmigrateコマンド実行するとうまくいきました!!!

いやぁまいったね。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのリソースコントローラ

laravelのリソースコントローラについてのメモです。

公式リファレンス
https://readouble.com/laravel/5.8/ja/controllers.html?header=%25E3%2583%25AA%25E3%2582%25BD%25E3%2583%25BC%25E3%2582%25B9%25E3%2582%25B3%25E3%2583%25B3%25E3%2583%2588%25E3%2583%25AD%25E3%2583%25BC%25E3%2583%25A9

リソースコントローラ

リソースコントローラを使うとアプリの基本的な操作であるCRUD(作成、一覧表示、編集、削除)などのアクションやそのルーティングが自動的にされます。

例えばusersのリソースコントローラだと下のようなアクションが自動で作られます。

HTTPメソッド URI アクション ルート名
GET /users index users.index
GET /users/create create users.create
POST /users store users.store
GET /users/{user} show users.show
GET /users/{user}/edit edit users.edit
PUT/PATCH /users/{user} update users.update
DELETE /users/{user} destroy users.destroy

作成

コントローラをつくるartisanコマンドに--resourceをつけます。

php artisan make:controller UsersController --resource

すると自動的にコントローラ内にこれらのアクションが用意されます。

UsersController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UsersController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

ルーティング

web.php
Route::resource('users', 'UsersController');

これだけですべてのアクションの紐づけができます。

また使うアクションを限定することもできます。作成、一覧表示のみを行う場合↓

web.php
//作成、一覧表示のみを行う場合
Route::resource('users', 'UsersController', ['only' => ['index', 'create', 'store', 'show']]);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのリソースコントローラを使ってCRUDアクション作成

laravelのリソースコントローラについてのメモです。

公式リファレンス
https://readouble.com/laravel/5.8/ja/controllers.html?header=%25E3%2583%25AA%25E3%2582%25BD%25E3%2583%25BC%25E3%2582%25B9%25E3%2582%25B3%25E3%2583%25B3%25E3%2583%2588%25E3%2583%25AD%25E3%2583%25BC%25E3%2583%25A9

リソースコントローラ

リソースコントローラを使うとアプリの基本的な操作であるCRUD(作成、一覧表示、編集、削除)などのアクションやそのルーティングが自動的にされます。

例えばusersのリソースコントローラだと下のようなアクションが自動で作られます。

HTTPメソッド URI アクション ルート名
GET /users index users.index
GET /users/create create users.create
POST /users store users.store
GET /users/{user} show users.show
GET /users/{user}/edit edit users.edit
PUT/PATCH /users/{user} update users.update
DELETE /users/{user} destroy users.destroy

作成

コントローラをつくるartisanコマンドに--resourceをつけます。

php artisan make:controller UsersController --resource

すると自動的にコントローラ内にこれらのアクションが用意されます。

UsersController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UsersController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

ルーティング

web.php
Route::resource('users', 'UsersController');

これだけですべてのアクションの紐づけができます。

また使うアクションを限定することもできます。作成、一覧表示のみを行う場合↓

web.php
//作成、一覧表示のみを行う場合
Route::resource('users', 'UsersController', ['only' => ['index', 'create', 'store', 'show']]);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでプレースホルダを使ったフォーム作成

PHPでフォーム作成

以前の記事で、DVWAでは変数をSQL文にそのままいれたことから、SQLインジェクションが可能なコードとなってしまっていました。

そこで今回は、そんなSQLインジェクションに対処するための方法として、プレースホルダについて紹介したいと思います。

プレースホルダとは?

プレースホルダはその名の通り、SQL文に対して後から値をセットするための場所を確保する機能となっています。

プレースホルダは、疑問符プレースホルダの?、名前付きプレースホルダの:値、で表現されまます。

また、後から入れる値のバインド値のことを、プリペアドステートメント(予約文)と言います。

このプレースホルダを使うことによって、入力値から直接SQLインジェクションが行われることを、回避できるというわけです。

HTML/PHP/MySQLでフォーム作成

ここでは、実際にフォームを作成し、プレースホルダを使うコードについて記載していきたいと思います。

コードは下記の記事を参考にしています。
https://noumenon-th.net/programming/2016/01/18/mysql-2/

前提条件

  • PHP 5.46
  • MySQL 14.14

MySQLでDB、テーブル、カラムの設定

まずは、MySQLでDBとテーブルとカラムをセットしていきます。

ここではDBをtest1、テーブルをname_list、カラムをid・nameでセットします。

# mysql -u root -p

mysql> CREATE DATABASE test1;
mysql> USE test1;
mysql> CREATE TABLE name (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,name TEXT NOT NULL) DEFAULT CHARACTER SET=utf8;

フォームのHTMLファイルを作成

まず基本となる簡単な、フォームをHTMLで記載していきます。

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>データを入力する</title>
    <meta charset="utf-8">
    </head>
<body>
  <h1>フォーム画面</h1> 
      <form action="form.php" method="post">
          名前を入力:<input type="text" name="name">
          <input type="submit" value="登録する">
      </form>
</body>
</html>

フォーム情報を登録するPHPファイル作成

次に、フォーム情報をMySQLに登録するPHPファイルを作成していきます。

SQL文はプレースホルダを使って表示していきます。

form.php
<?php

header("Content-type: text/html; charset=utf-8");

//データベース接続
$server = "IPアドレス";  
$userName = "MySQLのユーザー名"; 
$password = "MySQLのパスワード"; 
$dbName = "test1";

$mysqli = new mysqli($server, $userName, $password, $dbName);

if ($mysqli->connect_error){
    echo $mysqli->connect_error;
    exit();
}else{
    $mysqli->set_charset("utf-8");
}

if(empty($_POST)) {
    echo "<a href='index.php'>index.php</a>←登録はこのページから";
}else{
    //名前入力判定
    if (!isset($_POST['name'])  || $_POST['name'] === "" ){
        echo "名前が入力されていません。";
    }else{
        //プリペアドステートメントの設定(SQL文を最初に用意して、その後はクエリ内のパラメーターの値だけを変更する)
        $stmt = $mysqli->prepare("INSERT INTO name_list (name) VALUES (?)");

        if($stmt){
            //プレースホルダに値を設定する、bind_paramsの第一引数は文字列が入るので's'
            $stmt->bind_param('s', $name);
            $name = $_POST['name'];

            //登録する名前をhtml表記、クオテーションを取った状態で表示
            if($stmt->execute()){
                echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8')."さんを登録しました";
            }else{
                echo $stmt->errno . $stmt->error;
            }

            $stmt->close();
        }else{
            echo $mysqli->errno . $mysqli->error;
        }
    }
}

// データベース切断
$mysqli->close();

?>

実際にはこんな感じに動作します。

demo

これで、プレースホルダを利用したPHPのフォーム作成は完了です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker環境のメールはmailhogで決まり

docker環境でメールどうしよう問題

  • 直接送りゃいいんじゃね?
    • いやいや、事故るっしょ。1万通とかメール送信してしもたら・・・
  • そのためだけにsendgridとかmailgunとか
    • おおげさ
  • postfixのコンテナあげる?
    • 環境つくるだけで一苦労しそう(docker-hubにあるけど)

ということでいろいろ探してたらMailHogというものを発見しました

  • メリット
    • 事故らない
    • いくらメール投げてもローカルにしかとどまらないので
    • 設定が簡単
    • WEB UIでメール確認ができる
  • デメリット
    • 日本語メールは文字化けすることがある、UTF-8でおねがいしたい
    • なぜかdockerコンテナが死ぬことがある、まあコンテナのリスタートしてください

Howto

1. docker-compose.ymlにはこれだけ追加するだけ

docker-compose.yml
mailhog:
  image: mailhog/mailhog
  ports:
    - "8025:8025"

2. mhsendmailを入れてください

  • https://github.com/mailhog/mhsendmail
  • goで出来ています
  • yumとかaptでsendmailいちいち入れるの面倒なので、こいつをマウントしてやれば良い
    • 例えばこんな感じ
docker-compose.yml
volumes:
  - "./bin/mhsendmail:/usr/local/bin/mhsendmail"

3. phpの人向け

  • webサーバのコンテナのphp.iniに追加(変更)しておくと、phpの標準mail関数でメール送信できますんで既存コードをいじらなくて済む
php.ini
sendmail_path = "/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025"

以上です、快適なdockerライフを!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

phpのshort_open_tagがOffの場合のエラー内容が分かりづらかった

phpでviewをテンプレートエンジンとして実装している際に以下のような記載をしたところ
以下のようにエラーになりました。

<?php if ($is_ok): ?>
<p><?php echo $message1; ?></p>
<?php else: ?>
<p><?php echo $message2; ?></p>
<? endif; ?>
エラー内容
Fatal Error - Method Fuel\Core\View::__toString() must not throw an exception, caught ParseError: syntax error, unexpected end of file in /XXX/XXX.php on line 0

調べてみると、
<? endif; ?>
この部分が誤字脱字でした。
short_open_tagの指定が必要でした。

short_open_tagとは?

php.iniでshort_open_tagをONにすると<?php<?として省略できます。
が、このphp指定が抜けていたのでおかしくなったようです。

エラー内容が分かりづらい。。

ちなみにphp7.3でもデフォルト値はOnとコメント記載があったのに
設定自体はOffになっていました。

デフォルト設定
; short_open_tag
;   Default Value: On
;   Development Value: Off
;   Production Value: Off

php7.4からは<?はDeprecatedとなりphp8からは廃止される予定のようなので変わったのかな?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[備忘メモ] PHP で各位の IP アドレスを取得する

現在ページをみているユーザのIP

こいつは簡単。

$_SERVER['REMOTE_ADDR'];

Laravel なら下記でも取得できる。

\Request::ip()

プログラム実行しているサーバのIP

こいつは少々厄介。
複数台でサーバ分散されてると特に。

まずは、$_SERVER を使うパターン。

$_SERVER['SERVER_ADDR'];

ただ、上記だとサーバのプライベートIPが取れてしまい、グローバルIPが取れないことがある。
そのため、グローバルIPが欲しい場合、以下を使う。

$hostname = "www.google.com";
$ip = gethostbyname($hostname);

上記ならグローバルIPが取れる。
ただ、サーバ複数で分散してるとダメ。

なので、下記を使う。

$ip = rtrim(`curl inet-ip.info 2>/dev/null`);

多分上手くいくはず。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テーマに正しくjsファイル、スタイルシートを読み込ませる方法

結論

cssファイルを読み込むにはwp_enqueue_style、
jsファイルを読み込むにはwp_enqueue_scriptを使用する。

functions.php
function test_scripts() {
  wp_enqueue_style( 'main-style', get_template_directory_uri().'/css/main.css' ); // cssファイルを読み込む
  wp_enqueue_script( 'main-script', get_template_directory_uri().'/js/main.js'); // jsファイルを読み込む
}

add_action( 'wp_enqueue_scripts', 'test_scripts' );

使用方法

wp_enqueue_style( $handle, $src, $deps, $ver, $in_footer );
wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer );

wp_enqueue_style , wp_enqueue_scriptとも関数の規則は同じです。

$handle

スタイルの名前。
ユニークでないければならない。
既存の名前を入れると、キューに追加されない。

$src

スタイルシート、スクリプトファイルまでのURL。
外部ファイルを読み込むことも可能。その場合は絶対パスで入力する。

ちなみに内部ファイルを読み込む際、以下の関数は頻出度が高く、覚えておくこと必須。

get_template_directory_uri(); // 親テーマのディレクトリを取得
get_stylesheet_directory_uri(); // 子テーマのディレクトリを取得
get_stylesheet_uri(); // 子テーマのstyle.cssを取得

詳しくはcodexを見ると良い。
わかりやすく、書かれている。

関数リファレンス/get template directory uri
関数リファレンス/get stylesheet directory uri
関数リファレンス/get stylesheet uri

$deps

このスタイル・スクリプトを読み込む前に、読み込ませたいスタイル・スクリプトの$handleを配列形式で記入する。

$ver

バージョン番号を指定できる。デフォルトはfalseになっている。

$in_footer

デフォルトはfalse。
trueの場合、</body> 終了タグの前に配置。
falseの場合、<head> に置かれる。

終わりに

正直、codexを見れば全て書かれている。
関数リファレンス/wp enqueue style
関数リファレンス/wp enqueue script
しかし、いちいち開くのが億劫だ。高頻出関数の引数くらい覚えておきたいという願いを込めてメモ✍️

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHP フレームワークを使わない掲示板の制作 *初心者の勉強用

目的

フレームワーク無しのPHP(PHP+Eloquent)で掲示板を作る事で、基本的な技術を確認する(主に脆弱性について)。

*今回はEloquentを使用したのですが、SQLインジェクション対策を学ぶ場合はEloquent無しの方が良いかと思います。

要件定義

入力フォームでのクロスサイトスクリプティングの対策,CSRF対策。
簡単なログイン実装し、パスワードはハッシュ化、バリデーションもする。

脆弱性対策について

新規登録/ログインの処理を例にして書きたいと思います。
参考のURLは下記にまとめています。

新規登録

新規登録の際は、XSS対策のエスケープ処理とパスワードのハッシュ化の処理をしてます。

対策 結果
XSS対策 (エスケープ処理 )
パスワードのハッシュ化

書き方としては、~.tpl.phpがviewで、同名の~.phpがphp部分になります(MVC構造ではないので、phpはコントローラーにまとめられていません)。*GETメソッドで参照される部分と言えるのかな?
~.phprequire_once~.tpl.phpを読み込みます。
下記の場合はsignup.phpsignup.tpl.phpを読み込んでいます。

signup.php
    //データベースの接続読みこんでる
    require_once 'bootstrap.php';

    //Laravelと違って、これしないとsession使えない
    session_start();

    //view読みこんでる
    require_once 'views/signup.tpl.php';
    exit;

バリデーションにsession機能を利用するので、session_start()します。
新規登録/ログイン処理をしたユーザーは、sessionに格納されます。


下記は投稿フォームのviewになります。

signup.tpl.php
<!DOCTYPE html>
<html lang='ja'>
<?php include('header.inc.php'); ?>

<body>
  <?php include('nav.php'); ?>
  <div class="container">
    <form action='newAccount.php' method='post'>

      //新規登録フォームでCSRF対策はしていないのですが、Keyとして一応。
      <input type="hidden" name='user_pass' value='user_pass'>

      <label for='name'>Name</label><br>
      <input type='text' name='name'>
      <p></p>
      <label for='email'>Email</label><br>
      <input type='text' name='email'>
      <p></p>
      <label for='password'>Password</label><br>
      <input text='password' name='password'>
      <p></p>
      <button type='submit'>作成する</button>
    </form>
    <p><a href='index.php'>一覧に戻る</a></p>
  </div>


  <?php include('footer.inc.php'); ?>
</body>

</html>



上記の投稿フォームでPOSTされた情報は、下記のメソッドを参照してユーザー登録されます。
htmlspecialchars()でエスケープ処理をし、password_hash()でパスワードをハッシュ化しています。
【PHP】パスワードのハッシュ化とパスワードの認証をする方法
【PHP入門講座】 XSS攻撃への対策...エスケープ処理について

newAccount.php
require_once 'bootstrap.php';

    session_start();

    if (isset($_POST["user_pass"])) {
        $user = new User;
        $name = $_REQUEST['name'];
        $user->name = htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
        $email = $_REQUEST['email'];
        $user->email = htmlspecialchars($email, ENT_QUOTES, 'UTF-8');
        $password = $_REQUEST['password'];
        $password = htmlspecialchars($password, ENT_QUOTES, 'UTF-8');
        $user->password = password_hash($password, PASSWORD_DEFAULT);
        $user->save();
        $_SESSION['username'] = $_POST['name'];
        header('Location: index.php');
      }
    exit;

ログイン

こっちは新規登録時にハッシュ化されてデータベースに保存されたデータを、読み込む処理をしてます。
また、CSRF対策もしています。「ログイン処理にCSRF対策必要?」とまだ十分に理解していない部分もあるのですが、必要みたいです。下記参照。
ログイン時のCSRF対策は必要か

対策 結果
CSRF対策
XSS対策 (エスケープ処理 )
パスワードのハッシュ化
signin.php
    require_once 'bootstrap.php';

    session_start();

    //ログインページを表示するとトークンを作成します。
    $toke_byte = openssl_random_pseudo_bytes(16);
    $csrf_token = bin2hex($toke_byte);
    //作成したトークンをセッションに格納します。
    $_SESSION['csrf_token'] = $csrf_token;

    require_once 'views/signin.tpl.php';
    exit;

上記で作った$csrf_tokenを下記のフォームでログイン情報と一緒にポストします。

signin.tpl.php
<!DOCTYPE html>
<html lang='ja'>
<?php include('header.inc.php'); ?>

<body>
  <?php include('nav.php'); ?>
  <div class="container">
    <form action='signin_check.php' method='post'>
    <input type="hidden" name="csrf_token" value="<?=$csrf_token?>">
      <label for='name'>Name</label><br>
      <input type='text' name='name'>
      <p></p>
      <label for='email'>Email</label><br>
      <input type='text' name='email'>
      <p></p>
      <label for='password'>Password</label><br>
      <input text='password' name='password'>
      <p></p>
      <button type='submit'>ログイン</button>
    </form>
    <p><a href="signup.php">Create an account</a></p>
    <p><a href="#">Forgot password?</a></p>
    <p><a href='index.php'>一覧に戻る</a></p>
  </div>

  <?php include('footer.inc.php'); ?>
</body>

</html>

セッションに格納されたトークンとフォームからPOSTされたトークンが一致した場合のみ処理が始まります。
パスワードはハッシュ化されている為、password_verify()を使用して読み込みます。

signin-check.php
    require_once 'bootstrap.php';

    session_start();

    $users = User::all();

    if (isset($_POST["csrf_token"])
         && $_POST["csrf_token"] === $_SESSION['csrf_token']) {
        foreach($users as $user){
          if (password_verify ($_POST['password'],$user->password)){

            $_SESSION['username'] = $_POST['name'];
          break;
           }
          }
          header('Location: index.php');
        }        
        echo "不正なリクエストです";

    exit;

CSRF対策

CSRF対策をひよっこエンジニアがまとめてみた(随時更新予定)
サイトを安全に!PHPでcsrf対策を行う方法【初心者向け】
3分で覚え直す$_SESSIONの使い方まとめ [PHP]

XSS対策

【PHP入門講座】 XSS攻撃への対策
XSSの対策をひよっこエンジニアがまとめてみた

まとめ

かなり行き当たりばったりで始めた為、勉強法として半端な感じが否めないです。
反省点も洗い出して、勉強法としてブラッシュアップしたいなと思ってます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】サスケェ!お前はオレにとっての新たな光だ!(ズンドコキヨシリスペクト)

■はじめに

かねてより作ろうと思っていたのですが、すっかり忘れていました。
ということでズンドコリスペクトでPHP上であのセリフがそろうまでループさせます。
7mecIABUZubeCgK1582040700_1582040768.png

■プログラム

<?php
$words = ['サスケェ!','お前は','オレにとっての','新たな','光だ!'];
$seikai = join('',$words);
$totaltimes=0;
do{
    $str ='';
    for($times=1;$times<=count($words);$times++){
        $str .=$words[rand(0,count($words)-1)];
    }
    echo ++$totaltimes."回目:$str\n";
}while($str!= $seikai);          

■おわりに

早ければ100回くらいで揃いますが、運が悪いと5,000回以上もループしますね。
一番笑ったNGは「光だ!サスケェ!オレにとっての光だ!サスケェ!」です。
簡単なプログラムですが、まだまだ改善の余地はありそうです。
そういえば、久しぶりにdo-while使ったな。。

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む