PHP で Selenium を動かすためにやったこと

Selenium 童貞捨てたのは Python だったけど使い慣れてる PHP でも Selenium を使いたかったので備忘録
以前やった内容を後日まとめてるのでもしかしたら手順に抜けがあるかも。。。

前提

  • composer はDL済み

手順

1. ChromeDriver のダウンロード

自分の使ってる GoogleChrome と同じversionの ChromeDriver をDLして解凍する chromedriver.chromium.org

2. chromedriver のパスを通す

自分の場合は /usr/local/bin に移動

mv ~/Downloads/chromedriver /usr/local/bin

※未検証だけど確か WebDriver で chromedriver のパスを指定できるようになってるはず

3. selenium-server-standalone のダウンロード

下記から適当に安定版をDL
www.selenium.dev 自分の場合はDL時にChromeのセキュリティ関係(?)でブロックされたのでシークレットモードで開いてDLした

4. selenium-server-standalone をホームディレクトリに移動

起動しやすい場所であればどこでもOK

mv ~/Downloads/selenium-server-standalone-xxxx.jar ~/

5. composer で php-webdriver/webdriver のインストール

$ composer require --dev php-webdriver/webdriver

6. 適当なプログラムの作成

example.php というファイル名で作成

<?php

use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverBy;

require_once './vendor/autoload.php';

(function () {
    $host = 'http://localhost:4444/wd/hub';
    $driver = RemoteWebDriver::create($host, DesiredCapabilities::chrome());
    $driver->get('https://www.google.com/');
    $driver->findElement(WebDriverBy::cssSelector('input[name="q"]'))
        ->sendKeys('不死川実弥 過去 何巻')->submit();
})();

不死川実弥の過去が何巻だったかをググるだけのプログラム

7. selenium-server-standalone の起動

java -jar ~/selenium-server-standalone-3.4.0.jar &

8. プログラムの実行

php example.php

Macの場合、セキュリティ関係で初回は↓のようなポップアップが出てくるので、

システム設定>セキュリティとプライバシー>一般タブで開くことを許可する必要がある

9. プログラムが動くことの確認


これで PHP でも自動操作し放題だぜ!!!! (対象サイトの利用規約を必ず読みましょう)
WebDriver の操作方法もいつかブログにまとめたい。いつか。。。!

参考

github.com blog.shimabox.net qiita.com


株式会社エイルシステムではWebエンジニア・モバイルアプリエンジニアを募集しています。
実務経験がなくてもOKです。ご興味のある方は弊社HPよりご連絡ください。


[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

【新品】鬼滅の刃 13 遷移変転 吾峠呼世晴/著
価格:484円(税込、送料別) (2021/9/26時点)


[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

鬼滅の刃コミックセット1-19巻 新品送料無料 ポイントでお得
価格:10900円(税込、送料無料) (2021/9/26時点)


USJ 行きましたメモ

※この記事にはUSJのアトラクションである 鬼滅の刃 XRライド や スーパー・ニンテンドー・ワールド に関するネタバレがあります。


知人の誕生日&鬼滅コラボを目当てにユニバ行ってきたので備忘録

エクスプレス・パスの購入

目当てが「鬼滅の刃 XRライド」だったから絶対待ち時間エグいだろうと思って事前に購入(購入にはClubユニバーサルへのアカウント登録が必要)
9/21のチケットを購入できるのが8/18~だったから8/18の0時になった瞬間にオンライン購入しようとしたけどアクセス集中のせいか、サイトが重くなって中々繋がらなかった
翌朝アクセスしたらみんなアクセスだけで購入はしないのかキャンセルしたのかわかんないけど、普通に繋がってスムーズに購入できた

当日行ったら最大240分待ちとかだったから本当に購入しておいてよかった。。。

入場券付きホテルの予約

前日にユニバから徒歩3分くらい?の入場券付きのプランでホテル泊まった


ホテル内ミニオンだらけで可愛かった🥰

f:id:IlIIlIIIlIIlI:20210925191010j:plain
f:id:IlIIlIIIlIIlI:20210925191014j:plain

入場待ち

AM9:00から入場可だったけど知人が禰豆子のポップコーンバケツが欲しいとのことなのでAM7:00くらいから一緒に入場待ち
ホテルの人曰く始発組がAM6:00くらいから並び始めるらしく、この時点ですでに結構並んでた
9月中頃だったけど日光強すぎたし日焼けしたくない人はまじで日傘は必須!!

入場

AM9:00入場かと思いきやAM8:45から入場できた。入場直前に持ち物チェックがあったんだけどペットボトル1本は持ち込みOKらしい。

入場した途端ほとんどの人が禰豆子のポップコーンバケツの売り場までダッシュしてった
クルーの方が一生懸命止めてたけどみんなガン無視だったwww
ちな自分も軽く速歩きしたけど単純に体力がないのでついていけなかった。。。

売り場についたときには既に長蛇の列で2つくらい折返し部分できてた。
40分くらい?並んで禰豆子GET🎉(知人のついでに並んだだけなので親にあげた)
はちみつうめ味のポップコーンがついてくるんだけど禰豆子が目当てだからなのかポップコーンは断っている人もちらほらいた😢
汚したくない+持ち歩くには普通に邪魔wなので購入後はロッカーに預けました

並んでる最中はほんとに↓こんな感じだった。 クルーの方も割り込み禁止の呼びかけはしてたけど、足元にソーシャルディスタンスの線が貼ってあるわけでもないから列は結構密だった。

あとから知ったけど9時には完売してたらしい

アトラクションとか

体力的にエクスプレス・パスに含まれるアトラクションしか乗らなかったw
アトラクションで一番良かったのは圧倒的に 鬼滅の刃 XRライド だった🙌映像が綺麗すぎた🙌

入り口にある炭治郎と煉獄さん🔥

f:id:IlIIlIIIlIIlI:20210925190638j:plain

後ろに回ると禰豆子が顔出してて可愛かった🥰

f:id:IlIIlIIIlIIlI:20210925190812j:plain

マリオカートも期待してたんだけど3Dメガネ?が結構雑で期待はずれでした😭(雑というか横から現実が見える感じ)
ヨッシー・アドベンチャーはまじで子供向け。ヨッシーは可愛いけど個人的に一番いらんかったw
でもニンテンドー・ワールドの世界観はパークの中でずば抜けてよかった🤟

f:id:IlIIlIIIlIIlI:20210925192056j:plain
個人的にスマブラコクッパの爆弾?があることに一番テンションあがった(コクッパ詳しくない) f:id:IlIIlIIIlIIlI:20210925192122j:plain f:id:IlIIlIIIlIIlI:20210925191456j:plain
f:id:IlIIlIIIlIIlI:20210925191127j:plain

右上に現実が見える😭😭😭

https://www.usj.co.jp/ticket/event/kimetsu/exp-7-ride.htmlwww.usj.co.jp

↑のエクスプレス・パスだったんだけど最後の方疲れ切っててミニオンだけ捨てたw
今考えたらヨッシー・アドベンチャー捨てればよかった。。。

時間的な余裕はあったんだけどまじで体力がついてかんかった😭ババア悲しい😭😭😭

ちなみにニンテンドー・ワールドは再入場不可になってて、エリア内のグッズショップにあるグッズのほとんどはエリア外にも売ってるんだけど、ほんの一部だけエリア内にしかないから迷ったらここで先に購入しておくのがおすすめ💪

キノピオカフェのご飯たち🍄 ご飯全部可愛かった🍄 f:id:IlIIlIIIlIIlI:20210925190358j:plain

夜ご飯は鬼滅の藤の花の食事処行った🎴
宇髄さんマーク🎆

f:id:IlIIlIIIlIIlI:20210925191746j:plain

持っていってよかったもの

  • 日傘 ... 日焼け防止のため。これがないと待ち時間暑すぎて死ぬ。
  • ハンディ扇風機 ... ないと暑すぎて死ぬ。
  • モバイルバッテリー ... 基本スマホ触ってなかったんだけど、やっぱ写真撮ってるからか結構減りが早くて持っていってよかった
  • 替えマスク ... コロナ対策。汗かいて気持ち悪いので一応持っていった
  • 持ち歩き除菌スプレー(ジェル) ... コロナ対策。至るところにおいてあったんだけど食べる直前に使う用

持っていかなくてよかったもの

  • USJ公式アプリ ... (持ち物じゃないけど) マップ確認とかアトラクションの待ち時間が確認できる

公式アプリについてはマップ確認できるんだけどマップに載ってる情報が少なすぎて全然使いもんにならんかったw 現在地が確認できるのは便利なんだけど、マップ上にエリア名?とかが載ってないから結構使いにくい
結局パーク内にある紙のマップの方使ってた
エクスプレス・パス使わずに行く人は待ち時間確認用にいいかも?


[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

劇場版「鬼滅の刃」無限列車編【通常版】【Blu-ray】 [ 花江夏樹 ]
価格:3741円(税込、送料無料) (2021/9/25時点)


[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

スーパーマリオ 3Dワールド + フューリーワールド
価格:5920円(税込、送料無料) (2021/9/25時点)


Composer のパッケージを作成した🎉

前からやってみたいと思いつつ後回しにしていた。。。
文字列をLeet文字に変換するっていう使い所がないパッケージだけど作れたことに満足:)

<?php
use Leet\Leet;
Leet::generate('shinazugawa sanemi') // 5h1n42u64w4 54n3m1

今は数字への変換しかできないけど記号への変換も今後対応する予定

そして Travis CI 童貞をやっと捨てた
いまいち使い方をよくわからんまま使ってるけどテスト通ってるのでヨシ!

f:id:IlIIlIIIlIIlI:20210918192311j:plain packagist.org



Laravel の Eloquent ORM でサブクエリを作る

日付単位で number の合計が10以上のレコードのみを抽出する

データ

id target_date number
1 2021-09-01 1
2 2021-09-02 2
3 2021-09-01 3
4 2021-09-03 4
5 2021-09-04 4
6 2021-09-11 2
7 2021-09-06 5
8 2021-09-12 23
9 2021-09-12 10
10 2021-09-23 20

SQL

SELECT
    *
FROM
    samples
INNER JOIN(
    SELECT
        id,
        SUM(number) AS number_sum
    FROM
        samples
    GROUP BY
        samples.target_date
) AS samples1
ON
    samples1.id = samples.id
WHERE
    number_sum >= 10;

※numberはMySQL予約語かも

これを Laravel の Eloquent を使ってサブクエリで書く

<?php
$subQuery = Sample::selectRaw('sum(number) as number_sum')
    ->addSelect('id')
    ->groupBy('target_date');

Sample::joinSub($subQuery, 'samples1', 'samples1.id', 'samples.id')
    ->where([['number_sum', '>=', '10']])
    ->get(['samples.id', 'target_date', 'number_sum']);

joinSub は Eloquent じゃなくて QueryBuilder の機能だった

結果

id target_date number_sum
8 2021-09-12 33
10 2021-09-23 20

参考

github.com


株式会社エイルシステムではWebエンジニア・モバイルアプリエンジニアを募集しています。
実務経験がなくてもOKです。ご興味のある方は弊社HPよりご連絡ください。


テーブルの中で重複しているレコードを抽出する

マスターデータ作るときに何のデータが重複してるかの確認したい時にいつもググってるので整理しておく
ただの確認用なのでサブクエリとかは使わない

テストデータ

id character_name
1 炭治郎
2 伊之助
3 善逸
4 カナヲ
5 禰豆子
6 炭治郎
7 禰豆子

炭治郎と禰豆子が重複しているからこの2行のみを抽出したい

SELECT
    character_name
FROM
    characters
GROUP BY
    character_name
HAVING
    COUNT(character_name) > 1;

結果

id character_name
1 炭治郎
5 禰豆子

要点

GROUP BY した結果に重複している行があるのを確認したいのでWHERE句ではなくHAVINGでやる必要がある
詳しくは以下に記載 iliiliiiliili.hatenablog.jp


[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

鬼滅の刃 23 (ジャンプコミックス) [ 吾峠 呼世晴 ]
価格:506円(税込、送料無料) (2021/9/25時点)



Seleniumでタイピング

前提

  • Seleniumを動かすための設定などは済んでいる
  • jpn.traineddata はDL済み
  • いきなり未定義変数が出てきたりしていますがあくまで個人メモです

手順

1. 初期起動画面待機

初期起動画面でコンテンツが表示されるのを待つ

driver.get(URL)
# 5秒待機
time.sleep(5)

2. 画面上の特定のボタンを押す

クリック座標が確認できるようにブラウザのConsoleに以下を貼り付けて、ボタンをクリックする

document.body.addEventListener('click', (e) => console.log(e))

ボタンをクリックするように設定

# ボタンをクリックしたときにConsoleに表示される座標を設定
ActionChains(driver).move_by_offset(x, y).click().perform()
# クリック後のアニメーションの待機
time.sleep(1)

3. 要素をスクショする

# canvas要素をスクショ
canvas = driver.find_element_by_tag_name('canvas').screenshot_as_png
# 一時保存
file_name = f'{cur_dir}/images/elm.png'
# 書き込みできるように
with open(file_name, 'wb') as f:
f.write(canvas)

4. スクショした画像から文字周辺をトリミング

# スクショしたcanvas要素から文字周辺のみをトリミング
src = Image.open(file_name)
crop = src.crop((left, upper, right, lower))
# トリミングした画像を保存
cropped_file = f'{cur_dir}/images/elm_cropped.png'
crop.save(cropped_file)

5. 文字認識の精度のための画像の調整

# 文字認識の精度調整のため画像を2値化して保存
image_read = cv2.imread(cropped_file)
image_read = cv2.bitwise_not(image_read)
bit_image_file = f'{cur_dir}/images/elm_bit.png'
cv2.imwrite(bit_image_file, image_read)

6. 文字認識

# 文字認識
tools = pyocr.get_available_tools()
res = tools[0].image_to_string(Image.open(bit_image_file), lang="eng")

7. タイピング

# 認識した文字をcliに出力してタイピング
print(res)
driver.find_element_by_tag_name('body').send_keys(res)

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

実践Selenium WebDriver [ サタヤ・アバサララ ]
価格:3300円(税込、送料無料) (2021/9/25時点)


CodeIgniter3 の QueryBuilder でサブクエリを作る

databaseの接続設定は済んでいる前提

$subQuery = $this->db->select('count(*) as column_count, sample_id')->get_compiled_select('table_a');

$this->db->select('*')->join("({$subQuery}) AS table_a", 'table_a.sample_id = samples.id', 'left', false);
$query = $this->db->get('samples');
  • get() ではなく、get_compiled_select() を使用することでクエリを実行しない。
  • get_compiled_select() の第2引数がデフォルト true なので $this->db->reset_query() を実施してくれる。(正しくは _reset_select() )
  • $this->db->from('table') でやる場合はサブクエリの後に $this->db->reset_query()を呼んであげる必要がある。

個人的にはget_compiled_select()で済ませる方がシンプルなので好きです。


※   上記コードはサブクエリとの join() メソッドで第4引数のエスケープを false にしているので場合によってはSQLインジェクションが発生します。   サブクエリでjoinしてあげるクエリはあくまで CodeIgniter の QueryBuilder で組み立てた文字列を使用するようにしましょう!


参考記事

codeigniter.jp www.kabanoki.net


株式会社エイルシステムではWebエンジニア・モバイルアプリエンジニアを募集しています。
実務経験がなくてもOKです。ご興味のある方は弊社HPよりご連絡ください。

Chatwork から来た社内メンバーへの通知をメンションありで Slack に通知する

※過去ブログの転記


作った経緯

  • 基本社内・社外含めコミュニケーションツールは Slack だけど Chatwork を使っているプロジェクトも存在する
  • Chatwork をブラウザタブで常時開いていないため通知に気づかない

流れ

ネットで調べると出てくる部分は省略しました。

  1. Google アカウントの作成(省略)
  2. Slack bot の作成(省略)
  3. Google App Script の作成方法(省略)
  4. Google App Script へのライブラリ追加方法(省略)
    (2020/12/26 時点で新しいUIでの GAS ではライブラリの追加ができなかったので古いUIに切り替えて追加しました)
  5. Google App Script に処理を書く(ここがメイン)
  6. Chatwork でのサービス連携(省略)

省略した部分に関しては以下の記事がわかりやすいと思います

もっとこうした方がいいなどご意見ございましたらご指摘ください!(特に正規表現は雰囲気で書いている。)

const SLACK_TOKEN = 'xoxb-...'; // Slack bot の token を設定
// https://api.slack.com/methods/chat.postMessage のArguments を指定する
const OPTIONS = {
  username: 'Chatwork',
  link_name: true,
  icon_emoji: ":chatwork:"
};
// Slack に存在する社内ユーザーの ChatworkID と SlackユーザーID の定義
// どれが誰か分からなくなるので日本語でコメント文を書いておく
const MENTIONS = {
  '1234567': 'SLACKUSERID1', // ユーザー1
  '890123': 'SLACKUSERID2', // ユーザー2
}
// Chatwork ルームID => Slack チャンネル名
const CHANNELS = {
  '123456789': 'channel-A',
  '12345678': 'channel-B'
}

/**
 * webhook から呼び出される処理
 * ACCOUNTS に対象ユーザーがないメッセージは送らない
 */
function doPost(e) {
  // リクエストからJSONデータを取得してパース
  const obj = JSON.parse(e.postData.contents);
  // Chatwork から受け取ったメッセージ
  const body = obj.webhook_event.body;
  // body に含まれる対象ユーザーを複数検索(ex. 「[To:1234567]ユーザー1さん [To:890123]ユーザー2 さん」 という文字列)
  const toUsers = body.match(/\[To:([\s\S]*?)\]/g);
  var mention = '';
  if (toUsers != null) {
      toUsers.forEach(function (user) {
          const toUser = user.match(/\[To:([\s\S]*?)\]/);
          if (toUser[1] in MENTIONS) {
              mention += "<@"+ MENTIONS[toUser[1]]  +"> さん ";
          }
      });
  }

  // 返信元に ChatworkID が含まれている場合にもメンションに追加する (ex. [rp aid=1234567 to=123456789-xxxx] という文字列)
  const reply = body.match(/\[rp\ aid\=([\s\S]*?)\ to\=/);
  if (reply && reply[1] in MENTIONS) {
    mention += "<@"+ MENTIONS[reply[1]]  +"> さん ";
  }

  // 全員宛の場合はメンションに @channel をつける
  const isAll = body.match(/toall/);
  if (isAll) {
      mention += " <!channel> ";
  }
  
  // メンションが空であれば return
  if (mention === '') {
    return;
  }
  // メンションの設定
  let message = mention + "宛てにチャットワークより連絡がありました。\n";
  // Chatwork のメッセージに飛ぶためのリンク設定
  message += "https://www.chatwork.com/#!rid" + obj.webhook_event.room_id + "-" + obj.webhook_event.message_id + "\n";
  // 本文の表示
  message += body;

  // SlackApp を使用して Slack に message を投げる
  const slackApp = SlackApp.create(SLACK_TOKEN);
  slackApp.postMessage('#' + CHANNELS[obj.webhook_event.room_id], message, OPTIONS);
}

あとは Chatwork 側のWebhookの設定とSlack側のチャンネルたちにSlask Botのアプリを追加してあげる。

というか Chatwork の Webhook の上限が5件までっぽい。有料プランは上限上げてほしい。

コミュニケーションツールはSlack一択にしたいな〜〜〜〜〜〜〜


参考にしたサイト

www.whizz-tech.co.jp github.com help.receptionist.jp https://help.chatwork.com/hc/ja/articles/360000142962-%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88ID%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8Bhelp.chatwork.com

丸囲み文字コピペ用

数字

① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩
⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳

英文字

Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ
Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ

カタカナ

㋐ ㋑ ㋒ ㋓ ㋔ ㋕ ㋖ ㋗ ㋘ ㋙ ㋚ ㋛ ㋜ ㋝ ㋞ ㋟ ㋠ ㋡ ㋢ ㋣ ㋤ ㋥ ㋦ ㋧ ㋨
㋩ ㋪ ㋫ ㋬ ㋭ ㋮ ㋯ ㋰ ㋱ ㋲ ㋳ ㋴ ㋵ ㋶ ㋷ ㋸ ㋹ ㋺ ㋻ ㋼ ㋽ ㋾

Linuxコマンド覚書

find

ファイルもしくはディレクトリの検索 実行すると検索結果が出力される

$ find {検索するパス} {検索条件} {オプション}

拡張子を指定して検索

$ find ../../ -name '*.txt'

特定の文字列を含むファイルを検索

$ find Desktop/ -type f -name "*2019*"

sampleという名前が含まれるファイルを検索

$ find /Desktop -name "*sample*" -type f

sampleという名前が含まれるディレクトリを検索

$ find /Desktop -name "*sample*" -type d

名前にsampleが含まれる、パーミッションが644のものを検索

$ find /Desktop -name "*sample*" -perm 644 

昨日更新されたファイルを検索

$ find Desktop/ -mtime 1

0:今日
1:昨日
-3:三日以内に更新されたファイル
+10:10日よりも前に更新されたファイル

空ファイルもしくはディレクトリを検索する

$ find Desktop/ -empty

ファイル名がsmapleを含むかつ.pngファイルを検索する

$ find Desktop/ -name "*sample*" -and  -name "*.png"

ファイル名がsmapleを含むもしくは.pngファイルを検索する

$ find Desktop/ -name "*sample*" -or  -name "*.png"

grep

ファイルの中の文字列を検索する

$ grep {検索正規表現} {パス/ファイル名}

デスクトップにあるsample.txtの中にaaaという文字列がないかを検索する

$ grep aaa Desktop/sample.txt 

検索結果に行番号を表示する

$ grep aaa Desktop/sample.txt -n

大文字小文字を区別せずに検索する

$ grep aaa Desktop/sample.txt -i

検索結果の前後2行分を表示する

$ grep aaa Desktop/sample.txt -C2

ディレクトリ内も検索対象とする

$ grep aaa Desktop/ -r

open

open -a {アプリを指定} {パスとファイル名} 

finderを開く

$ open .

Chromeでデスクトップのsample.jpegを開く

$ open -a "Google Chrome" Desktop/sample.jpeg 

アプリ名にスペースがある場合はダブルクォートで囲う

一階層上の全ての.pngファイルを開く

$ open ../*.png

screencapture

$ screencapture {保存場所とファイル名}

デスクトップにsample.pngという名前で画面キャプチャ

$ screencapture ../Destop/sample.png

PHPUnitのアサーションについて

書いてないのもあるので修正が必要



論理値の検証

assertFalse

assertFalse(bool $condition[, string $msg = ''])

$conditionがfalseかどうか
assertNotFalse()はこの逆

assertTrue

assertTrue(bool $condition[, string $msg = ''])

$conditionがtrueかどうか
assertNotTrue()はこれの逆

assertIsBool

assertIsBool($actual[, string = ''])

$actualがboolかどうか
assertIsNotBool()はこの逆

空かどうかの検証

assertEmpty

assertEmpty(mixed $actual[, string $msg = '’])

$actualが空かどうか
assertNotEmpty()はこれの逆

assertNull

assertNull(mixed $variable[, string $msg = ''])

$variableNULLかどうか


等しいかどうか

assertEquals

assertEquals(mixed $expect, mixed $actual[, string $msg = ''])

$expect$actualが等しいかどうか
等しくない時に$msgを返す
assetNotEquals()はこの逆

assertEquals(float $expect, float $actual[, string $msg = '', float $delta = 0])

$expect$actualの差分が$delta以下かどうか
以上の時に$msgを返す

assertSame

assertSame(mixed $expect, mixed $actual[, string $msg = ''])

2つの変数$expect$actualが同じ型・同じ値かどうか
assertNotSame()はこれの逆


比較の検証

assertGreaterThan

assertGreaterThan(mixed $expect, mixed $actual[, string $msg = ''])

$actualの値が$expectの値より大きいかどうか
$expect < $actual
大きくない時に$msgを返す

assertGreaterThanOrEqual

assertGreaterThanOrEqual(mixed $expect, mixed $actual[,string $msg =''])

$actualの値が$expectの値を含み大きいかどうか
$expect <= $actual
以下の時に$msgを返す

assertLessThan

assertLessThan(mixed $expect, mixed $actual[, string $msg = ''])

$actualの値が$expectより小さいかどうか
$expect > $actual
小さくない時に$msgを返す

assertLessThanOrEqual

assertLessThanOrEqual(mixed $expect, mixed $actual[, string $msg = ''])

$actualの値が$expectの値を含み小さいかどうか
$expect >= $actual
以上の時に$msgを返す


配列の検証

assertIsArray

assertIsArray($acutual[,string $msg = ''])

$actualが配列かどうか
assertIsNotArray()はこの逆

assertArrayHasKey

assertArrayHasKey(mixed $key, array $array [, string $msg = ‘'])

$array配列に$keyキーが存在するかどうか
存在しないときにエラー$msgを返す
asserArrayNotHasKey()はこれの逆

assertArraySubset

assertArraySubset(array $subset, array $array [, bool $strict = false, string $msg = '']);

$array$subsetを含んでいるかどうか
含まない場合に$msgを返す
$stricttrueにすると型の比較まで行う
エラーログが$subsetをダンプするだけで、どこが違うかを教えてくれないのでわかりづらい

assertContains

assertContains(mixed $needle, iterable $haystack[, string $msg])

$needle$haystackに存在するかどうか
objectも検証可能
存在しない場合に$msgを返す
assertNotContains()はこれの逆

assertContainsOnly

assertContainsOnly(string $type, itrable $haystack[, bool $isNativeType = null, strig $msg = '’])

$haystackの中身の型が$typeだけかどうか
$typeだけじゃない時に$msgを返す
$isNaviteTypeクラスは$typeがネイティブなPHPの型であるかどうかを表す
assertNotContainsOnly()はこれの逆


オブジェクトの検証

assertObjectHasAttribute

assertObjectHasAttribute(string $attrName, object $obj[, string $msg = ''])

$obj->attrNameが存在するかどうか
assertObjectNotHasAttribute()はこれの逆


文字列の検証

assertContains

assertContains(string $needle, string $haystack[, string $msg], bool $ignoreCase = false])

$needle$haystackの文字列に含まれるかどうか
$ignoreCasetrueの時に大文字小文字を区別しない

assertIsString

assertIsString($actual[, $msg = ''])

$actualの型がstringかどうか
assertIsNotString()はこれの逆

assertStringMatchsFormat

assertStringMatchsFormat(string $format, string $string[, string $msg = ''])

$string が書式文字列$formatに一致しているかどうか

assertRegExp

assertRegExp(string $pattern, string $string[, string $msg = ''])

$string$patternに一致しているかどうか
assertNotRegExp()はこれの逆

assertStringEndsWith

assertStringEndsWith(string $suffix, string $string[, string $msg = ''])

$string$suffixで終わっているかどうか
assertStringEndsNotWith()はこれの逆

assertStringStartsWith

assertStringStartsWith(string $prefix, string $string[, string $msg = ''])

$string$prefixで始まっているかどうか
assertStringStartsNotWith()はこれの逆


数の検証

assertCount

assertCount($expect, $haystack[, string $msg = ''])

$haystackの要素数$expectと一致しているかどうか
assertNotCount()はこれの逆


クラスの検証

assertClassHasAttribute

assertClassHasAttribute(string $attrName, string $className, string $msg = ‘')

$classNameにプロパティ変数attrNameが存在するかどうか
存在しない場合に$msgを返す
assertClassNotHasAttribute()はこれの逆

assertClassHasStaticAttribute

assertClassHasStaticAttribute(string $attrName, string $className [, string $msg = ''])

$classNameに静的プロパティ$attrNameが存在するかどうか
存在しない場合に$msgを返す

assertContainsOnlyInstancesOf

assertContainsOnlyInstancesOf(string $className, Traversable|array $haystack[, string $msg = ''])

$haystack$classNameクラスの唯一のインスタンスを含むかどうか
含まない時に$msgを返す


その他の型の検証

assertIsFloat

assertIsFloat($actual[, string $msg =''])

$actualの型がfloatかどうか
assertIsNotFloat()はこれの逆

assertIsInt

assertIsInt($actual[, string $msg = ''])

$actualの型がintかどうか
assertIsNotInt()はこれの逆

assertIsInterable

assertIsInterable($actual[, string $msg])

$actualの型がintertableかどうか
assertIsNotInterable()はこれの逆

assertIsNumeric

assertIsNumeric($actual[, string $msg = ''])

$actualの型がnumericかどうか
assertIsNotNumeric()はこれの逆

assertIsObject

assertIsObject($actual[, string $msg = ''])

$actualの型がobjectかどうか
assetIsNotObject()はこれの逆

assertIsResource

assertIsResource($actual[, string $msg = ''])

$actualの型がresouceかどうか
assertIsNotResouce()はこれの逆

assertIsScalar

assertIsScalar($actual[, string $msg = ''])

$actualの型がscalar値かどうか
assertIsNotScalar()はこれの逆

assertIsCallable

assertIsCallable($actual[, string $msg])

$actualの型がcallableかどうか
assertIsNotCallable()はこの逆


その他の検証

assertInfinite

assertInfinite(mixed $variable[,string $msg])

$variableがINFかどうか
assertFinite()はこの逆

assertNan

assertNan(mixed $valiable[, string $msg= ''])

$valiableがNANかどうか

assertInstanceOf

assertInstanceOf($expect $actual[, string $msg= ''])

$actual$expectインスタンスかどうか
assertNotInstanceOf()はこの逆

assertEqualXMLStructure

assertEqualXMLStructure(DOMElement $expect, DOMElement $actual[, bool $checkAttr = false, $msg = ''])

$actualのDOMElementのXML構造が$expectのDOMElementのXML構造と等しいかどうか


ファイル・ディレクトリの検証

assertIsReadable

assertIsReadable($actual[, string $msg = ''])

$actualに指定したファイルもしくはディレクトリが読み込み可能かどうか
読み込み不可の場合は$msgを返す
assertNotIsReadable()はこれの逆

assertIsWritable

assertIsWritable($actual[, string $msg = ''])

$actualに指定したファイルもしくはディレクトリが書き込み可能かどうか
書き込み不可の場合は$msgを返す
assertNotIsWritable()はこれの逆

assertDirectoryExsits

assertDirectoryExsits(string $dir[, string $msg = ''])

$dirで指定したディレクトリが存在するかどうか
存在しない時に$msgを返す
assertDirectoryNotExists()はこれの逆

assertDirectoryIsReadable

assertDirectoryIsReadable(string $dir[, string $msg = ''])

$dirで指定したディレクトリが読み込み可能かどうか
読み込み不可の時に$msgを返す
assertDirectoryNotIsReadable()はこれの逆

assertDirectoryIsWritable

assertDirectoryIsWritable(string $dir[, string $msg = ''])

$dirで指定したディレクトリが書き込み可能かどうか
書き込み不可の時に$msgを返す
assertDirectoryNotIsWritable()はこれの逆

assertFileEquals

assertFileEquals(string $expect, string $actual[, string $msg = ''])

$expectで指定したファイルと$actualで指定したファイルの内容が等しいかどうか
異なる場合に$msgを返す
assertFileNotEquals()はこの逆

assertFileExists

assertFileExists(string $fileName[, string $msg = ''])

ファイル$fileNameが存在するかどうか
存在しない時は$msgを返す
assertFileNotExists()はこの逆

assertFilesReadable

assertFilesReadable(string $fileName[, string $msg = ''])

$fileNameで指定したファイルが読み込み可能かどうか
読み込み不可の場合は$msgを返す
assertFilesNotReadable()はこの逆

assertFilesWritable

assertFilesWritable(string $fileName[, string $msg = ''])

$fileNameで指定したファイルが書き込み可能かどうか
書き込み不可の時には$msgを返す
assertFilesNotWritable()はこの逆

JSONの検証

assertJsonFileEqualsJsonFile

assertJsonFileEqualsJsonFile(mixed $expectFile, mixed $actualFile[, srtring $msg])

$actualFile$expectFileの内容がJSONとして等しいかどうか

assertJsonStringEqualsJsonFile

assertJsonStringEqualsJsonFile(mixed $expectFile, mixed $actualJson[, string $msg = ''])

$actualJsonの値が$expectFileの値と等しいかどうか

assertJsonStringEqualJsonString

assertJsonStringEqualJsonString(mixed $expectJson, mixed $actualJson[, string $msg =''])

$actualJsonの値が$expectJsonの値と等しいかどうか


参考にしたサイト

phpunit.readthedocs.io

jQueryのDOM操作メソッド 〜要素の取得編〜

続き
途中だけど忘れそうなのでとりあえずアップだけしておく...
後々追加するかも
もしかしたら間違ってる部分あるかも


children - 指定した要素の1階層下の子要素を取得する

$("#children").on("click",function(){
  $(this).children("p").css("background-color","red");
});

find - 指定した要素の子孫要素を取得する

p

pspan

p

$("#find").on("click",function(){
  $("#findTarget").find("span").css("background-color","red");
});

closest - 指定した直近の親要素を取得

  • listParent1
  • listParent2
    • listChild1
    • listChild2
    • listChild3
  • listParent3
  • listParent4
$("#closest").on("click",function(){
  $("#closestTarget ul li:last-of-type").closest("ul").css("background-color","red");
});

contents - 指定した要素の子要素のテキストを含み取得

contentsTarget
$("#contents").on("click",function(){
  console.log($(".contentsTarget").contents());//要素の中のテキストも取得
  console.log($(".contentsTarget").children());//要素を取得
  $(".contentsTarget").contents().wrap('<p style="background-color: red;" />');
});

next - 指定した要素の直後の兄弟要素を返す

$("#next").on("click",function(){
  $(this).next().css("background-color","red");
});

nextAll - 指定した要素以降の兄弟要素を返す

$("#nextAll").on("click",function(){
  $(this).nextAll("[id^='nextAllTarget']").css("background-color","red");
});

nextUntil - 指定した要素以降の兄弟要素で指定したセレクタに一致するまで選択する

$("#nextUntil").on("click",function(){
  $(this).nextUntil("#nextUntilTarget3","button").css("background-color","red");//第二引数でフィルタリングできる
});

prev - 指定した要素の直前の兄弟要素を返す

$("#prev").on("click",function(){
  $(this).prev().css("background-color","red");
});

prevAll - 指定した要素以前の兄弟要素を返す

$("#prevAll").on("click",function(){
  $(this).prevAll("[id^='prevAllTarget']").css("background-color","red");
});

prevUntil - 指定した要素以前の兄弟要素で指定したセレクタに一致するまで選択する

$("#prevUntil").on("click",function(){
  $(this).prevUntil("#prevUntilTarget2","button").css("background-color","red");//第二引数でフィルタリングできる
});

first - 指定した要素の一番最初の要素を取得

  • list
  • list
  • list
  • list
$("#first").on("click",function(){
  $("#firstTarget li").first().css("background-color","red");
});

last - 指定した要素の一番最後の要素を取得

  • list
  • list
  • list
  • list
$("#last").on("click",function(){
  $("#lastTarget li").last().css("background-color","red");
});

not - 指定したセレクタを除く

  • list
  • list
  • list
  • list
  • list
  • list
$("#not").on("click",function(){
  $("#notTarget").children("li").not(":nth-of-type(2)").css("background-color","red");
});

each - 指定した要素をループ処理

  • list
  • list
  • list
  • list
  • list
  • list
$("#each").on("click",function(){
  $("#eachTarget li").each(function(key,value){
    $(this).text($(this).text() + key);
  });
});

is - 指定した要素などのbool値を返す

$("#is").on("click",function(){
  var $true = $(this).is(":visible");
  var $false = $(this).is(":hidden");
  console.log($true);
  console.log($false);
});

has - 指定した要素などを持つ要素を取得する

hasTarget1

hasTarget2
$("#has").on("click",function(){
  $("[id^='hasTarget']").has("p").css("background-color","red");
  $("[id^='hasTarget']:has(span)").css("background-color","blue");
});

siblings - 前後を問わず同じ階層の要素を取得する

siblingsTarget1

siblingsTarget2
var $css = {
  "background-color":"red",
  "color":"white"
};
$("[id^=siblingsTarget]").css($css);
$("#siblings").on("click",function(){
  $(this).siblings("div[id^=siblingsTarget]").css("background-color","blue");
});

slice - 指定した部分を取り出す

  • list
  • list
  • list
  • list
  • list
$("#slice").on("click",function(){
  $("#sliceTarget li").slice(2,4).css("background-color","red");//第二引数がなければ2番目以降〜最後まで
});

filter - 要素の絞り込み

  • list
  • list
  • list
  • list
  • list
$("#filter").on("click",function(){
  $("#filterTarget li").filter(":nth-of-type(3)").css("background-color","red");
  $("#filterTarget li").filter("#last").css("background-color","blue");
});

eq - 0から始まるインデックス番号の要素を取得

  • list0
  • list1
  • list2
  • list3
  • list4
$("#eq").on("click",function(){
  $("#eqTarget li").eq(3).css("background-color","red");
  $("#eqTarget li").eq(-1).css("background-color","blue");//-1で最後の要素を取得する -2の場合はlist3を取得する
});

add - 指定した要素を追加

  • list
  • list
  • list
  • list
$("#add").on("click",function(){
  var $style = $("#addTarget li:nth-of-type(2)").next().add().css("background-color","red");//3番目のliにcssが適用される
  console.log($style);//2番目のli
});

addBack - 選択した要素に指定した要素を追加する

  • list
  • list
  • list
  • list
$("#addBack").on("click",function(){
  var $style2 = $("#addBackTarget li:nth-of-type(2)").next().addBack().css("background-color","red");
  console.log($style2);//2,3番目のli
});

end - 最新のフィルタリング処理を破棄して一つ前の選択状態に戻す

  • list
  • list
  • list
  • list
$("#end").on("click",function(){
  $("#endTarget").find("li:first-of-type").css("background-color","red").end().find("li:nth-of-type(2)").css("background-color","blue");
});

アコーディオンメニューを開いたときにコンテンツの一番初めからスクロールさせる

  • list

余計な余白


<button type="button" id="scroll">scroll()</button>
<ul id="scrollTarget">
    <li style="height: 500px;">list</li>
</ul>
<div style="height: 300px;">
    <p>余計な余白</p>
</div>
<script>
  var $scrollTarget = $("#scrollTarget");
  //開閉メニューのボタン
  var $scroll = $("#scroll");
  //topからの位置を取得
  var $scrollInt = $("#scroll").offset().top;

  //初期設定
  $scrollTarget.hide();

  $scroll.on("click",function(){
    $scrollTarget.slideToggle("normal",function(){
      //表示状態であれば、topからの位置分スクロールトップに移動
      if($scrollTarget.is(":visible")) {
        $("body,html").animate({scrollTop: $scrollInt},"fast");
      }
    });
  });
</script>

パーミッション確認方法など覚書

記号 意味 数字 権限 使用例
r read 4 読み出し 読むだけのHTML,CSS,画像ファイルなど
w write 2 書き込み データの書き込みや書き換えが必要なファイル
x exec 1 実行 サーバー上で実行が必要なファイル touch,rmなどはできない

記述例

数字 記号 意味
644 rw-r--r-- 所有者読み書き可能/所有者以外読み出しのみ (ファイルのデフォルトの権限)
755 rwxr-xr-x 所有者以外書き込み不可 (ディレクトリのデフォルトの権限)
700 rwx------ 所有者のみ読み書き実行可能
666 rw-rw-rw- 読み書きのみ

777 (rwxrwxrwx) はセキュリティ上良くない

$ ls -l は隠しファイルが表示されないので

$ ls -all {パス}

で指定パスのパーミッションを確認する

例:

drwxr-xr-x   18 user  staff     96 12  1 19:48 directory_name
1 2 3 4 5 6 7 8 9 10 11 12
d rwx r-x r-x 18 user staff 96 12 1 19:48 directory_name
  1. ディレクトリかファイルか
    d:ディレクト
    -:ファイル
    |:シンボリックリンク

  2. 所有者の権限

  3. グループの権限

  4. その他のアクセス権限

  5. そのディレクトリにあるディレクトリorファイル数

  6. 所有者名

  7. グループ名

  8. ファイルサイズ

  9. 作成日時もしくは変更日時の月

  10. 作成日時もしくは変更日時の日

  11. 作成日時もしくは変更日時の時間

  12. ファイルもしくはディレクトリ名

変更するときはchmod(change mode)で変更可能

$ chmod {変更したい権限} {パス}

パーミッション確認方法など覚書

※過去ブログの転記

 

 

記号 意味 数字 権限 使用例
r read 4 読み出し 読むだけのHTML,CSS,画像ファイルなど
w write 2 書き込み データの書き込みや書き換えが必要なファイル
x exec 1 実行 サーバー上で実行が必要なファイル touch,rmなどはできない

記述例

数字 記号 意味
644 rw-r--r-- 所有者読み書き可能/所有者以外読み出しのみ (ファイルのデフォルトの権限)
755 rwxr-xr-x 所有者以外書き込み不可 (ディレクトリのデフォルトの権限)
700 rwx------ 所有者のみ読み書き実行可能
666 rw-rw-rw- 読み書きのみ

例:

drwxr-xr-x   18 user  staff     96 12  1 19:48 directory_name
1 2 3 4 5 6 7 8 9 10 11 12
d rwx r-x r-x 18 user staff 96 12 1 19:48 directory_name
  1. ディレクトリかファイルか
    d:ディレクト
    -:ファイル
    |:シンボリックリンク

  2. 所有者の権限

  3. グループの権限

  4. その他のアクセス権限

  5. そのディレクトリにあるディレクトリorファイル数

  6. 所有者名

  7. グループ名

  8. ファイルサイズ

  9. 作成日時もしくは変更日時の月

  10. 作成日時もしくは変更日時の日

  11. 作成日時もしくは変更日時の時間

  12. ファイルもしくはディレクトリ名

変更するときはchmod(change mode)で変更可能

$ chmod {変更したい権限} {パス}