tag:crieit.net,2005:https://crieit.net/tags/PHP/feed 「PHP」の記事 - Crieit Crieitでタグ「PHP」に投稿された最近の記事 2022-10-13T13:41:39+09:00 https://crieit.net/tags/PHP/feed tag:crieit.net,2005:PublicArticle/18297 2022-09-26T22:01:37+09:00 2022-10-13T13:41:39+09:00 https://crieit.net/posts/BBS アノニマス BBS プログラムの系譜をまとめていく <p>BBS プログラムについて調べていると日本でのインターネット掲示板のプログラムが時代背景の理解なしには、なぜそうなっているのかまったくわからない点があるので、掘り下げて1995年辺りから系譜をおって概要を調べてまとめていきたい。</p> <p>よく知られている匿名のインターネット掲示板である 4chan、そのクローンのような komica (Taiwan) 、8kun 等へ影響を与えているようなので、その元になったと思われるプログラムのことについてよく知りたい。つまり、2 ch てよく知らないけど、まだあるということを最近知って、その直接的ではないが 2ch から影響をうけた 4chan 、8chan 等で事件がよく起こっていることに関心がある。</p> <hr /> <p>komica<br /> <a target="_blank" rel="nofollow noopener" href="https://ja.m.wikipedia.org/wiki/Komica">https://ja.m.wikipedia.org/wiki/Komica</a></p> <p>8chan / 8kun<br /> <a target="_blank" rel="nofollow noopener" href="https://ja.m.wikipedia.org/wiki/8chan">https://ja.m.wikipedia.org/wiki/8chan</a></p> <p>歴史参考資料:<br /> あめぞうの歴史<br /> <a target="_blank" rel="nofollow noopener" href="http://history.amebbs.com/">http://history.amebbs.com/</a></p> <p>『あやしいわーるどの歴史』(2006/09/28 現在)<br /> <a target="_blank" rel="nofollow noopener" href="http://f16.aaacafe.ne.jp/~stwalker/">http://f16.aaacafe.ne.jp/~stwalker/</a></p> <p>スレッドフロート型掲示板<br /> <a target="_blank" rel="nofollow noopener" href="https://ja.m.wikipedia.org/wiki/スレッドフロート型掲示板">https://ja.m.wikipedia.org/wiki/スレッドフロート型掲示板</a></p> <p>Everything shii knows<br /> 'Anonymous'<br /> <a target="_blank" rel="nofollow noopener" href="https://shii.bibanon.org/shii.org/knows/Anonymous.html">https://shii.bibanon.org/shii.org/knows/Anonymous.html</a><br /> '4chan'<br /> <a target="_blank" rel="nofollow noopener" href="https://shii.bibanon.org/shii.org/knows/4chan.html">https://shii.bibanon.org/shii.org/knows/4chan.html</a></p> <p>「Qアノン」はなぜ日本でも浸透しているのか? 黒幕の実像に迫ったルポ/藤原学思『Qを追う 陰謀論集団の正体』<br /> <a target="_blank" rel="nofollow noopener" href="https://note.com/asahi_books/n/n30f689b063bf">https://note.com/asahi_books/n/n30f689b063bf</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://archive.ph/bjmj1">https://archive.ph/bjmj1</a><br /> 個人情報3万件流出か<br /> <a target="_blank" rel="nofollow noopener" href="https://archive.ph/5vPgR">https://archive.ph/5vPgR</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://archive.ph/jsZdB">https://archive.ph/jsZdB</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://crieit.now.sh/upload_images/1bc8646e5abd82bb79be14705c53c99763413ffe8b67c.png">image</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://crieit.now.sh/upload_images/26c0b56a78d0f45842551ef97242f58663427b260a61b.jpg">image</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://atmarkit.itmedia.co.jp/ait/articles/1309/12/news012.html">https://atmarkit.itmedia.co.jp/ait/articles/1309/12/news012.html</a></p> <p><a target="_blank" rel="nofollow noopener" href="https://crieit.now.sh/upload_images/c253b04ab039a1a90209ef72ec32c1b163427bc13fef7.jpg">image</a></p> <p>Onionちゃんねるで発生した「さっしーえっち ◆MwKdCUj7XWlQ」による情報漏えいをまとめてみた。<br /> <a target="_blank" rel="nofollow noopener" href="https://piyolog.hatenadiary.jp/entry/20130901/1378006827">https://piyolog.hatenadiary.jp/entry/20130901/1378006827</a></p> <p>「昔から、「2chのコードをひろゆきは書いてない」というデマを流す人がいたけど、あめぞうが潰れたのはあめぞうウイルスの影響です。」@hirox246<br /> <a target="_blank" rel="nofollow noopener" href="https://togetter.com/li/1957179?page=3">https://togetter.com/li/1957179?page=3</a></p> <p>なぜ沖縄はデマ情報の悪意にさらされるのか|ひろゆき氏のツイートに端を発し注目を集める沖縄・辺野古の抗議運動。繰り返されるデマと悪意にさらされる構造とは|ゲスト:伊藤昌亮・モバイルプリンス(10/12)<br /> <a target="_blank" rel="nofollow noopener" href="https://youtu.be/k92y1O9jAwk?t=3389">https://youtu.be/k92y1O9jAwk?t=3389</a></p> <h2 id="[ PHP 4 ] ksphp-en (kuz-sysadmin committed on 6 Aug 2021)"><a href="#%5B+PHP+4+%5D+ksphp-en+%28kuz-sysadmin+committed+on+6+Aug+2021%29">[ PHP 4 ] ksphp-en (kuz-sysadmin committed on 6 Aug 2021)</a></h2> <p>A similar script to the one used on ayashii's world, an ancient japanese textboard that is considered the precursor to 2ch.<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/kuz-sysadmin/ksphp-en/">https://github.com/kuz-sysadmin/ksphp-en/</a></p> <p>kuzuha script (perl) => PHP 4</p> <p><strong>group: ayashii world</strong><br /> <strong>group: kuzuha script</strong></p> <h2 id="[ Perl ] くずはすくりぷと (2000.9.3-)"><a href="#%5B+Perl+%5D+%E3%81%8F%E3%81%9A%E3%81%AF%E3%81%99%E3%81%8F%E3%82%8A%E3%81%B7%E3%81%A8+%282000.9.3-%29">[ Perl ] くずはすくりぷと (2000.9.3-)</a></h2> <p><strong>[origin]</strong> くずはすくりぷと Rev.0.1 Preview 9 (2000.9.3) (掲示板本体)<br /> あやしいわーるど@暫定版 Rev. 0.22 (2012.10.19現在)<br /> w.atwiki.jp/swsw/pages/12.html</p> <p><strong>group: ayashii world</strong><br /> <strong>group: kuzuha script</strong></p> <h2 id="[ Perl ] 靴スミ掲示板 (2000.1.21)"><a href="#%5B+Perl+%5D+%E9%9D%B4%E3%82%B9%E3%83%9F%E6%8E%B2%E7%A4%BA%E6%9D%BF+%282000.1.21%29">[ Perl ] 靴スミ掲示板 (2000.1.21)</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://www.csmith.jp/contents/csbbs/">https://www.csmith.jp/contents/csbbs/</a></p> <p><strong>group: amezou</strong></p> <h2 id="[ Perl ] あめぞう型掲示板 スクリプト一覧 (2006/07/23 初版)"><a href="#%5B+Perl+%5D+%E3%81%82%E3%82%81%E3%81%9E%E3%81%86%E5%9E%8B%E6%8E%B2%E7%A4%BA%E6%9D%BF%E3%80%80%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E4%B8%80%E8%A6%A7+%282006%2F07%2F23+%E5%88%9D%E7%89%88%29">[ Perl ] あめぞう型掲示板 スクリプト一覧 (2006/07/23 初版)</a></h2> <p>2007/03/04 更新<br /> あめぞう型掲示板 スクリプト一覧<br /> <a target="_blank" rel="nofollow noopener" href="http://www.amebbs.com/doc/bbs.html">http://www.amebbs.com/doc/bbs.html</a></p> <p><strong>group: amezou</strong></p> <h2 id="[ Perl ] スレッドフロート式、マルチ 某ch風板 (2007/04/28)"><a href="#%5B+Perl+%5D+%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%95%E3%83%AD%E3%83%BC%E3%83%88%E5%BC%8F%E3%80%81%E3%83%9E%E3%83%AB%E3%83%81+++%E6%9F%90ch%E9%A2%A8%E6%9D%BF+%282007%2F04%2F28%29">[ Perl ] スレッドフロート式、マルチ 某ch風板 (2007/04/28)</a></h2> <p>フリースレッドボード<br /> (FreeThreadBoard)<br /> スレッドフロート式、マルチ 某ch風板<br /> <a target="_blank" rel="nofollow noopener" href="http://usa.rgr.jp/cgilab.html">http://usa.rgr.jp/cgilab.html</a></p> <p><strong>group: 2 chan</strong></p> <h2 id="[ PHP 5 ] スレッドフロート型掲示板スクリプト(PHP・UTF-8・スマホ対応) (2021年10月18日 12時00分 正午の月齢:12.0 月名:十三夜 潮汐:中潮 月齢:12.0[十三夜] 潮汐:中潮)"><a href="#%5B+PHP+5+%5D+%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%95%E3%83%AD%E3%83%BC%E3%83%88%E5%9E%8B%E6%8E%B2%E7%A4%BA%E6%9D%BF%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%28PHP%E3%83%BBUTF-8%E3%83%BB%E3%82%B9%E3%83%9E%E3%83%9B%E5%AF%BE%E5%BF%9C%29+%282021%E5%B9%B410%E6%9C%8818%E6%97%A5+12%E6%99%8200%E5%88%86+%E6%AD%A3%E5%8D%88%E3%81%AE%E6%9C%88%E9%BD%A2%3A12.0++%E6%9C%88%E5%90%8D%3A%E5%8D%81%E4%B8%89%E5%A4%9C++%E6%BD%AE%E6%B1%90%3A%E4%B8%AD%E6%BD%AE+%E6%9C%88%E9%BD%A2%3A12.0%5B%E5%8D%81%E4%B8%89%E5%A4%9C%5D+%E6%BD%AE%E6%B1%90%3A%E4%B8%AD%E6%BD%AE%29">[ PHP 5 ] スレッドフロート型掲示板スクリプト(PHP・UTF-8・スマホ対応) (2021年10月18日 12時00分 正午の月齢:12.0 月名:十三夜 潮汐:中潮 月齢:12.0[十三夜] 潮汐:中潮)</a></h2> <p>ThreadFloatBbs・煤式<br /> <a target="_blank" rel="nofollow noopener" href="https://susu.cc/2021/10/threadfloat.html">https://susu.cc/2021/10/threadfloat.html</a></p> <p><strong>group: 2 chan</strong></p> <h2 id="[ Perl ] MiniBBS"><a href="#%5B+Perl+%5D+MiniBBS">[ Perl ] MiniBBS</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://www.rescue.ne.jp/cgi/minibbs1/">https://www.rescue.ne.jp/cgi/minibbs1/</a></p> <h2 id="[ PHP 4 ? ] futaba chan script (02/12/23-)"><a href="#%5B+PHP+4+%3F+%5D+futaba+chan+script+%2802%2F12%2F23-%29">[ PHP 4 ? ] futaba chan script (02/12/23-)</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://news.ycombinator.com/item?id=7639435">https://news.ycombinator.com/item?id=7639435</a><br /> <strong>group: futaba chan</strong><br /> <strong>group: 4 chan</strong></p> <hr /> <h2 id="[ Perl ] , [ PHP ] , [ node.js ] , [ go ] , [ rust ] , [ Scheme ] , [ ur ] , [ elixir ] , [ haskell ] 108 catalouge"><a href="#%5B+Perl+%5D+%2C+%5B+PHP+%5D+%2C+%5B+node.js+%5D+%2C+%5B+go+%5D+%2C+%5B+rust+%5D+%2C+%5B+Scheme+%5D+%2C+%5B+ur+%5D+%2C+%5B+elixir+%5D+%2C+%5B+haskell+%5D+108+catalouge">[ Perl ] , [ PHP ] , [ node.js ] , [ go ] , [ rust ] , [ Scheme ] , [ ur ] , [ elixir ] , [ haskell ] 108 catalouge</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://overscript.net/">https://overscript.net/</a></p> <blockquote> <p>Overscript<br /> a little list of bbs scripts<br /> This is overscript, a comprehensive overview of all known anonymous futaba-style bulletin board scripts.<br /> if you have any scripts you know about that aren't listed here, please email [email protected] with details about it</p> </blockquote> <p><strong>group: futaba chan</strong><br /> <strong>group: 4 chan</strong></p> <h2 id="[ PHP ] Shiichan Anonymous BBS (2004)"><a href="#%5B+PHP+%5D+Shiichan+Anonymous+BBS+%282004%29">[ PHP ] Shiichan Anonymous BBS (2004)</a></h2> <p>Shiichan 3960 - The PHP script that was used at world4ch<br /> <a target="_blank" rel="nofollow noopener" href="https://wakaba-c3-cx.translate.goog/shii/shiichan?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=en&_x_tr_pto=wapp">https://wakaba.c3.cx/shii/shiichan</a></p> <p><strong>group: futaba chan</strong><br /> <strong>group: 4 chan</strong></p> <h2 id="[ PHP ] futallaby"><a href="#%5B+PHP+%5D+futallaby">[ PHP ] futallaby</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://www-1chan-net.translate.goog/futallaby/index_old.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=en&_x_tr_pto=wapp">http://www.1chan.net/futallaby/</a></p> <p><strong>group: futaba chan</strong><br /> <strong>group: 4 chan</strong></p> <h2 id="[ PHP ] Wakaba and [ Perl ] Kareha"><a href="#%5B+PHP+%5D+Wakaba+and+%5B+Perl+%5D+Kareha">[ PHP ] Wakaba and [ Perl ] Kareha</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://wakaba.c3.cx/s/web/wakaba_kareha">https://wakaba.c3.cx/s/web/wakaba_kareha</a></p> <p>WakabaScript - An image board script.<br /> KarehaScript - A database-less message and image board script.</p> <p>Rf. Shiichan Anonymous BBS (<a target="_blank" rel="nofollow noopener" href="https://wakaba-c3-cx.translate.goog/shii/shiichan?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=en&_x_tr_pto=wapp">https://wakaba.c3.cx/shii/shiichan</a>)<br /> Rf. https://shii.bibanon.org/shii.org/knows/Anonymous.html</p> <p><strong>group: futaba chan</strong><br /> <strong>group: 4 chan</strong></p> <h2 id="[ Perl ] 2ch bbs.cgi (2011/1 )"><a href="#%5B+Perl+%5D+2ch+bbs.cgi+%282011%2F1+%29">[ Perl ] 2ch bbs.cgi (2011/1 )</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/j416dy/2ch/blob/master/bbs-main.cgi">https://github.com/j416dy/2ch/blob/master/bbs-main.cgi</a></p> <p><strong>group: 4 chan</strong></p> <h2 id="[ Python2 ] weabot"><a href="#%5B+Python2+%5D+weabot">[ Python2 ] weabot</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/z411/weabot">https://github.com/z411/weabot</a><br /> Rf. https://bienvenidoainternet.org/bai/<br /> <strong>group: PyIB</strong><br /> <strong>group: Bienvenido a Internet BBS/IB</strong></p> <h2 id="[ PHP ] Tinyboard"><a href="#%5B+PHP+%5D+Tinyboard">[ PHP ] Tinyboard</a></h2> <p>Tinyboard is a free light-weight, fast, highly configurable and user-friendly imageboard software package. It is written in PHP and has few dependencies.</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/savetheinternet/Tinyboard">https://github.com/savetheinternet/Tinyboard</a></p> <p><strong>group: Tinyboard</strong></p> <h2 id="[ PHP 5.4- ] vichan"><a href="#%5B+PHP+5.4-+%5D+vichan">[ PHP 5.4- ] vichan</a></h2> <p>vichan is a free light-weight, fast, highly configurable and user-friendly imageboard software package. It is written in PHP and has few dependencies.</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/vichan-devel/vichan">https://github.com/vichan-devel/vichan</a></p> <p><strong>group: <a target="_blank" rel="nofollow noopener" href="https://github.com/savetheinternet/Tinyboard">Tinyboard</a></strong><br /> <strong>group: 8 chan</strong></p> <p>つづく。</p> tomato tag:crieit.net,2005:PublicArticle/18280 2022-08-14T17:08:59+09:00 2022-08-15T06:32:55+09:00 https://crieit.net/posts/OpenBlocks-IoT-WEB OpenBlocks IoTをWEBサーバにする <p>OpenBlocks IoT をWEBサーバにする方法です。<br /> OpenBlocks IoT をセンサー類のゲートウェイとして使用し、さらにWEBサーバとすることができれば、OpenBlocks IoT 1台でセンサーからのデータを入力しモニターする等のシステムを稼働させることも可能となります。</p> <p><a href="https://crieit.now.sh/upload_images/84240a486f4bcb757b594d8970038ed062f84fdae123d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/84240a486f4bcb757b594d8970038ed062f84fdae123d.png?mw=700" alt="image" /></a></p> <p>FW4搭載の OpenBlocks IoT を対象とした解説です。FW4非搭載の OpenBlocks IoT については、汎用OSが裸で搭載されているため、それぞれのOSに応じた手段でWEBページの実装と配信が可能であり、それは難しいことではないためわざわざ解説する必要はないでしょう。</p> <blockquote> <p>FW4とは?という方はこのサイトを参照してください。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.plathome.co.jp/product/fw/fw4-application/">IoTゲートウェイソフトウェア FW4</a></p> </blockquote> <p>一方、FW4搭載の OpenBlocks IoT の場合は、FW4 が HTTP/HTTPS を使用しており、ユーザがWEBページを実装しようとする場合に「さて、どうしたものか」と悩むかもしれません。</p> <blockquote> <p>FW4 にはNode-REDが付随しており、Node-REDを用いたWEBシステム実装が可能です。ただし、以下の解説では、Node-REDを使用せずにWEBシステムを実装する方法を述べています。</p> </blockquote> <h2 id="FW4のHTTPサーバを利用する"><a href="#FW4%E3%81%AEHTTP%E3%82%B5%E3%83%BC%E3%83%90%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B">FW4のHTTPサーバを利用する</a></h2> <p>前述のとおり、FW4 は HTTP/HTTPS を使用しています。FW4 のマンマシン・インターフェースはGUIですが、これはWEBで実装されています。このため、FW4 は自身のGUIを処理するためにHTTPサーバを使用しています</p> <p><a target="_blank" rel="nofollow noopener" href="https://docs.plathome.co.jp/docs/openblocks/fw4/webui/reference/index">Debian Linux FW4 WEB-UIガイド</a></p> <p>FW4 が使用するHTTPサーバをユーザも使用することができます。FW4 のHTTPサーバに相乗りするわけです。<br /> OpenBlocks のメーカーがこれを許しているわけではありません。以下に述べる方法でWEBアプリケーションを構築する場合は自己責任でお願いします。</p> <h3 id="FW4のGUIはどこにいるのか"><a href="#FW4%E3%81%AEGUI%E3%81%AF%E3%81%A9%E3%81%93%E3%81%AB%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B">FW4のGUIはどこにいるのか</a></h3> <p>FW4 の GUI に使用されているWEBページは、ファイル・システム中のどこにいるのか、これがわかれば FW4 のHTTPサーバに容易に相乗りすることができます。<br /> これを探すのは簡単です。OpenBlocks の Debian にログインしコマンドを使用すればわかります。</p> <blockquote> <p>OpenBlocks の Debian にログインするには、FW4の設定でSSHを有効にする必要があります。<br /> <a target="_blank" rel="nofollow noopener" href="https://docs.plathome.co.jp/docs/openblocks/fw4/webui/initial/initial">Debian Linux FW4 スタートアップガイド 初期設定</a></p> </blockquote> <p>FW4 の GUI を処理しているのは PHP です。FW4 のログインのページを見ると、<code>login.php</code>となっている。<br /> <a href="https://crieit.now.sh/upload_images/5690c1081c78d2b94df4ab56361aa3ce62f8a63e5362a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5690c1081c78d2b94df4ab56361aa3ce62f8a63e5362a.png?mw=700" alt="image" /></a><br /> <code>login.php</code> を探すと、<code>/var/webui/docroot</code>と出てくる。わかりやすいですね。</p> <pre><code class="sh">root@obsiot:~# find / -name login.php -print /var/webui/docroot/system/login.php root@obsiot:~# </code></pre> <h3 id="/var/webui/docroot"><a href="#%2Fvar%2Fwebui%2Fdocroot">/var/webui/docroot</a></h3> <p><code>/var/webui/docroot</code>はというと。</p> <pre><code class="sh">root@obsiot:~# ls -l /var/webui/docroot total 112 -rw-r--r-- 1 www-data www-data 1524 Dec 21 2021 _ctrl_datacollect.php -rw-r--r-- 1 www-data www-data 1284 Dec 20 2021 _file_del.php -rw-r--r-- 1 www-data www-data 1412 Jan 26 2022 _nodered_ctl.php -rw-r--r-- 1 www-data www-data 1076 Dec 20 2021 _ppp_con.php drwxr-xr-x 2 www-data www-data 4096 Apr 29 16:15 airmanage drwxr-xr-x 6 www-data www-data 4096 Apr 10 2020 apps drwxr-xr-x 4 www-data www-data 4096 Apr 29 16:16 css drwxr-xr-x 2 www-data www-data 4096 Apr 29 16:15 extension drwxr-xr-x 4 www-data www-data 4096 Apr 29 16:15 images -rw-r--r-- 1 www-data www-data 18740 Dec 20 2021 index.php -rw-r--r-- 1 www-data www-data 4548 Dec 21 2021 index_datacontroller.php -rw-r--r-- 1 www-data www-data 3652 Jan 26 2022 index_nodered.php drwxr-xr-x 3 www-data www-data 4096 Apr 29 16:16 js drwxr-xr-x 3 www-data www-data 4096 Apr 29 16:16 lib drwxr-xr-x 2 www-data www-data 4096 Apr 29 16:15 maintenance drwxr-xr-x 2 www-data www-data 4096 Apr 29 16:15 network -rw-r--r-- 1 www-data www-data 21 Oct 12 2020 phpinfo.php drwxr-xr-x 2 www-data www-data 4096 Apr 29 16:15 service drwxr-xr-x 2 www-data www-data 4096 Apr 29 16:16 system drwxr-xr-x 2 www-data www-data 4096 Apr 29 16:15 technical drwxrwxrwt 2 www-data www-data 4096 Apr 29 17:54 tmp -rw-r--r-- 1 www-data www-data 884 Dec 20 2021 unsupport.php root@obsiot:~# </code></pre> <p><code>phpinfo.php</code>で見てみると。<br /> <a href="https://crieit.now.sh/upload_images/af285e9fb292814cf93441e83a89e8ff62f8a8ceb8b3d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/af285e9fb292814cf93441e83a89e8ff62f8a8ceb8b3d.png?mw=700" alt="image" /></a><br /> 万全の体制です。<br /> ユーザ用のディレクトリを作成します。その下にテスト用のページを作成します。</p> <pre><code class="sh">root@obsiot:/var/webui/docroot# mkdir hoge root@obsiot:/var/webui/docroot# cd hoge root@obsiot:/var/webui/docroot# vi test.php root@obsiot:/var/webui/docroot/hoge# ls -l total 4 -rw-r--r-- 1 root root 32 Aug 14 16:48 test.php root@obsiot:/var/webui/docroot/hoge# cat test.php <?php echo "Hello, World."; ?> root@obsiot:/var/webui/docroot/hoge# </code></pre> <p><a href="https://crieit.now.sh/upload_images/876c8fb28b0bdd37893d4f47bb07be2862f969f93a855.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/876c8fb28b0bdd37893d4f47bb07be2862f969f93a855.png?mw=700" alt="image" /></a><br /> 狙いどおりです。</p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18256 2022-07-23T23:14:58+09:00 2022-07-23T23:47:21+09:00 https://crieit.net/posts/PLC-JSON-6 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 前回は、PLCから取得したデータをデータベースに保存しました。</p> <p><a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a></p> <p>タイトルに書かれたテーマは前回で完了しています。完了していますが、これでは何か物足りないと感じ、今回はデータベースに次々と書かれるPLCからのデータをブラウザに表示してみます。</p> <p><a href="https://crieit.now.sh/upload_images/e6061bc3683ceff53f53e09554a0288e62dbf2a4188d4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e6061bc3683ceff53f53e09554a0288e62dbf2a4188d4.png?mw=700" alt="image" /></a></p> <h2 id="WEBアプリケーション"><a href="#WEB%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">WEBアプリケーション</a></h2> <p>データベースのデータをWEBブラウザに表示するために、WEBアプリケーションをサーバ上に作成します。このサンプルでは、WEBアプリケーションはPHPスクリプトで実装します。</p> <p>いよいよ図が窮屈になってきました。これ以上に窮屈な図は、もはや理解容易性の面で逆効果です。正確さと理解容易性は、ある時点以後、反比例します。</p> <p><a href="https://crieit.now.sh/upload_images/a96212b429a0ca11f28e750801258aea62dbf49dd5ca8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a96212b429a0ca11f28e750801258aea62dbf49dd5ca8.png?mw=700" alt="image" /></a></p> <p>PHPである必要はありません。では、このサンプルを何故にPHPスクリプトで実装するのか?それは、そこ(私の開発環境)にPHPが稼働していたから。</p> <p>PHPの準備についてはここでは解説しません。PHPの準備についての解説は、他の記事にお任せします。PHPスクリプトでのMySQLアクセスについても、他の記事にお任せします。</p> <h2 id="HTMLとPHPスクリプト、そしてJavaScript"><a href="#HTML%E3%81%A8PHP%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%80%81%E3%81%9D%E3%81%97%E3%81%A6JavaScript">HTMLとPHPスクリプト、そしてJavaScript</a></h2> <p>結果的には、サーバ・サイドはPHPスクリプトのみではなく、以下の3つのファイルで構成してみました。</p> <ul> <li>HTML</li> <li>PHPスクリプト</li> <li>JavaScript</li> </ul> <p>3つめの JavaScript は jQuery です。jQuery は一般に配布されているとても便利なライブラリです。jQuery を利用することにより、とても高機能なWEBアプリケーションを短期間で実装することができます。jQuery の解説も他の記事にお任せします。</p> <p>以下がサンプルです。</p> <p>index.htm</p> <pre><code class="html"><html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="Cache-Control" content="no-cache"> <title>Sample</title> <script type="text/javascript" src="js/jquery-3.6.0.min.js"></script> <script type="text/javascript"> function count_update() { $.ajax({ url:"index.php", method:"POST", success:function(data) { $('#count').html(data); } }); } $(function() { setInterval ( function() { count_update(); }, 1000 ); }); </script> </head> <body> count: <div id="count" style="display: inline-block;">count</div> </body> </html> </code></pre> <p>index.php</p> <pre><code class="php"><?php try { $dbh = new PDO('mysql:dbname=hoge;host=localhost;charset=utf8;' , 'hoge' , 'hoge001'); } catch (PDOException $e) { echo "Can't connect to database: " . $e->getMessage() . "\n"; exit(); } $sql = "SELECT JSON_EXTRACT(body, '$.value') AS count FROM from_plc ORDER BY time_insert DESC"; $res= $dbh->query($sql); foreach($res as $value) { echo $value['count']; break; } ?> </code></pre> <p>jQuery は、HTML中で参照されている、jquery-3.6.0.min.js です。<br /> HTML中には、jQueryを呼び出すオリジナルのJavaScriptスクリプトを書いています。<br /> いずれもコードの解説はいたしません。</p> <h2 id="WEBブラウザでアクセスしてみる"><a href="#WEB%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">WEBブラウザでアクセスしてみる</a></h2> <p>WEBブラウザで上記の index.htm を参照すると以下ようになります。<br /> <a href="https://crieit.now.sh/upload_images/104c32d7402ec5f8908cc9021b18f35162dbfdbf6b841.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/104c32d7402ec5f8908cc9021b18f35162dbfdbf6b841.png?mw=700" alt="image" /></a><br /> 数字は、データベース上のテーブルが更新される都度変化してます。(正確には、JSONの要素"value"の値が変化してから1秒以内に、ブラウザの表示も変化します)</p> <h2 id="最後に"><a href="#%E6%9C%80%E5%BE%8C%E3%81%AB">最後に</a></h2> <p>ここまでサンプルとして実装したシステムを応用すると、工場の機器により製造される製品の生産数を、ほぼリアルタイムに画面表示する、といったシステムを構築することができます。<br /> 勘違いしていただきたくないのは、この記事で示したサンプルはあくまでもサンプルであって、基盤となるミドルウェアやプロトコル、プログラム言語は、この記事のサンプルで使用したものである必要はありません。もし、この記事で取り上げたような、PLCからデータを取得し、これを処理するようなシステムを設計・構築する場合は、この記事の内容にとらわれず、それぞれの環境、条件、実現したい機能などに応じて、最適なハードウェア、ミドルウェア、プロトコル、プログラム言語、そしてアーキテクチャを想像し選択してください。</p> <blockquote> <p>実はこのアーキテクチャを想像する、というのが設計者にとって最も楽しく重要な工程なのです。実装のテクニックなどは、アーキテクチャの上に成立する技術であって、アーキテクチャがイマイチなシステムは、おそらく実装もへんてこりんになってしまったり、どこかアンバランスなものになってしまうものです。</p> </blockquote> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18255 2022-07-22T18:43:07+09:00 2022-07-30T16:03:47+09:00 https://crieit.net/posts/PLC-JSON-5 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 前回は、データを保存するデータベースとテーブルを準備しました。</p> <p><a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a></p> <p>今回は、MQTTブローカーからデータを受信するサブスクライバーを実装します。少々駆け足になりますが、サブスクライバーには、データを受信しつつデータベースに書き込むまでを実装します。<br /> <a href="https://crieit.now.sh/upload_images/9a527b4c96ab1ffcf8096821036def3462d966e06c6c1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9a527b4c96ab1ffcf8096821036def3462d966e06c6c1.png?mw=700" alt="image" /></a></p> <h2 id="プログラミング"><a href="#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0">プログラミング</a></h2> <p>Pythonを使用して実装します。Pythonでなければならない合理的な理由はありませんが、私の環境では、Pythonではだめだという理由もありません。</p> <p>PythonでMQTTをハンドリングには、paho-mqttというライブラリを使うと、新規コーディング量を減らすことができます。paho-mqttの導入については、このサイトをおすすめします。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/emqx_japan/items/b63c918fe137a6db4b37">Python で MQTT (Paho)</a></p> <p>同様に、PythonからMySQLをアクセスするには、mysql-connector-python-rfというライブラリを使うと、新規コーディング量を減らすことができます。mysql-connector-python-rfの導入については、このサイトをおすすめします。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/hoto17296/items/0cfe7cdd3c47b69cc892">Python 3 から MySQL を触る</a></p> <p>最低限の機能を実装してみました。</p> <pre><code class="python">import paho.mqtt.client as mqtt import json import mysql.connector #----------------------------- # mqtt host = '127.0.0.1' port = 1883 topic = 'sample' def on_connect(client, userdata, flags, respons_code): print('status {0}'.format(respons_code)) client.subscribe(topic) def on_message(client, userdata, msg): json_text = json.dumps(json.loads(msg.payload)) print(json_text) try: #----------------------------- # database conn = mysql.connector.connect( host='127.0.0.1', port=3306, user='hoge', password='hoge001', database='hoge' ) cur = conn.cursor() sql = "insert into from_plc (body) values (%s);" cur.execute(sql, (json_text,)) conn.commit() cur.close() conn.close() except Exception as e: print(e) print("Debug: Error at Database access.") return if __name__ == '__main__': client = mqtt.Client(protocol=mqtt.MQTTv311) client.on_connect = on_connect client.on_message = on_message client.connect(host, port=port, keepalive=60) client.loop_forever() </code></pre> <p>サンプル・プログラムは、MQTTブローカー(=mosquitto)に届く、Topic = sample のメッセージを待ち受けます。メッセージが届くと、メッセージ中のデータをJSON文字列に変換し、これをMySQLのテーブル"from_plc"に追加します。この動作をひたすら繰り返します。</p> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>Pythonスクリプトを起動しておきます。</p> <pre><code>c:\Users\hoge>py sample.py status 0 </code></pre> <p>これまでと同様に、PLCには、本物の代わりにModbus/TCPシミュレーターを使用します。<br /> Modbus/TCPシミュレーター"mod_RSsim.exe"を起動します。<br /> <a href="https://crieit.now.sh/upload_images/7c92f619579f92befb1d50f450ce74b062d96b0700adb.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7c92f619579f92befb1d50f450ce74b062d96b0700adb.png?mw=700" alt="image" /></a><br /> ゲートウェイのシミュレーターを起動するために、EasyBuilderのメニュー[プロジェクト]-[オンラインシミュレーション]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/a6e571139cf659163b06d6721ff09fbf62d96b6c39201.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a6e571139cf659163b06d6721ff09fbf62d96b6c39201.png?mw=700" alt="image" /></a><br /> シミュレーターの起動を確認します。<br /> <a href="https://crieit.now.sh/upload_images/12e87729c176c0ab6bf620f55f4ff1a262d96ba8913fc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/12e87729c176c0ab6bf620f55f4ff1a262d96ba8913fc.png?mw=700" alt="image" /></a><br /> Modbus/TCPシミュレータの、アドレス40001(注意: +0です)をクリックし、Valueに任意の数値を入力し値を変更します。[OK]ボタンで閉じます。<br /> <a href="https://crieit.now.sh/upload_images/f609d225a2af3983fd89312d7fb7770862d96bd14929c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f609d225a2af3983fd89312d7fb7770862d96bd14929c.png?mw=700" alt="image" /></a><br /> Modbus/TCPシミュレータの、アドレス40001の値を変更する都度、Pythonスクリプトがこの情報をJSONで受け取り、コンソールに表示します。<br /> Pythonスクリプトを止めるには、キーボードから<code>Ctrl+C</code>を押してください。</p> <pre><code>c:\Users\hoge>py sample.py status 0 {"value": 0, "ts": "2022-07-21T15:27:43.676079"} {"value": 1, "ts": "2022-07-21T15:27:49.704662"} {"value": 2, "ts": "2022-07-21T15:27:57.641088"} {"value": 3, "ts": "2022-07-21T15:28:07.644020"} {"value": 4, "ts": "2022-07-21T15:28:11.627588"} {"value": 5, "ts": "2022-07-21T15:28:14.659965"} Traceback (most recent call last): File "c:\Users\hoge\sample.py", line 44, in <module> client.loop_forever() File "C:\Users\hoge\AppData\Local\Programs\Python\Python310\lib\site-packages\paho\mqtt\client.py", line 1756, in loop_forever rc = self._loop(timeout) File "C:\Users\hoge\AppData\Local\Programs\Python\Python310\lib\site-packages\paho\mqtt\client.py", line 1150, in _loop socklist = select.select(rlist, wlist, [], timeout) KeyboardInterrupt ^C c:\Users\hoge>py sample.py </code></pre> <p>MySQLのテーブル"from_plc"を確認すると、ゲートウェイからのJSONデータが保存されているのはわかります。</p> <pre><code>c:\Users\hoge>mysql -u hoge -p Enter password: ******* Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 24 Server version: 8.0.29 MySQL Community Server - GPL Copyright (c) 2000, 2022, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use hoge Database changed mysql> select * from from_plc; +--------------------------------------------------+---------------------+ | body | time_insert | +--------------------------------------------------+---------------------+ | {"ts": "2022-07-21T15:27:43.676079", "value": 0} | 2022-07-21 23:27:43 | | {"ts": "2022-07-21T15:27:49.704662", "value": 1} | 2022-07-21 23:27:49 | | {"ts": "2022-07-21T15:27:57.641088", "value": 2} | 2022-07-21 23:27:57 | | {"ts": "2022-07-21T15:28:07.644020", "value": 3} | 2022-07-21 23:28:07 | | {"ts": "2022-07-21T15:28:11.627588", "value": 4} | 2022-07-21 23:28:11 | | {"ts": "2022-07-21T15:28:14.659965", "value": 5} | 2022-07-21 23:28:14 | +--------------------------------------------------+---------------------+ 6 rows in set (0.00 sec) mysql> </code></pre> <h2 id="次回"><a href="#%E6%AC%A1%E5%9B%9E">次回</a></h2> <p>PLCのデータをデータベースにJSONで書きました。もう次回は不要かもしれませんが、せっかくデータベースに書いたので、次回はこれをブラウザに表示させます。くどいなあ。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18254 2022-07-21T09:17:30+09:00 2022-07-23T23:37:55+09:00 https://crieit.net/posts/PLC-JSON-4 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 初回から前回にかけては、ゲートウェイを使って、PLCから取得したデータをMQTTでブローカーに送信するまでを実装しました。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a></p> <p>今回はデータベースを準備します。<br /> <a href="https://crieit.now.sh/upload_images/348f9673721149a9206023ad2b3ca8e062d7eb7ab1449.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/348f9673721149a9206023ad2b3ca8e062d7eb7ab1449.png?mw=700" alt="image" /></a></p> <h2 id="データベース"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9">データベース</a></h2> <p>このサンプルではMySQLを使用します。MySQLでなければいけない理由はまったくありません。MySQLを選択した理由は、そこ(私の開発環境、作業場ですね)で動いていたからです。<br /> テーマを「JSONで保存」とした以上は、JSON型をサポートしているDBMSである必要はあるのですが、MySQL以外にもJSON型をサポートしているDBMSはあります。</p> <p>ユーザとデータベースは事前に作成されていたものを使います。これらの作成については解説はいたしません。</p> <p>MySQLにおける、データベースとユーザの作成については、こちらを参照ください。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.dbonline.jp/mysql/database/">データベースの作成</a><br /> <a target="_blank" rel="nofollow noopener" href="https://www.dbonline.jp/mysql/user/">ユーザーの作成</a></p> <h2 id="テーブルを作成する"><a href="#%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B">テーブルを作成する</a></h2> <p>任意のユーザとデータベースでテーブルを作成します。</p> <pre><code>hoge@localhost C:\Users\hoge>mysql -u hoge -p Enter password: ******* Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 9 Server version: 8.0.29 MySQL Community Server - GPL Copyright (c) 2000, 2022, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use hoge Database changed mysql> </code></pre> <p>データを保存するためのテーブルをひとつだけ作成します。</p> <pre><code>mysql> create table from_plc ( -> body json, -> time_insert timestamp default current_timestamp -> ); Query OK, 0 rows affected (0.17 sec) mysql> show full columns from from_plc; +-------------+-----------+-----------+------+-----+-------------------+-------------------+---------------------------------+---------+ | Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment | +-------------+-----------+-----------+------+-----+-------------------+-------------------+---------------------------------+---------+ | body | json | NULL | YES | | NULL | | select,insert,update,references | | | time_insert | timestamp | NULL | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | select,insert,update,references | | +-------------+-----------+-----------+------+-----+-------------------+-------------------+---------------------------------+---------+ 2 rows in set (0.07 sec) mysql> </code></pre> <h2 id="次回"><a href="#%E6%AC%A1%E5%9B%9E">次回</a></h2> <p>続きは次回とさせていただきます。<br /> MySQLのセットアップを行われた方がいるとすれば、その方にとっては、この記事の中で最も時間を要した回であったはずです。そういった方に配慮し、今回は短めとし続きは次回とさせていただきます。<br /> 私の記事は先を急ぎませんので。<br /> 次回は、いよいよデータをデータベースに書き込みます。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18252 2022-07-20T08:40:18+09:00 2022-07-23T23:36:59+09:00 https://crieit.net/posts/PLC-JSON-3 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> 前回は、ゲートウェイを用いてPLCからデータを取得し、ゲートウェイのメモリに保存しました。<br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a></p> <p>今回はこのデータをサーバに送信します。<br /> <a href="https://crieit.now.sh/upload_images/99f8027812fe841fafbb82cb316db3e062d514f019ff6.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/99f8027812fe841fafbb82cb316db3e062d514f019ff6.png?mw=700" alt="image" /></a></p> <p>サンプルで使用するゲートウェイは、引き続きWEINTEK製ゲートウェイの開発環境であるEasyBuilderのシミュレーターです。WEINTEK製ゲートウェイはサーバへのデータ送信に2種類のインタフェースを持っており、そのひとつがMQTTです。サンプルではMQTTを使用してサーバにデータを送信します。</p> <p>IT技術より生産現場の技術に強い方は、"MQTT"とか登場した時点で「もういやだな」となるかもしれません。そういった方はロガーの類が欲しくなりますよね。わかります。この記事ではロガーの類はまったく登場しません。この先を読み進めるかどうか、ご自由にご判断ください。</p> <p>サーバ側にはMQTTのブローカーが必要となります。サンプルでは、MQTTブローカーとしてMosquittoを使用します。<br /> <a href="https://crieit.now.sh/upload_images/ad4d70ef98a1d741deedb1e19d7eef3762d52f8173167.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ad4d70ef98a1d741deedb1e19d7eef3762d52f8173167.png?mw=700" alt="image" /></a></p> <p>Mosquittoはテストの場面で必要となります。サンプルの実装に加えて、テストをしてみたいという方は、自己責任でMosquitto、あるいは他のMQTTブローカーを準備してください。Mosquittoの準備については以下を参照してください。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/koichi_baseball/items/8fa9e0bdbe6d0aebe57d">【MQTT】MQTTの導入 mosquittoのインストール/動作確認まで</a></p> <h2 id="ゲートウェイのMQTT送信設定"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AEMQTT%E9%80%81%E4%BF%A1%E8%A8%AD%E5%AE%9A">ゲートウェイのMQTT送信設定</a></h2> <p>データをサーバに送信するために、前回まで作成したプロジェクトに設定を加えます。</p> <p>EasyBuilderのメニュー[IIoT/エネルギー]-[MQTT]をクリックすます。<br /> <a href="https://crieit.now.sh/upload_images/27403bdd1beb17ed91a9e1faad0c1ca462d5180119baf.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/27403bdd1beb17ed91a9e1faad0c1ca462d5180119baf.png?mw=700" alt="image" /></a></p> <p>[有効にする]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/419a1bed20d2da61b4a260fcfaa1a92f62d518b58b47d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/419a1bed20d2da61b4a260fcfaa1a92f62d518b58b47d.png?mw=700" alt="image" /></a></p> <p>[一般的な属性]はデフォルトのままです。サンプルでは、ゲートウェイのシミュレーターと同じWindowsでMosquittoを動作させるので、IPアドレスは127.0.0.1のままです。[OK]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/3643faef7ae563403b18ecd92652d34562d5191b1eb6a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/3643faef7ae563403b18ecd92652d34562d5191b1eb6a.png?mw=700" alt="image" /></a></p> <p>[トピック発行者]タブで[新規作成]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/0602c7b6837e71a182c385cd85680f2862d51a01c7f24.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0602c7b6837e71a182c385cd85680f2862d51a01c7f24.png?mw=700" alt="image" /></a><br /> <a href="https://crieit.now.sh/upload_images/999d37a64349d3064b39bf08d0b2694f62d51a4c34273.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/999d37a64349d3064b39bf08d0b2694f62d51a4c34273.png?mw=700" alt="image" /></a></p> <p>[一般的な属性]タブで以下のように設定します。<br /> 中ほどの設定、[アドレス]のLBと0は、LB0がONになったらMQTTでサーバにデータを送信する、という意図です。<br /> <a href="https://crieit.now.sh/upload_images/01b4e17a1817470a004336900fa7ffbb62d51b4209fc2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/01b4e17a1817470a004336900fa7ffbb62d51b4209fc2.png?mw=700" alt="image" /></a></p> <p>[アドレス]タブに移り、[新規作成]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/59910b8f3292a98b49714a41816f8c3762d51c263f6ec.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59910b8f3292a98b49714a41816f8c3762d51c263f6ec.png?mw=700" alt="image" /></a></p> <p><a href="https://crieit.now.sh/upload_images/58b56e5ee811a948f574326534769a5762d51c2fa8f19.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/58b56e5ee811a948f574326534769a5762d51c2fa8f19.png?mw=700" alt="image" /></a></p> <p>ダイアログに以下のように設定し、[OK]ボタンをクリックします。<br /> [アドレス]の設定LWと0は、LW0にあるデータを送信する、という意図です。LW0には、PLCから取得したデータが置かれています。<br /> <a href="https://crieit.now.sh/upload_images/de2bc7ffeecf75b76dec7522f7a27fd162d51cb082328.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/de2bc7ffeecf75b76dec7522f7a27fd162d51cb082328.png?mw=700" alt="image" /></a></p> <p>[MQTTトピック発行者オブジェクト属性]ウィンドウの[OK]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/bc638bc4ee1e717109c9a3a6157e5d5f62d51d48b7489.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/bc638bc4ee1e717109c9a3a6157e5d5f62d51d48b7489.png?mw=700" alt="image" /></a></p> <p>[MQTT]ウィンドウの[終了]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/9534a197b16903a10274457bdd2e71e662d51dbac95f8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/9534a197b16903a10274457bdd2e71e662d51dbac95f8.png?mw=700" alt="image" /></a></p> <p>以上で、ゲートウェイのMQTT設定は完了です。</p> <h2 id="ゲートウェイのプログラミング"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0">ゲートウェイのプログラミング</a></h2> <p>前回作成したゲートウェイのプログラムに、若干のステップを追加します。</p> <p>EasyBuilderのメニュー[プロジェクト]-[マクロ]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/b6df62bab4d0a16cee1095a55dcb836f62d5216eb34e4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b6df62bab4d0a16cee1095a55dcb836f62d5216eb34e4.png?mw=700" alt="image" /></a><br /> <a href="https://crieit.now.sh/upload_images/5f0828812556ea6a775135414a365cc362d52185a7fbe.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/5f0828812556ea6a775135414a365cc362d52185a7fbe.png?mw=700" alt="image" /></a></p> <p>[ID : 001]をダブル・クリックします。<br /> <a href="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d52233bc50d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d52233bc50d.png?mw=700" alt="image" /></a></p> <p>以下のように、ステップを追加します。<br /> <a href="https://crieit.now.sh/upload_images/48e72c305c89ae10a73ee74cb389156462d5295243ba1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/48e72c305c89ae10a73ee74cb389156462d5295243ba1.png?mw=700" alt="image" /></a><br /> 6〜10行目では、今回取得したデータと前回取得したデータを比較して、同じデータであったら何もしない、と書いています。業務上、連続する同じデータは不要という場合に、サーバに無駄なデータを送信しません。<br /> 13〜14行目は、MQTT送信をキックするために、ゲートウェイのLB0をオンにします。LB0はMQTT送信後に自動でオフになります(そのようにMQTTを設定しました)。</p> <p>コピーしたい方はこちらからどうぞ。</p> <pre><code>macro_command main()         unsigned short        x40001 = 0         GetData(x40001, "MODBUS TCP/IP (32-Bit)", 4x, 1, 1)                  unsigned short        LW0 = 0         GetData(LW0, "Local HMI", LW, 0, 1)         if x40001 == LW0 then                 return         end if         SetData(x40001, "Local HMI", LW, 0, 1)         bool        LB0 = 1                 SetData(LB0, "Local HMI", LB, 0, 1) end macro_command </code></pre> <p>エディタの[閉じる]ボタンをクリックします。<br /> <a href="https://crieit.now.sh/upload_images/901952ea06c21742a0a40d878071bca462d56c6e2048c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/901952ea06c21742a0a40d878071bca462d56c6e2048c.png?mw=700" alt="image" /></a></p> <p>[コンパイルが成功]に[ID : 000]があることを確認します。<br /> <a href="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d56ca4cea61.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/62d62aa6b4b8125fc109c443ad5f47b262d56ca4cea61.png?mw=700" alt="image" /></a></p> <blockquote> <h3 id="連続する同じデータは不要"><a href="#%E9%80%A3%E7%B6%9A%E3%81%99%E3%82%8B%E5%90%8C%E3%81%98%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AF%E4%B8%8D%E8%A6%81">連続する同じデータは不要</a></h3> <p>連続する同じデータは不要という仕様は、よくある仕様です。例えば、工場の生産マシンで生産される製品の数をPLCで数えている場合、そのカウンタがこれに該当します。変化のないカウント値は不要なはずです。この不要なデータをネットワークやサーバに垂れ流すと、ネットワークやサーバにとって無駄な負荷となります。データベースのサイズも無駄に膨れ上がります。「IoTのデータは無駄なデータばかりだけど、そういうものだ」という誤った悟りを持った設計者を多く見かけますが、実は、設計次第でエレガントなシステムになります。想像が足りないだけなのです。<br /> 時系列データベースが提供する重複排除機能などは、あったほうが便利、という程度です。排除する前に発生させなければ良いのです。ただし、ロガーなどを使って無秩序に記録したデータをデータベースにロードする、というような場合は、不要なデータの排除に苦労します。<br /> 連続する同じデータをサーバ側で捨てるテクニックもあります。これについては機会があれば、別途解説をさせていただきます。</p> </blockquote> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>テストには、MQTTブローカーと、一時的にテストで使用するMQTTの汎用サブスクライバーが必要となります。<br /> これらは、Mosquittoが提供してくれます。Mosquittoの準備については以下を参照してください。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/koichi_baseball/items/8fa9e0bdbe6d0aebe57d">【MQTT】MQTTの導入 mosquittoのインストール/動作確認まで</a></p> <p>私の開発・テスト環境では、MosquittoブローカーはWindowsの設定で自動起動にしてあり、常に実行中です。<br /> <a href="https://crieit.now.sh/upload_images/d3c132e88775428a7334c810a8606fc362d5203d78fd4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d3c132e88775428a7334c810a8606fc362d5203d78fd4.png?mw=700" alt="image" /></a></p> <blockquote> <p>注意: 他のサーバ上のMosquittoへ送信する場合には注意が必要です。Mosquittoは、標準の設定では外部からのメッセージを受け付けません。この場合は、外部からのメッセージを受け付けるよう、Mosquittoの設定を変更する必要があります。</p> </blockquote> <p>受信側となるMQTTサブスクライバーを起動しておきます。<br /> Mosquittoにはコマンド起動可能な簡単なサブスクライバーが付属しています。Windowsでは、このコマンドはMosquittoのインストール・フォルダにあります。<br /> 以下のように起動しておきます。</p> <pre><code>c:\Program Files\mosquitto>mosquitto_sub -h localhost -t sample </code></pre> <p>オプション -h localhost は、localhost上のブローカーから受信することを示しています。-t sample はトピック名を示しており、ゲートウェイのMQTT設定で設定したトピック名と同じである必要があります。mosquitto_sub はトピックsampleのメッセージをひたすら待ち続けます。</p> <p>前回のテストと同様に、Modbus/TCPシミュレーター"mod_RSsim.exe"を起動します。</p> <blockquote> <p>注意: "mod_RSsim.exe"は連続起動に時間制限がある点に注意してください。制限を超えた場合は再起動させます。</p> </blockquote> <p><a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d52790afb85.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d52790afb85.png?mw=700" alt="image" /></a></p> <p>EasyBuilderのメニュー[プロジェクト]-[オンラインシミュレーション]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/439fd0e9432caba5e9ed0546875106a462d528085da76.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/439fd0e9432caba5e9ed0546875106a462d528085da76.png?mw=700" alt="image" /></a></p> <p>シミュレーターが起動したことを確認します。<br /> <a href="https://crieit.now.sh/upload_images/75590b5b1988d3633f2403c0eda4c4f762d5285c8e0d4.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/75590b5b1988d3633f2403c0eda4c4f762d5285c8e0d4.png?mw=700" alt="image" /></a></p> <p>前回のテストと同様に、Mdobus/TCPシミュレーターのアドレス40001のデータを任意の数値に書き換えます。<br /> <a href="https://crieit.now.sh/upload_images/117891e3a7d9cfb3ad2aee7bc5db0e2662d5287c3564a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/117891e3a7d9cfb3ad2aee7bc5db0e2662d5287c3564a.png?mw=700" alt="image" /></a></p> <p>Mosquittoのサブスクライバーが、受信したデータをコンソールに出力します。<br /> <a href="https://crieit.now.sh/upload_images/e6004c9810a653ca72e7b22384c6d9b862d525e0e1ffc.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e6004c9810a653ca72e7b22384c6d9b862d525e0e1ffc.png?mw=700" alt="image" /></a></p> <p>ゲートウェイのプログラムで、データに変化があった場合にのみ送信、としているため同じデータが次々と送られてくることはありません。ゲートウェイを単なるプロトコル変換器だと考えているなら、それは誤りです。</p> <p>ゲートウェイのMQTT設定にて、タイムスタンプを付与するよう設定したため、tsという名前でゲートウェイの時刻が送られてきます。</p> <blockquote> <p>このゲートウェイの時刻はサーバの時刻と必ずしも一致しない、という点を設計上留意してください。時刻の取扱いについては別途、解説をさせていただきます。</p> </blockquote> <p>以上でテストは終了です。</p> <h2 id="次回"><a href="#%E6%AC%A1%E5%9B%9E">次回</a></h2> <p>続きは次回とさせていただきます。<br /> 次回は、データベースを準備します。</p> <p><a href="https://crieit.net/posts/PLC-JSON-1">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18249 2022-07-18T11:42:52+09:00 2022-07-23T23:35:09+09:00 https://crieit.net/posts/PLC-JSON-1 PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (1) <p>PLCからゲートウェイでデータを取得し、データベースにJSONで保存します。複数回に分けて、サンプルを用いて解説します。<br /> <a href="https://crieit.now.sh/upload_images/4bef24e301bab8c385bc5659d66fab1262d4b8451782b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4bef24e301bab8c385bc5659d66fab1262d4b8451782b.png?mw=700" alt="image" /></a><br /> 初回は、ゲートウェイでPLCのデータを取得します。<br /> <a href="https://crieit.now.sh/upload_images/c2c94364c86325cb400ed616c683221562d4b8537ad2c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c2c94364c86325cb400ed616c683221562d4b8537ad2c.png?mw=700" alt="image" /></a><br /> サンプルは、Windowsがあれば環境の準備と動作が可能です。よろしければ、自己責任のもとで環境を準備し、サンプルを動作させてみてください。</p> <h2 id="PLC"><a href="#PLC">PLC</a></h2> <p>この解説でのサンプルでは、PLCの代わりにModbus/TCPシミュレーターを使用します。これにより、PLCの本物がなくても、実装の学習とテストが可能となります。<br /> 今回使用したModbus/TCPシミュレーターは、貴重なmod_RSsim.exeです。もちろん、他のシミュレータでも構いません。</p> <ul> <li>Modbus/TCPシミュレーター: MOD-RSsim Version 8.20</li> </ul> <p><a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4afc50041e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4afc50041e.png?mw=700" alt="image" /></a></p> <h2 id="ゲートウェイ"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4">ゲートウェイ</a></h2> <p>この解説のサンプルでは、WEINTEK製ゲートウェイを想定します。WEINTEK製ゲートウェイのソフトウェア開発ツールは、Windows上でソフトウェアを書くことができることに加え、ゲートウェイの動きをシミュレートすることが出来ます。これにより、ゲートウェイの本物がなくても、実装の学習とテストが可能となります。</p> <ul> <li>WEINTEK製ゲートウェイのソフトウェア開発環境: EasyBuilder Pro</li> </ul> <p>EasyBuilderはWEINTEKのサイトからダウンロードすることが出来ます。<br /> <a target="_blank" rel="nofollow noopener" href="https://www.weintek.com/globalw/Download/Download.aspx">https://www.weintek.com/globalw/Download/Download.aspx</a></p> <h2 id="ゲートウェイの設定とプログラミング"><a href="#%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%A8%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0">ゲートウェイの設定とプログラミング</a></h2> <p>ゲートウェイは各社、各モデルで実装のスタイルが大きく異なります。しかし、この解説では、多くのゲートウェイでの機能実装が可能となるよう勘案し、よりシンプルな手順になるよう考慮しています。<br /> 前述のとおり、この解説では、ゲートウェイの本物を使用しません。代わりにEasyBuilderという、WEINTEK製ゲートウェイの開発環境を使用します。</p> <p>EasyBuilder Pro を起動します。<br /> <a href="https://crieit.now.sh/upload_images/70b6249e55a43645fa24782555dba08a62d4bfe2afa50.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/70b6249e55a43645fa24782555dba08a62d4bfe2afa50.png?mw=700" alt="image" /></a></p> <p>プロジェクトを新たに作成します。<br /> これから行う設定が、どのゲートウェイの設定であるかをゲートウェイのモデルの一覧から選択します。このサンプルでは、最もシンプルなモデル"cMT-G01"を選択します。<br /> <a href="https://crieit.now.sh/upload_images/73133d6f86ad19a49ce3d64ec95ce1a662d4c0da0fe55.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/73133d6f86ad19a49ce3d64ec95ce1a662d4c0da0fe55.png?mw=700" alt="image" /></a><br /> "cMT-G01"とはこのようなハードウェアです。<br /> <a target="_blank" rel="nofollow noopener" href="https://dl.weintek.com/public/cMT/eng/Datasheet/cMT-G01_Datasheet_ENG.pdf">cMT-G01</a></p> <p>このゲートウェイが対象にするPLCを設定します。メニュー[ホーム]-[システムパラメータ]を選択します。<br /> <a href="https://crieit.now.sh/upload_images/e1fd4a5385cd74a43bada117619cb52762d4c1ed7671d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e1fd4a5385cd74a43bada117619cb52762d4c1ed7671d.png?mw=700" alt="image" /></a></p> <p>[装置]タブを選択します。<br /> <a href="https://crieit.now.sh/upload_images/173c66d906d17f0d2905b9713ccd038c62d4c305cc7ca.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/173c66d906d17f0d2905b9713ccd038c62d4c305cc7ca.png?mw=700" alt="image" /></a></p> <p>[装置/サーバを新規追加]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/116c9134b0a53cfc0182069b0ee3f37a62d4e5e3e346e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/116c9134b0a53cfc0182069b0ee3f37a62d4e5e3e346e.png?mw=700" alt="image" /></a></p> <p>以下のように設定します。<br /> <a href="https://crieit.now.sh/upload_images/b4813a2fca2935a56983ddc1dfcefddc62d4c2a3aec24.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b4813a2fca2935a56983ddc1dfcefddc62d4c2a3aec24.png?mw=700" alt="image" /></a><br /> PLCに相当するModbus/TCPシミュレータは、EasyBuilderと同じWindowsマシン上で稼働させる予定です。このため装置(=PLC=Modbus/TCPシミュレータ)のIPアドレスを127.0.0.1にしています。<br /> 装置のポートはModbus/TCPの標準である502を選択しています。</p> <blockquote> <p>上記設定は、この解説におけるサンプルのための設定です。現実は、対象とするPLCやリモートIOなどに応じた設定が必要となります。<br /> 例えば、対象の装置が三菱電機のMELSECであれば、該当のモデルを選択します。下は、Mitsubishi QJ71E71の例です。<br /> <a href="https://crieit.now.sh/upload_images/cf6a77fc1f36d91ac1b06786216393f462d634aac0aa8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/cf6a77fc1f36d91ac1b06786216393f462d634aac0aa8.png?mw=700" alt="Mitsubishi QJ71E71 の例" /></a></p> </blockquote> <h2 id="テスト"><a href="#%E3%83%86%E3%82%B9%E3%83%88">テスト</a></h2> <p>ここまでで、PLC(=Modbus/TCPシミュレータ)に接続する設定が終わりました。接続のテストをします。</p> <p>mod_RSsim.exeを起動しておきます。<br /> <a href="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4c596c61b2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/59603780d55c186e1a70e1b112539c5562d4c596c61b2.png?mw=700" alt="image" /></a></p> <p>次はEasyBuilderです。<br /> もちろん、これまでの設定をゲートウェイ(cTM-G01)にロードしてゲートウェイを動かすことも可能ですが、今回はシミュレーターを使います。<br /> メニュー[プロジェクト]-[オンラインシミュレーション]をクリックします。<br /> <a href="https://crieit.now.sh/upload_images/e57da9ac757974ac53d74202ab3f186962d4c5d9df4b8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e57da9ac757974ac53d74202ab3f186962d4c5d9df4b8.png?mw=700" alt="image" /></a></p> <p>シミュレーターが起動します。"MODBUS TCP/IP (32-Bit)"に接続済みであるように見えますが、これはあてにはなりません。<br /> <a href="https://crieit.now.sh/upload_images/2b4ac9177034a63ae4627b253d2cade562d4c6d9f2b98.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2b4ac9177034a63ae4627b253d2cade562d4c6d9f2b98.png?mw=700" alt="image" /></a></p> <p>Modbus/TCPシミュレーターのウィンドウの左上にある"Connected"の表示を確認します。ゲートウェイからの接続が行われていれば、[1/10]となります。<br /> <a href="https://crieit.now.sh/upload_images/320155af5baceef2c37476485fd2741962d4e7455062b.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/320155af5baceef2c37476485fd2741962d4e7455062b.png?mw=700" alt="image" /></a></p> <h2 id="次回へ"><a href="#%E6%AC%A1%E5%9B%9E%E3%81%B8">次回へ</a></h2> <p>続きは次回とさせていただきます。<br /> 初心者の方々のために、ゆっくりとマッタリと進めさせていただきます。<br /> 次回は、ゲートウェイによるPLCからのデータ取得です。</p> <p><a href="https://crieit.net/posts/PLC-JSON-2">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (2)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-3">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (3)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-4">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (4)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-5">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (5)</a><br /> <a href="https://crieit.net/posts/PLC-JSON-6">PLCからゲートウェイでデータを取得しデータベースにJSONで保存 (6)</a></p> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18241 2022-07-16T02:44:09+09:00 2022-07-16T03:56:58+09:00 https://crieit.net/posts/PHP-62d1a769cd327 PHP - とある月の月末の日付を取得する <p>少々まわりくどいようですが、これが安全な手続きのうちのひとつです。</p> <h2 id="サンプル"><a href="#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB">サンプル</a></h2> <pre><code><?php // test.php $str = '2022-07-16'; $str = date('Y-m-01', strtotime('1 month', strtotime($str))); echo date('Y-m-d', strtotime('-1 day', strtotime($str))); ?> </code></pre> <p>テスト</p> <pre><code>$ php -f test.php 2022-07-31 $ </code></pre> COOL MAGIC PRODUCTS tag:crieit.net,2005:PublicArticle/18234 2022-07-12T23:45:36+09:00 2022-07-12T23:45:36+09:00 https://crieit.net/posts/mysql80-with-recursive-in-standalone-eloquent-20220712 MySQL8.0 の再帰クエリを単独の Eloquent で使ってみる <p>ちょうどやりたいことができそうだったので、 MySQL8.0 で新しく追加された再帰クエリを使ってみました。なお、単独インストールした Eloquent での使用を想定しているので、リレーションの機能をそのまま使うのは難しそうなケースでした。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>内容としてはありがちな部署マスタのようなもので、親子関係まで見たいと考えたためでした。</p> <div class="table-responsive"><table> <thead> <tr> <th align="right">id</th> <th align="right">parent</th> <th align="left">name</th> </tr> </thead> <tbody> <tr> <td align="right">1</td> <td align="right">0</td> <td align="left">営業部</td> </tr> <tr> <td align="right">2</td> <td align="right">1</td> <td align="left">営業1課</td> </tr> <tr> <td align="right">3</td> <td align="right">1</td> <td align="left">営業2課</td> </tr> <tr> <td align="right">4</td> <td align="right">0</td> <td align="left">システム部</td> </tr> <tr> <td align="right">5</td> <td align="right">4</td> <td align="left">開発課</td> </tr> <tr> <td align="right">6</td> <td align="right">4</td> <td align="left">運用保守課</td> </tr> <tr> <td align="right">7</td> <td align="right">5</td> <td align="left">フロントエンドグループ</td> </tr> <tr> <td align="right">8</td> <td align="right">5</td> <td align="left">バックエンドグループ</td> </tr> </tbody> </table></div> <p>イメージとしてはこんなイメージのテーブルがあったとして、例えば「システム部」と指定した場合、「開発課」「運用保守課」「フロントエンドグループ」「バックエンドグループ」のIDを子部署として参照したい、というようなケースです。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="php">$hogeObjectsArray = $this->dbConnect->connection('hogera') ->select(" WITH recursive child( depth, id, parent, name ) AS ( SELECT 0, id, parent, name FROM hoge WHERE id = {$foo} UNION ALL SELECT child.depth + 1, hoge.id, hoge.parent, hoge.name FROM hoge, child WHERE hoge.parent = child.id ) SELECT depth, id, parent, name FROM child ORDER BY depth "); </code></pre> <p>最終的には上述の形で取得できました。</p> <p><code>->table($this->table)</code> 等でテーブルを指定せず、いきなり <code>->select()</code> しているのがミソです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="with recursive"><a href="#with+recursive">with recursive</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://codezine.jp/article/detail/2679">MySQL 8.0の再帰With句のサンプル集 (1\/3):CodeZine(コードジン)</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/rstliz/articles/65278841ac26f7">MySQL 再帰クエリで階層構造のデータを一度に取得</a></li> </ul> <h3 id="単独の Eloquent 内で with recursive を使用したい"><a href="#%E5%8D%98%E7%8B%AC%E3%81%AE+Eloquent+%E5%86%85%E3%81%A7+with+recursive+%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E3%81%84">単独の Eloquent 内で with recursive を使用したい</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/okumurakengo/items/0670ce39d758ed65e8e7">100行ぐらいのSQLをLaravelのEloquent\/Query Builderで頑張る - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://leben.mobi/blog/laravel_raw_sql/php/">Laravel5で生のSQLを実行する方法</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Yinaura/items/562079d937021f1fc30a">MySQL — サブクエリに AS を付けないとエラーを起こす | Every derived table must have its own alias - Qiita</a></li> </ul> <h4 id="メソッド確認"><a href="#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E7%A2%BA%E8%AA%8D">メソッド確認</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Capsule/Manager.html#method_connection">Illuminate\Database\Capsule\Manager | Laravel API</a></li> <li>query(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Connection.html#method_query">Illuminate\Database\Connection | Laravel API</a></li> <li>Builder: <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html">Illuminate\Database\Query\Builder | Laravel API</a></li> <li>selectRaw(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_selectRaw">Illuminate\Database\Query\Builder | Laravel API</a></li> <li>select(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_select">Illuminate\Database\Query\Builder | Laravel API</a></li> <li>raw(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Connection.html#method_raw">Illuminate\Database\Connection | Laravel API</a></li> </ul> <h4 id="unionAll"><a href="#unionAll">unionAll</a></h4> <ul> <li>unionAll(): <a target="_blank" rel="nofollow noopener" href="https://laravel.com/api/5.8/Illuminate/Database/Query/Builder.html#method_unionAll">Illuminate\Database\Query\Builder | Laravel API</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://mysql.sql55.com/sql/mysql-union.php">MySQL の UNION \/ UNION ALL - MySQL の基礎 - MySQL 入門</a></li> </ul> <h4 id="(未使用) 外部パッケージ"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E5%A4%96%E9%83%A8%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8">(未使用) 外部パッケージ</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/soda-framework/eloquent-closure">GitHub - soda-framework\/eloquent-closure</a></li> </ul> <h4 id="(未使用) もはや生PDOに回帰するか"><a href="#%28%E6%9C%AA%E4%BD%BF%E7%94%A8%29+%E3%82%82%E3%81%AF%E3%82%84%E7%94%9FPDO%E3%81%AB%E5%9B%9E%E5%B8%B0%E3%81%99%E3%82%8B%E3%81%8B">(未使用) もはや生PDOに回帰するか</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/jjongs08/items/f53bc04d582e63e7ad10">Laravelでクエリ結果を配列で取得する方法 - Qiita</a></li> </ul> <h4 id="whereIn()"><a href="#whereIn%28%29">whereIn()</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/miriwo/items/553dccbae72a25b5467b">laravel クエリビルダ whereInを使って賢く取得情報を絞ろう - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18211 2022-06-09T23:58:40+09:00 2022-06-09T23:58:40+09:00 https://crieit.net/posts/eloquent-use-db-raw-a-facade-root-has-not-been-set-error-20220610 Eloquent 単独で COUNT(*) を使用したいが DB:raw() を使用すると A facade root has not been set. エラーになる <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>Eloquent 単独利用しているプログラムの中で COUNT(*) を使用したくなったのでその方法をメモ。</p> <p>単純に件数をカウントしてその数値を使いたいだけであれば</p> <pre><code class="php">$data_count = $dbConnect->table('hoge') ->count(); </code></pre> <p>で済ませることができます。が、今回はサブクエリとして使用した方がコード的にすっきりしそうな状況でした。</p> <p>先の方法ではコード上は関連性のない別のクエリをもう1つ記述しなければならないので如何なものかと思い……。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://awesome-programmer.hateblo.jp/entry/2019/07/03/162835">LaravelのEloquentで集計関数Count()などを使うとき - バックエンドとフロントエンドを行き来するWEBプログラマ―のメモ帳</a></li> </ul> <p>検索したところ、 SQL で <code>COUNT(*)</code> を使用する場合は <code>DB:raw()</code> を使うとのこと。</p> <p>ただし、 Laravel の中ならばいざ知らず今回は Eloquent 単独使用なので、 <code>DB</code> なるクラスはどこから来るのか……となってしまいました。しかも <code>::raw()</code> は static メソッドですし。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://readouble.com/laravel/8.x/ja/queries.html">データベース:クエリビルダ 8.x Laravel</a></li> </ul> <p>引き続き検索すると、 <code>DB</code> はファサードでした。</p> <p>しかし、 Laravel 公式のサンプルのように</p> <pre><code class="php">use Illuminate\Support\Facades\DB; </code></pre> <p>を追加すると、</p> <blockquote> <p>A facade root has not been set.</p> </blockquote> <p>というエラーが発生して怒られてしまいます。</p> <p>通常の Laravel 内で使用する場合はインスタンスとの紐付けが上手く行われますが、今回は Eloquent のみの単体使用なのでこのパターンではできそうにないように見受けられました (できたとしても素直に Laravel を使った方が早そう)。</p> <p>最終的には</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/s-higuma/items/ab8fa110b13301d0c959">Eloquentを単体で導入している時にDBファサードを使用する方法 - Qiita</a></li> </ul> <p>この記事の方法で</p> <pre><code class="php">use \Illuminate\Database\Capsule\Manager as DB; </code></pre> <p>を <code>DB::raw()</code> を使うクラスのファイルの中で再度読み込ませる (別のファイルでDB接続用のインスタンスを生成していて、それを DI していたので今回は「再度読み込ませる」という表現) ことで乗り越えることができました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="count()"><a href="#count%28%29">count()</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/zaburo/items/d665804f8ea850502c64">Laravelでの基本的なリレーションシップもしくはJOIN - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.84kure.com/blog/2017/07/12/laravel-eloquent%E3%81%A7%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%99%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%AE%E6%B3%A8%E6%84%8F/">[Laravel] Eloquentでカウントするときの注意 – 端くれプログラマの備忘録</a></li> </ul> <h3 id="DB::raw()"><a href="#DB%3A%3Araw%28%29">DB::raw()</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://awesome-programmer.hateblo.jp/entry/2019/07/03/162835">LaravelのEloquentで集計関数Count()などを使うとき - バックエンドとフロントエンドを行き来するWEBプログラマ―のメモ帳</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://readouble.com/laravel/8.x/ja/queries.html">データベース:クエリビルダ 8.x Laravel</a></li> </ul> <h3 id="A facade root has not been set. エラー"><a href="#A+facade+root+has+not+been+set.+%E3%82%A8%E3%83%A9%E3%83%BC">A facade root has not been set. エラー</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://re-engines.com/2020/05/11/laravel-facade/">LaravelのFacade(ファサード)とは? 何気なく使用していた裏側の仕組みを解説! | RE:ENGINES</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.wakuwakubank.com/posts/453-laravel-sql-basic/">Laravel|DBデータ操作(DBファサード, クエリビルダ, Eloquent) - わくわくBank</a> (バージョン古い)</li> <li><a target="_blank" rel="nofollow noopener" href="https://matchingood-engineer.hatenablog.com/">エンジニアブログ</a> (バージョン古い)</li> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/22925451/how-can-i-query-raw-via-eloquent">php - How can I query raw via Eloquent? - Stack Overflow</a> (バージョン古い)</li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/s-higuma/items/ab8fa110b13301d0c959">Eloquentを単体で導入している時にDBファサードを使用する方法 - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18207 2022-05-31T23:48:56+09:00 2022-05-31T23:48:56+09:00 https://crieit.net/posts/stdclass-and-illuminate-support-collection-convert-array-20220601 Illuminate\Support\Collection を配列に変換する <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>Eloquent で取得したデータはそのままだと Object の Collection になっているのですが、配列として扱いたいケースが出てきました。そこで先の Collection を配列に変換するメモを残しておきます。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="php">$employeesCollectionObject = $dbConnect->table($this->table) ->where( [ ['id', '=', $id], ] ) ->select( 'employee_id', 'depart', ) ->get(); $employeesCollectionArray = $employeesCollectionObject->map(function ($item, $key) { return [ 'tori_id' => $item->employee_id, 'domain' => $item->depart, ]; }); </code></pre> <p>例えばこうすることで、</p> <pre><code class="php">object(Illuminate\Support\Collection)[xxx] protected 'items' => array (size=10) 0 => object(stdClass)[yyy] public 'employee_id' => string '001' (length=3) public 'depart' => string 'sales' (length=5) 1 => object(stdClass)[zzz] public 'employee_id' => string '002' (length=3) public 'depart' => string 'accounting' (length=10) 2 => ... </code></pre> <p>このようなコレクション→オブジェクトの配列が</p> <pre><code class="php">object(Illuminate\Support\Collection)[xxx] protected 'items' => array (size=10) 0 => array (size=2) 'employee_id' => string '001' (length=3) 'depart' => string 'sales' (length=5) 1 => array (size=2) 'employee_id' => string '002' (length=3) 'depart' => string 'accounting' (length=10) ... </code></pre> <p>このようなコレクション→連想配列の配列になります。各プロパティへのアクセス等が犠牲になりますが、今回は織り込み済み。</p> <p>さらに <code>->toArray()</code> を使って</p> <pre><code class="php">$employeesCollectionArray = $employeesCollectionObject->map(function ($item, $key) { return [ 'tori_id' => $item->employee_id, 'domain' => $item->depart, ]; }); $customersArray = $employeesCollectionArray->toArray(); </code></pre> <p>こうすることで</p> <pre><code class="php">array (size=10) 0 => array (size=2) 'employee_id' => string '001' (length=3) 'depart' => string 'sales' (length=5) 1 => array (size=2) 'employee_id' => string '002' (length=3) 'depart' => string 'accounting' (length=10) ... </code></pre> <p>配列→連想配列にできました。</p> <p>今回はこれで対応。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://techplay.jp/column/630">Laravel collectionの使い方【初心者向け】|TECH PLAY Magazine [テックプレイマガジン]</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ucan-lab/items/47638a7b52090f59c2bf">Laravel Collection型の配列変換 toArray と all の違い - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18194 2022-05-19T23:55:46+09:00 2022-05-19T23:55:46+09:00 https://crieit.net/posts/php-phoenix-migrate-db-depend-other-db-20220521 (PHP) Phoenix でデータ関係が依存しているDBをマイグレーションする <p><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix">lulco/phoenix</a> でデータ関係が依存しているDBをマイグレーションする方法をメモ。</p> <p>ドキュメントが全然ないので地道にやっていきます……。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <p>早速コードを。</p> <h3 id="phoenix.php"><a href="#phoenix.php">phoenix.php</a></h3> <pre><code class="php">return [ 'migration_dirs' => [ 'first' => __DIR__ . '/migrations/first', 'second' => __DIR__ . '/migrations/second', ], 'environments' => [ 'local' => [ 'adapter' => 'mysql', 'host' => $_ENV['MYSQL_HOST'], 'port' => (int)$_ENV['MYSQL_PORT'], // optional 'username' => $_ENV['MYSQL_USER'], 'password' => $_ENV['MYSQL_PASSWORD'], 'db_name' => $_ENV['MYSQL_DBNAME'], 'charset' => 'utf8mb4', ], 'production' => [ 'adapter' => 'mysql', 'host' => $_ENV['MYSQL_HOST'], 'port' => (int)$_ENV['MYSQL_PORT'], // optional 'username' => $_ENV['MYSQL_USER'], 'password' => $_ENV['MYSQL_PASSWORD'], 'db_name' => $_ENV['MYSQL_DBNAME'], 'charset' => 'utf8mb4', ], ], 'default_environment' => 'local', 'log_table_name' => 'phoenix_log', ]; </code></pre> <p>肝は <code>migration_dirs</code> で <code>first</code> と <code>second</code> でそれぞれ対応するディレクトリを指定しているところ。</p> <h3 id="/migrations/first/hoge.php"><a href="#%2Fmigrations%2Ffirst%2Fhoge.php">/migrations/first/hoge.php</a></h3> <pre><code class="php"><?php namespace migrations; use Phoenix\Database\Element\Index; use Phoenix\Migration\AbstractMigration; class HogeMigration extends AbstractMigration { protected function up(): void { $this->table('hoge') ->addColumn('create_date', 'datetime') ->addColumn('name', 'string') ->create(); // insert $hogeData = [ [ 'name' => 'foo' ], [ 'name' => 'bar' ], [ 'name' => 'buz' ], // 略 ]; $rows = []; foreach ($hogeData as $key => $val) { $rows[] = [ 'create_date' => date('Y-m-d H:i:s'), 'name' => $val['name'], ]; } $this->insert('hoge', $rows); } protected function down(): void { $this->table('hoge') ->drop(); } } </code></pre> <p>まずは最初に <code>hoge</code> というDBを作成し、そこにデータを流し込みます。</p> <h3 id="/migrations/second/fuga.php"><a href="#%2Fmigrations%2Fsecond%2Ffuga.php">/migrations/second/fuga.php</a></h3> <pre><code class="php"><?php namespace migrations; use Phoenix\Database\Element\Index; use Phoenix\Migration\AbstractMigration; class FugaMigration extends AbstractMigration { protected function up(): void { $this->table('fuga') ->addColumn('create_date', 'datetime') ->addColumn('name', 'string') ->addColumn('hoge_id', 'integer') ->create(); // select hoge data $hogeRows = $this->select('SELECT id, name FROM hoge'); // insert $fugaData = [ [ 'name' => 'un' ], [ 'name' => 'deux' ], [ 'name' => 'trois' ], // 略 ]; $rows = []; foreach ($fugaData as $key => $val) { $id = 0; // hoge の name と fuga の name が一致する要素を array_filter() で抽出し、 array_values() で番号を詰める $hogeArray = array_values( array_filter( $hogeRows, function($hogeRow) use ($val) { $needle = mb_strlen(mb_strtolower($val[0])) > 0 ? mb_strtolower($val[0]) : 'NOTHING'; return mb_strpos(mb_strtolower($hogeRow['name']), $needle) !== false; } ) ); // $hogeArray の要素が1つ (一意に定まる) 場合はその値を、そうでない場合はデフォルト値をセット $id = count($hogeArray) === 1 ? (int)$hogeArray[0]['id'] : 0; $rows[] = [ 'create_date' => date('Y-m-d H:i:s'), 'name' => $val[0], 'hoge_id' => $id, // 上述でセットした id を使用 ]; } $this->insert('fuga', $rows); } protected function down(): void { $this->table('fuga') ->drop(); } } </code></pre> <p>次に <code>fuga</code> を作成し、そこに徐に <code>$this->select()</code> で SQL文 を発行、先程流し込んだデータを抽出します。</p> <p>その抽出したデータと <code>fuga</code> に流し込みたいデータを突き合わせて初期データを生成し、それを <code>fuga</code> に流し込む……という算段。</p> <p>これで意図したデータをマイグレーションすることができました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix">GitHub - lulco\/phoenix: Framework agnostic database migrations for PHP.</a> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/lulco/phoenix/blob/master/src/Database/Adapter/PdoAdapter.php">phoenix\/PdoAdapter.php at master · lulco\/phoenix · GitHub</a></li> </ul></li> </ul> <p>ドキュメントがないのでコードを読んで普通に <code>select</code> とか使えそう、と思って試したりしていました。</p> arm-band tag:crieit.net,2005:PublicArticle/18193 2022-05-19T23:50:29+09:00 2022-05-19T23:50:29+09:00 https://crieit.net/posts/eloquent-connect-multiple-db-20220520 Eloquent で複数DBに接続する <p>単独の Eloquent を利用して異なるデータソースにアクセスしてみたのでメモしておきます。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="php"><?php declare(strict_types=1); use Illuminate\Database\Capsule\Manager; class DBConnection { private $db; public function __construct() { $this->db = new Manager; // default $this->db->addConnection([ 'driver' => 'mysql', 'host' => $_ENV['MYSQL1_HOST'], 'database' => $_ENV['MYSQL1_DBNAME'], 'username' => $_ENV['MYSQL1_USER'], 'password' => $_ENV['MYSQL1_PASSWORD'], 'charset' => 'utf8', ]); // second $this->db->addConnection([ 'driver' => 'mysql', 'host' => $_ENV['MYSQL2_HOST'], 'database' => $_ENV['MYSQL2_DBNAME'], 'username' => $_ENV['MYSQL2_USER'], 'password' => $_ENV['MYSQL2_PASSWORD'], 'charset' => 'utf8', ], 'second'); // addConnection() メソッドの第二引数に接続名を指定する (1つ目はなしだと default になる) $this->db->setAsGlobal(); $this->db->bootEloquent(); } // 略 } </code></pre> <p>肝はコメントにも記した通り <code>Manager</code>クラス の <code>addConnection()</code>メソッド について、第二引数に接続名を指定することです (1つ目はなしだと <code>default</code> になり、後で使用する分にも指定は不要) 。</p> <p>あ、しれっと書いていますが <code>dotenv</code> を使用した想定です。</p> <pre><code class="php"><br />$db = new DBConnection(); $secondDBrows = $db->connection('second')->table('hoge_db') ->where( /* 略 */ ) ->select( /* 略 */ ) ->get(); </code></pre> <p>こちらもしれっと書いていますがイメージとしては先程のDB接続用クラス <code>DBConnection</code> をインスタンス化して、その中から <code>connection()</code>メソッド で引数に先程指定した接続名を記述することで二つ目の接続を利用してDBにアクセスできる、ということです。</p> <p>これで意図したことを実装することができました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.bagooon.com/?p=1492">Laravelを使わず、直接PHPからEloquentを使用し、かつ複数のDBに接続する方法。</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://technoledge.net/composer-eloquent-illuminate-database/">LaravelのORM「Eloquent」を単体で使ってスクラッチ開発する | テクナレジ</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18192 2022-05-18T00:05:39+09:00 2022-05-18T00:05:39+09:00 https://crieit.net/posts/php-operating-array-closure-20220519 PHP の array_filter() で条件に一致した配列の要素を抽出する <p>PHP で array.filter のようなことをしたくなったのでメモ。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="php"><?php declare(strict_types=1); function getEmployeeList(string $dept_name): array { return array_values( array_filter( $employees, function($employee) use ($dept_name) { return $employee->getDeptName() === $dept_name; } ) ); } </code></pre> <p>イメージとしてこのような感じで意図した挙動になることを確認しました。</p> <p>やっていることととしては、「ある部署に所属する (部署名 <code>$dept_name</code> で抽出) 社員全員の一覧」を抽出しています。</p> <p>なお、前提として社員データの配列 <code>$employees</code> (各要素が予め定義された <code>Employee</code>オブジェクト) があるものとしています。</p> <p>ところで、この形 Eloquent の複合条件抽出でも見かけました……そう、 <code>array_filter()</code> の第二引数(コールバック関数)がクロージャで、スコープを越えて外部変数を使うために <code>use</code>句 を用いる、という形がそっくりそのままですね。</p> <p>デジャヴも良いところですね。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="arra_values()"><a href="#arra_values%28%29">arra_values()</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.array-values.php">PHP: array_values - Manual</a></li> </ul> <h3 id="array_filter()"><a href="#array_filter%28%29">array_filter()</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.array-filter.php">PHP: array_filter - Manual</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://kimagureneet.hatenablog.com/entry/2017/03/07/003958">【php】配列を複数条件で検索、絞り込みする方法メモ - とりあえずphpとか</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wani-pro.com/func-array_filter/">【php】 配列を条件で絞り込む(array_filter) - わにプログラミング</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18191 2022-05-18T00:03:50+09:00 2022-05-18T00:03:50+09:00 https://crieit.net/posts/eloquent-multiple-where-condition-closure-20220518 Eloquent で複合条件の抽出をする <p>Eloquent を実践的に使い始めたばかりなので初歩的なところですが、複合条件を組み立てるところについてメモ。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <p>やりたいこととしては、 <code>WHERE A AND B AND (C OR D)</code> という条件。</p> <pre><code class="php">$departName = [ '総務部', '営業部', ]; $dataRows = $dbConnect->table('hoge_db') ->where( [ ['user_id', '>', 23], ['age', '<', 42], ] ) ->where(function($query) use ($departName) { $query->orWhere('depart_name', '=', $departName[0]) ->orWhere('depart_name', '=', $departName[1]); }) ->select( 'fuga_db.name', 'fuga_db.attribute', 'fuga_db.parameter' ) ->join('fuga_db', 'hoge_db.user_id', '=', 'fuga_db.employee_id') ->get(); </code></pre> <p><code>$dbConnect</code> にDB接続情報が入ったインスタンスがあるという前提ですが、イメージとしてこのようなコードです。</p> <p>肝は以下。</p> <ul> <li><code>OR</code> 条件の括弧の中身を <code>where()</code>メソッド の中にクロージャとして無名関数で記述する。</li> <li>関数の引数はクエリ。そこに <code>orWhere()</code>メソッド を足していく。</li> <li>クロージャの外部の変数はスコープの関係で Undefined Variable になってしまうので <code>use</code>句 でクロージャ内に持ち込む</li> </ul> <p>これで意図した挙動になることを確認しました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/Hwoa/items/542456b63e51895f9a55">Laravel5で「.. or ...) and (..」みたいな複雑な条件を書く - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/mitashun/items/7b1a6c4bb44acd08472e">【Laravel】EloquentでOR検索する方法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://umebius.com/laravel/querybuilder%E3%81%AB%E3%82%88%E3%82%8Band%E6%A4%9C%E7%B4%A2%E3%81%A8or%E6%A4%9C%E7%B4%A2%E3%81%AE%E8%A4%87%E5%90%88/">Eloquent\/QueryBuilderによるAnd検索とOr検索の複合 - Laravel5開発</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18187 2022-05-10T22:43:05+09:00 2022-05-10T22:43:05+09:00 https://crieit.net/posts/slim-deploy-above-docroot-and-publish-below-subdirectory-20220510 Slim4 をサブディレクトリかつ本体はドキュメントルートより上にデプロイした状態で動作させる <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/slimphp/Slim-Skeleton">slimphp/Slim-Skeleton</a> を サブディレクトリに配置しつつ、 app/ や src/, vendor/ 等のディレクトリはドキュメントルートより上の階層にデプロイした状態で動作させることができたのでメモしておきます。</p> <h2 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h2> <pre><code>var/ └ www/ └ sampleapp/ ├ slim-app/ // Slim4-Skeleton 本体 ( public/ ディレクトリ以外) デプロイ先 │ ├ app/ │ ├ src/ │ ├ vendor/ │ └ etc. │ └ public_html/ // ドキュメントルート └ subdirectory/ // サブディレクトリ └ sub2/ // 2個目のサブディレクトリ ├ .htaccess // Slim4-Skeleton の public/ ディレクトリにある .htaccess └ index.php // Slim4-Skeleton の public/ ディレクトリにある index.php </code></pre> <p>今回の最終的な構成はこのようになりました。</p> <p>この階層構成は</p> <ul> <li>アプリケーション本体はドキュメントルート下には置きたくない</li> <li>複数の Slim アプリケーションを配置する可能性がある</li> <li>サブディレクトリも深い階層に設置する可能性がある</li> </ul> <p>ということで、本体のデプロイディレクトリやドキュメントルート下のサブディレクトリをそれぞれ1つずつ余計に掘っています。</p> <h2 id="手順"><a href="#%E6%89%8B%E9%A0%86">手順</a></h2> <p>すぐ削除するデモ環境なので割と雑に設定を。</p> <pre><code class="bash"># cd /var/www/sampleapp/ # mkdir slim-app # chown apache:apache slim-app/ # chmod 775 slim-app/ # ls -al 合計 16 drwxr-xr-x 4 ADMIN ADMIN 4096 MM月 dd yyyy hh:ii . drwxr-xr-x 18 ADMIN ADMIN 4096 MM月 dd yyyy hh:ii .. drwxrwxr-x 2 apache apache 4096 MM月 dd yyyy hh:ii slim-app drwxrwxr-x 4 apache apache 4096 MM月 dd yyyy hh:ii public_html # # pwd /var/www/sampleapp/ </code></pre> <p>FTP 等で <code>subdirectory/</code>, <code>subdirectory/sub2/</code> を掘ります。</p> <p>この状態になったら、</p> <ul> <li><code>/var/www/sampleapp/slim-app/</code> に <code>public/</code> 以外(他不要なファイル・ディレクトリは都度省く)をアップロード</li> <li><code>/var/www/sampleapp/slim-app/logs/</code> の権限を 777 、 <code>/var/www/sampleapp/slim-app/logs/app.log</code> を 666 にする</li> <li>ソースコードの <code>public/index.php</code> 内で <code>__DIR__ . '/../~~~.php';</code> となっているパスを <code>__DIR__ . '/../../../slim-app/~~~.php';</code> とデプロイ先の環境のパス階層に合わせる</li> <li><code>/var/www/sampleapp/public_html/subdirectory/sub2</code> に <code>public/</code> 以下のファイルをアップロード</li> </ul> <p>として動作することを確認しました。なお、 <code>public/.htaccess</code> はデフォルトのままでOKでした。</p> <p>なお、DBがある場合でDBサーバ上で必要な場合は接続情報を変更すること (接続元Webサーバがローカルの開発環境とサーバで異なるので別権限が必要)。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.asobou.co.jp/blog/web/slim-skeleton">PHPのマイクロフレームワーク「Slim4」の「slim-skeleton」を使用してハマったこと : ビジネスとIT活用に役立つ情報</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/asaokamei/items/b7a1af89db779317fe39">Slim4をサブディレクトリで走らせる方法 - Qiita</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18186 2022-05-10T22:39:48+09:00 2022-05-10T22:39:48+09:00 https://crieit.net/posts/docker-php-7-alpine-use-composer-and-xdebug-20220510 Docker の php:7-alpine イメージで Composer と Xdebug を使えるようにする <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/slimphp/Slim-Skeleton">slimphp/Slim-Skeleton</a> を利用したいと考えました。</p> <p>手元の端末で開発しようとしたところ、 XAMPP に Xdebug を入れ忘れていたことに気付きました。しかも、 XAMPP の PHP のバージョンで Xdebug のインストールが止まってしまいました。</p> <p>そこで、スケルトンプロジェクトに付随している Docker Compose での開発を試みましたが、この Docker Compose 内で pull してくるイメージは Composer も Xdebug もないので入れることにしました。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <h3 id="ディレクトリ構造"><a href="#%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%A7%8B%E9%80%A0">ディレクトリ構造</a></h3> <pre><code>root/ ├ _docker/ // Dockerに関する設定置き場 │ ├ dockerfiles/ │ │ ├ settings/ │ │ │ └ php.ini // PHPの設定 (Xdebug 有効化) │ │ │ │ │ └ Dockerfile │ │ │ ├ mysql/ // MariaDB 用ディレクトリ │ │ └ .gitkeep │ │ │ └ phpmyadmin/ │ ├ conf/ │ │ └ phpmyadmin-misc.ini // phpMyAdmin の設定 (メモリ上限など) │ │ │ └ sessions/ │ └ .gitkeep │ └ docker-compose.yml </code></pre> <p>Docker に関する部分のみ列挙。</p> <h3 id="docker-compose.yml"><a href="#docker-compose.yml">docker-compose.yml</a></h3> <pre><code class="yml">version: '3.7' volumes: logs: driver: local services: slim: build: context: ./_docker/dockerfiles/ dockerfile: Dockerfile working_dir: /var/www command: php -S 0.0.0.0:8080 -t public environment: docker: "true" ports: - 8080:8080 volumes: - .:/var/www - ./logs:/var/www/logs db: image: mariadb restart: always ports: - 3306:3306 volumes: - ./_docker/mysql/mysql:/var/lib/mysql - ./_docker/mysql/initdb.d:/docker-entrypoint-initdb.d environment: - MYSQL_ROOT_PASSWORD=pwd - MYSQL_DATABASE=test - MYSQL_USER=user - MYSQL_PASSWORD=pwd phpmyadmin: image: phpmyadmin/phpmyadmin volumes: - ./_docker/phpmyadmin/sessions:/sessions - ./_docker/phpmyadmin/conf/phpmyadmin-misc.ini:/usr/local/etc/php/conf.d/phpmyadmin-misc.ini environment: - PMA_ARBITRARY=1 - PMA_HOST=db - PMA_USER=user - PMA_PASSWORD=pwd ports: - 8081:80 </code></pre> <p>変更点は以下。</p> <ul> <li>PHP 用環境: <code>Dockerfile</code> を使ったイメージにカスタマイズ</li> <li>DB: MariaDB のイメージをそのまま利用 <ul> <li><code>environment</code> のパラメータはそのまま使用。アプリケーションや phpMyAdmin 側と動機は取れていないですがひとまずはこれで……</li> </ul></li> <li>phpMyAdmin: これも phpMyAdmin 公式イメージを流用</li> </ul> <h3 id="_docker/dockerfiles/Dockerfile"><a href="#_docker%2Fdockerfiles%2FDockerfile">_docker/dockerfiles/Dockerfile</a></h3> <pre><code class="Dockerfile">FROM php:7-alpine RUN apk --update add curl RUN set -ex \ && apk --no-cache add \ autoconf build-base RUN pecl install xdebug RUN docker-php-ext-enable xdebug RUN docker-php-ext-install pdo_mysql COPY settings/php.ini /usr/local/etc/php/conf.d RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer </code></pre> <p><code>php:7-alpine</code> をベースに Xdebug と Composer を追加。</p> <h3 id="_docker/dockerfiles/settings/php.ini"><a href="#_docker%2Fdockerfiles%2Fsettings%2Fphp.ini">_docker/dockerfiles/settings/php.ini</a></h3> <pre><code class="ini">xdebug.mode=coverage </code></pre> <p>Xdebug でカバレッジを有効化するための設定を追加するため。なお、 <code>Dockerfile</code> で <code>COPY</code> する際のホストマシンでのパスは <strong><code>Dockerfile</code> の存在するディレクトリから下でないと参照できない</strong> という地味な制約があるので <code>dockerfiles</code> ディレクトリをわざわざ掘りました。</p> <h3 id="_docker/phpmyadmin/conf/phpmyadmin-misc.ini"><a href="#_docker%2Fphpmyadmin%2Fconf%2Fphpmyadmin-misc.ini">_docker/phpmyadmin/conf/phpmyadmin-misc.ini</a></h3> <pre><code class="ini">allow_url_fopen = Off max_execution_time = 600 memory_limit = 64M post_max_size = 64M upload_max_filesize = 64M </code></pre> <p>メモリ上限等のカスタマイズ。</p> <hr /> <p>これで今回はカバレッジ出力まで動作することを確認しました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Xdebug"><a href="#Xdebug">Xdebug</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/bf5480f4d7c751ab28aa">PHPUnitでHTMLコードカバレッジを出すまで(Docker使用) - Qiita</a></li> </ul> <blockquote> <p>Warning: XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set</p> </blockquote> <p>が出たので step3 について実施。</p> <p>なお、今回のケースでは</p> <pre><code>extension=xdebug.so </code></pre> <p>は不要だった。</p> <h2 id="Dockerfile"><a href="#Dockerfile">Dockerfile</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://crudzoo.com/blog/php-docker">Dockerで作るNginx + PHP7 + Xdebug環境 | Crudzoo</a></li> </ul> <p>ほぼこれでOK.</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ucan-lab/items/fbf021bf69896538e515">php-alpineコンテナにxdebugをインストールする時にハマったメモ - Qiita</a></li> </ul> <p>こちらも参照。 <code>php -v</code> で <code>with Xdebug</code> と付いていればOK。</p> <h4 id="Dockerfile の COPY"><a href="#Dockerfile+%E3%81%AE+COPY">Dockerfile の COPY</a></h4> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://scrapbox.io/taka521-tech-notes/%E3%80%90Docker%E3%80%91COPY%E3%81%A7%E6%8C%87%E5%AE%9A%E3%81%95%E3%82%8C%E3%81%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AF%E3%80%81Dockerfile%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%8B%E3%82%89%E3%81%AE%E7%9B%B8%E5%AF%BE%E3%83%91%E3%82%B9%E3%81%A7%E3%80%81%E8%A6%AA%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E8%A6%8B%E3%82%8C%E3%81%AA%E3%81%84">【Docker】COPYで指定されたファイルは、Dockerfileが存在するディレクトリからの相対パスで、親ディレクトリを見れない - タカの技術ノート</a></li> </ul> <h3 id="Mariadb"><a href="#Mariadb">Mariadb</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mebee.info/2020/04/07/post-8227/">dockerを使用してmariadbを構築する | mebee</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tarch710/items/1236a23f7ffde4c512f2">開発環境をDockerにしたら、PDOでcould not find driverが出た - Qiita</a></li> </ul> <blockquote> <p>could not find driver</p> </blockquote> <p>普通に考えたら確かにドライバがないので追加して解決。</p> <h3 id="シェル"><a href="#%E3%82%B7%E3%82%A7%E3%83%AB">シェル</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yutachaos/items/56dd7ea09d7e2b0d9173">dockerでalpine linux ベースのcontainerに入って、shellを使いたいとき。 - Qiita</a></li> </ul> <p>bash ではなく ash 。 <code>bin/ash</code> 指定。</p> <h3 id="sr -c"><a href="#sr+-c">sr -c</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://genzouw.com/entry/2020/01/28/120014/1910/">docker-compose.ymlのcommandプロパティに複数コマンドを設定する方法 | ゲンゾウ用ポストイット</a></li> </ul> <p>最終的には使わずに済みましたが念のためメモ。</p> arm-band tag:crieit.net,2005:PublicArticle/18167 2022-04-11T23:53:09+09:00 2022-04-11T23:53:09+09:00 https://crieit.net/posts/prevent-timeout-in-composer-process-20220412 Composer で処理中にタイムアウトになるのを防ぐ <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <p>Composer で処理させたところ</p> <blockquote> <p>The process "'/usr/bin/unzip' -qq '/PATH/TO/PROJECT/vendor/composer/tmp-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' -d '/var<br /> /PATH/TO/PROJECT/vendor/composer/XXXXXXXX'" exceeded the timeout of 300 seconds.</p> </blockquote> <p>でタイムアウトになってしまったのでこれを防ぐ方法をメモ。</p> <h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2> <p>いくつか方法はあるようですが、今回は <code>composer.json</code> に以下の記述を追記することでタイムアウト判定の時間を 300秒(5分) から無制限に変更しました。</p> <pre><code class="json">"config": { "process-timeout": 0 }, </code></pre> <p>これでOK。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/YuK1Game/items/d7e993d6f210d250e157">composerでタイムアウトが発生する問題 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.ninton.co.jp/archives/6314">[php] composerが300秒でタイムアウトしてしまう | Ninton</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18122 2022-02-23T00:13:45+09:00 2022-02-23T00:13:45+09:00 https://crieit.net/posts/output-timezone-datetime-of-php-and-mysql-20220223 PHP と MySQL のタイムゾーンや時刻の確認メモ <p>PHP や MySQL でタイムゾーンや時刻を確認したくなったのでメモ。</p> <h2 id="PHP"><a href="#PHP">PHP</a></h2> <pre><code class="bash"># php -r 'echo date_default_timezone_get() . PHP_EOL; echo date(&quot;y-m-d H:i:s&quot;) . PHP_EOL;' Asia/Tokyo 22-02-13 19:16:50 </code></pre> <p>コマンドライン実行でサクッと確認。</p> <h2 id="MySQL"><a href="#MySQL">MySQL</a></h2> <pre><code class="bash">mysql> show variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | JST | | time_zone | SYSTEM | +------------------+--------+ 2 rows in set (0.00 sec) mysql> SELECT NOW(); +---------------------+ | NOW() | +---------------------+ | 2022-02-13 19:46:19 | +---------------------+ 1 row in set (0.00 sec) </code></pre> <p>こちらはSQL文で確認。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="PHP"><a href="#PHP">PHP</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.softel.co.jp/blogs/tech/archives/2077">【php】ちょっとしたphpをコマンドラインからさくっと実行する at softelメモ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.date.php">PHP: date - Manual</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.php.net/manual/ja/function.date-default-timezone-get.php">PHP: date_default_timezone_get - Manual</a></li> </ul> <h3 id="MySQL"><a href="#MySQL">MySQL</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://mysql.javarou.com/dat/000848.html">現在の日付・時刻を取得する - MySQL 逆引きリファレンス</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tailak/items/63dce2dd7dfe049b038e">MySQLでタイムゾーンを設定する - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://dev.mysql.com/doc/refman/5.6/ja/time-zone-support.html">MySQL :: MySQL 5.6 リファレンスマニュアル :: 10.6 MySQL Server でのタイムゾーンのサポート</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17898 2021-12-29T00:16:11+09:00 2021-12-29T00:17:49+09:00 https://crieit.net/posts/test-centos8-lamp-for-almalinux-in-docker-20211229 (Docker) Almalinux を見据えて CentOS8 での環境構築を試験してみる <p>手前味噌で恐縮ですが、自作の LAMP環境 を検証する Docker Compose について。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/arm-band/docker_compose_ambergrease">GitHub - arm-band/docker_compose_ambergrease</a></li> </ul> <p>イメージを CentOS7 ベースで構築しているので、いずれは AlmaLinux なり Rocky Linux なりに移行しなければならないことは分かっていました。</p> <p>そこで、その以降を見据えて残り数日の命の CentOS8 で試験してみることにしました。なぜ AlmaLinux や Rocky Linux ではなくわざわざ CentOS8 かというと、いきなりそれらのOSに飛びついて何らかの不具合が発生したときに原因の切り分けで苦労するならば、まだ同じシリーズで差分が少ない (と想定される) CentOS8 で試験して、問題なければ次に進もう、という段階的な試験を想定したためです。</p> <h2 id="検証"><a href="#%E6%A4%9C%E8%A8%BC">検証</a></h2> <pre><code class="bash">docker run --name php7_cent8 -it centos:centos8 /bin/bash </code></pre> <p>まずはイメージを取得し、 bash に入ります。</p> <h3 id="Apache + PHP"><a href="#Apache+%2B+PHP">Apache + PHP</a></h3> <p>最初は Apache + PHP 。こちらについては、以前 <a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/2020/02/23/centos8-lamp-install-note/">CentOS8 をインストールして遊んだとき</a>にremiリポジトリの入り方が CentOS7 とは異なることが分かっていたので、多少手を加える必要があるだろう、と踏んでいました。</p> <pre><code class="bash"># \cp -pf /usr/share/zoneinfo/Japan /etc/localtime # </code></pre> <p>これは問題なし。</p> <pre><code class="bash"># dnf -y update && yum -y install epel-release sudo less iproute httpd-devel zip unzip openssl mod_ssl ## 略 Installed: apr-1.6.3-12.el8.x86_64 apr-devel-1.6.3-12.el8.x86_64 apr-util-1.6.1-6.el8.x86_64 apr-util-bdb-1.6.1-6.el8.x86_64 apr-util-devel-1.6.1-6.el8.x86_64 apr-util-openssl-1.6.1-6.el8.x86_64 brotli-1.0.6-3.el8.x86_64 centos-logos-httpd-85.8-2.el8.noarch cyrus-sasl-2.1.27-5.el8.x86_64 cyrus-sasl-devel-2.1.27-5.el8.x86_64 epel-release-8-11.el8.noarch expat-devel-2.2.5-4.el8.x86_64 groff-base-1.22.3-18.el8.x86_64 httpd-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 httpd-devel-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 httpd-filesystem-2.4.37-43.module_el8.5.0+1022+b541f3b1.noarch httpd-tools-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 libdb-devel-5.3.28-42.el8_4.x86_64 libpath_utils-0.2.1-39.el8.x86_64 libpkgconf-1.4.2-1.el8.x86_64 libtalloc-2.3.2-1.el8.x86_64 mailcap-2.1.48-3.el8.noarch mod_http2-1.15.7-3.module_el8.4.0+778+c970deab.x86_64 mod_ssl-1:2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64 ncurses-6.1-9.20180224.el8.x86_64 openldap-devel-2.4.46-18.el8.x86_64 perl-Carp-1.42-396.el8.noarch perl-Data-Dumper-2.167-399.el8.x86_64 perl-Digest-1.17-395.el8.noarch perl-Digest-MD5-2.55-396.el8.x86_64 perl-Encode-4:2.97-3.el8.x86_64 perl-Errno-1.28-420.el8.x86_64 perl-Exporter-5.72-396.el8.noarch perl-File-Path-2.15-2.el8.noarch perl-File-Temp-0.230.600-1.el8.noarch perl-Getopt-Long-1:2.50-4.el8.noarch perl-HTTP-Tiny-0.074-1.el8.noarch perl-IO-1.38-420.el8.x86_64 perl-IO-Socket-IP-0.39-5.el8.noarch perl-IO-Socket-SSL-2.066-4.module_el8.3.0+410+ff426aa3.noarch perl-MIME-Base64-3.15-396.el8.x86_64 perl-Mozilla-CA-20160104-7.module_el8.3.0+416+dee7bcef.noarch perl-Net-SSLeay-1.88-1.module_el8.3.0+410+ff426aa3.x86_64 perl-PathTools-3.74-1.el8.x86_64 perl-Pod-Escapes-1:1.07-395.el8.noarch perl-Pod-Perldoc-3.28-396.el8.noarch perl-Pod-Simple-1:3.35-395.el8.noarch perl-Pod-Usage-4:1.69-395.el8.noarch perl-Scalar-List-Utils-3:1.49-2.el8.x86_64 perl-Socket-4:2.027-3.el8.x86_64 perl-Storable-1:3.11-3.el8.x86_64 perl-Term-ANSIColor-4.06-396.el8.noarch perl-Term-Cap-1.17-395.el8.noarch perl-Text-ParseWords-3.30-395.el8.noarch perl-Text-Tabs+Wrap-2013.0523-395.el8.noarch perl-Time-Local-1:1.280-1.el8.noarch perl-URI-1.73-3.el8.noarch perl-Unicode-Normalize-1.25-396.el8.x86_64 perl-constant-1.33-396.el8.noarch perl-interpreter-4:5.26.3-420.el8.x86_64 perl-libnet-3.11-3.el8.noarch perl-libs-4:5.26.3-420.el8.x86_64 perl-macros-4:5.26.3-420.el8.x86_64 perl-parent-1:0.237-1.el8.noarch perl-podlators-4.11-1.el8.noarch perl-threads-1:2.21-2.el8.x86_64 perl-threads-shared-1.58-2.el8.x86_64 pkgconf-1.4.2-1.el8.x86_64 pkgconf-m4-1.4.2-1.el8.noarch pkgconf-pkg-config-1.4.2-1.el8.x86_64 sscg-2.3.3-14.el8.x86_64 sudo-1.8.29-7.el8_4.1.x86_64 unzip-6.0-45.el8_4.x86_64 zip-3.0-23.el8.x86_64 Complete! </code></pre> <p>OK。</p> <pre><code class="bash"># dnf -y upgrade ## 略 Upgraded: epel-release-8-13.el8.noarch Complete! </code></pre> <p>特に大きな差分もなさそうです。OK。</p> <h3 id="remiリポジトリ"><a href="#remi%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA">remiリポジトリ</a></h3> <pre><code class="bash"># rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-8.rpm Retrieving http://rpms.famillecollet.com/enterprise/remi-release-8.rpm warning: /var/tmp/rpm-tmp.hlvLxe: Header V4 RSA/SHA256 Signature, key ID 5f11735a: NOKEY Verifying... ################################# [100%] Preparing... ################################# [100%] Updating / installing... 1:remi-release-8.5-2.el8.remi ################################# [100%] # rpm --import http://rpms.famillecollet.com/RPM-GPG-KEY-remi2021 # </code></pre> <p>OK。 CentOS8 用のリポジトリに切り替えました。鍵についてはすぐ <code>RPM-GPG-KEY-remi2022</code> にしなければならないでしょうけど。</p> <h4 id="dnf"><a href="#dnf">dnf</a></h4> <pre><code class="bash"># dnf config-manager --enable remi && dnf config-manager --enable remi-php74 No such command: config-manager. Please use /usr/bin/dnf --help It could be a DNF plugin command, try: "dnf install 'dnf-command(config-manager)'" </code></pre> <p><code>remi-php74</code> に指定で躓くかと思いきや、そもそも <code>config-manager</code> がない、と?</p> <p>元々は <code>yum-config-manager</code> だったのですが、 CentOS8 であれば <code>dnf</code> ベースの方が良いだろう、ということで置き換えてはみましたが、ダメですか……。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.trippyboy.com/2021/terraform/almalinux%E3%81%ABterraform%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92yum%E3%81%A7%E5%B0%8E%E5%85%A5%E3%81%99%E3%82%8B%E3%81%AE%E5%B7%BB/">Almalinuxにterraformコマンドをyumで導入するの巻 - TrippyBoyの愉快な日々</a></li> </ul> <p>こちらより <code>dnf install -y dnf-plugins-core</code> してみます。</p> <pre><code class="bash"># dnf install dnf-plugins-core ## 略 Installed: dbus-glib-0.110-2.el8.x86_64 dnf-plugins-core-4.0.21-3.el8.noarch python3-dateutil-1:2.6.1-6.el8.noarch python3-dbus-1.2.4-15.el8.x86_64 python3-dnf-plugins-core-4.0.21-3.el8.noarch python3-six-1.11.0-8.el8.noarch Complete! </code></pre> <p>OK。</p> <h4 id="remi + PHP"><a href="#remi+%2B+PHP">remi + PHP</a></h4> <pre><code class="bash"># dnf config-manager --enable remi && dnf config-manager --enable remi-php74 Error: No matching repo to modify: remi-php74. </code></pre> <p>……やはり <code>remi-php74</code> で引っかかりましたか。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mebee.info/2020/03/12/post-7365/">CentOs8 php7.4をインストールする | mebee</a></li> </ul> <p>……想定していたものとは少し違うやり方ですが、試してみましょう。</p> <pre><code class="bash"># dnf module reset php ## 略 Remi's RPM repository for Enterprise Linux 8 - x86_64 598 kB/s | 3.9 MB 00:06 Last metadata expiration check: 0:00:03 ago on DDD dd mmm yyyy hh:ii:ss AM UTC. Dependencies resolved. Nothing to do. Complete! </code></pre> <p>OK。</p> <pre><code class="bash"># dnf module install -y php:remi-7.4 ## 略 Installed: libedit-3.1-23.20170329cvs.el8.x86_64 libxslt-1.1.32-6.el8.x86_64 nginx-filesystem-1:1.14.1-9.module_el8.0.0+184+e34fea82.noarch oniguruma5php-6.9.7.1-1.el8.remi.x86_64 php-cli-7.4.27-1.el8.remi.x86_64 php-common-7.4.27-1.el8.remi.x86_64 php-fpm-7.4.27-1.el8.remi.x86_64 php-json-7.4.27-1.el8.remi.x86_64 php-mbstring-7.4.27-1.el8.remi.x86_64 php-xml-7.4.27-1.el8.remi.x86_64 Complete! # php --version PHP 7.4.27 (cli) (built: Dec 14 2021 17:17:06) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies </code></pre> <p>OK。入りましたね。これに従い該当箇所を書き換えます。</p> <pre><code class="dockerfile"># enable repository remi & remi-php74 #RUN dnf config-manager --enable remi && dnf config-manager --enable remi-php74 # disable default module RUN dnf module reset -y php RUN dnf module install -y php:remi-7.4 ## 略 # disable repository remi & remi-php74 #RUN dnf config-manager --disable remi && dnf config-manager --disable remi-php74 </code></pre> <p><code>config-manager</code> によるリポジトリ使用のオン・オフの切り替えを削除しました。</p> <pre><code class="bash"># dnf -y install php php-devel php-pdo php-mysqlnd php-mbstring php-gd php-pear php-pecl-apc-devel zlib-devel php-xml php-mcrypt php-pecl-xdebug ## 略 Installed: autoconf-2.69-29.el8.noarch automake-1.16.1-7.el8.noarch cmake-filesystem-3.20.2-4.el8.x86_64 cpp-8.5.0-4.el8_5.x86_64 dejavu-fonts-common-2.35-7.el8.noarch dejavu-sans-fonts-2.35-7.el8.noarch emacs-filesystem-1:26.1-7.el8.noarch fontconfig-2.13.1-4.el8.x86_64 fontpackages-filesystem-1.44-22.el8.noarch freetype-2.9.1-4.el8_3.1.x86_64 fribidi-1.0.4-8.el8.x86_64 gcc-8.5.0-4.el8_5.x86_64 gcc-c++-8.5.0-4.el8_5.x86_64 gd3php-2.3.3-4.el8.remi.x86_64 glibc-devel-2.28-164.el8.x86_64 glibc-headers-2.28-164.el8.x86_64 graphite2-1.3.10-10.el8.x86_64 harfbuzz-1.7.5-3.el8.x86_64 isl-0.16.1-6.el8.x86_64 jbigkit-libs-2.1-14.el8.x86_64 kernel-headers-4.18.0-348.2.1.el8_5.x86_64 keyutils-libs-devel-1.5.10-9.el8.x86_64 krb5-devel-1.18.2-14.el8.x86_64 libX11-1.6.8-5.el8.x86_64 libX11-common-1.6.8-5.el8.noarch libXau-1.0.9-3.el8.x86_64 libXpm-3.5.12-8.el8.x86_64 libcom_err-devel-1.45.6-2.el8.x86_64 libimagequant-2.12.5-1.el8.x86_64 libjpeg-turbo-1.5.3-12.el8.x86_64 libkadm5-1.18.2-14.el8.x86_64 libmcrypt-2.5.8-26.el8.x86_64 libmpc-1.1.0-9.1.el8.x86_64 libpng-2:1.6.34-5.el8.x86_64 libraqm-0.7.0-4.el8.x86_64 libselinux-devel-2.9-5.el8.x86_64 libsepol-devel-2.9-3.el8.x86_64 libsodium-1.0.18-2.el8.x86_64 libstdc++-devel-8.5.0-4.el8_5.x86_64 libtiff-4.0.9-20.el8.x86_64 libtool-2.4.6-25.el8.x86_64 libverto-devel-0.3.0-5.el8.x86_64 libwebp-1.0.0-5.el8.x86_64 libxcb-1.13.1-1.el8.x86_64 libxcrypt-devel-4.1.1-6.el8.x86_64 libxml2-devel-2.9.7-9.el8_4.2.x86_64 m4-1.4.18-7.el8.x86_64 make-1:4.2.1-10.el8.x86_64 openssl-devel-1:1.1.1k-5.el8_5.x86_64 pcre2-devel-10.32-2.el8.x86_64 pcre2-utf16-10.32-2.el8.x86_64 pcre2-utf32-10.32-2.el8.x86_64 perl-Thread-Queue-3.13-1.el8.noarch php-7.4.27-1.el8.remi.x86_64 php-devel-7.4.27-1.el8.remi.x86_64 php-fedora-autoloader-1.0.1-2.el8.remi.noarch php-gd-7.4.27-1.el8.remi.x86_64 php-mysqlnd-7.4.27-1.el8.remi.x86_64 php-opcache-7.4.27-1.el8.remi.x86_64 php-pdo-7.4.27-1.el8.remi.x86_64 php-pear-1:1.10.13-1.el8.remi.noarch php-pecl-apcu-5.1.21-1.el8.remi.7.4.x86_64 php-pecl-apcu-devel-5.1.21-1.el8.remi.7.4.x86_64 php-pecl-mcrypt-1.0.4-1.el8.remi.7.4.x86_64 php-pecl-xdebug-2.9.8-1.el8.remi.7.4.x86_64 php-process-7.4.27-1.el8.remi.x86_64 php-sodium-7.4.27-1.el8.remi.x86_64 xz-devel-5.2.4-3.el8.x86_64 zlib-devel-1.2.11-17.el8.x86_64 Complete! </code></pre> <p>OK。</p> <pre><code class="bash"># php -r &quot;copy('https://getcomposer.org/installer', 'composer-setup.php');&quot; && php composer-setup.php && php -r &quot;unlink('composer-setup.php');&quot; && mv composer.phar /usr/local/bin/composer All settings correct for using Composer Downloading... Composer (version 2.2.1) successfully installed to: /etc/yum.repos.d/composer.phar Use it: php composer.phar # composer --version Composer version 2.2.1 2021-12-22 22:21:31 </code></pre> <p>Composer も入りました。</p> <pre><code class="bash"># mkdir /var/log/php # chown apache /var/log/php # chmod 755 /var/log/php # mkdir -p /etc/ssl/private </code></pre> <p>まあこの辺りは普通に。</p> <h3 id="SSL"><a href="#SSL">SSL</a></h3> <pre><code class="bash"># openssl req -new -newkey rsa:2048 -nodes -out /etc/ssl/private/server.csr -keyout /etc/ssl/private/server.key -subj &quot;/C=/ST=/L=/O=/OU=/CN=*.lvh.me&quot; ## 略 # openssl x509 -days 365 -req -signkey /etc/ssl/private/server.key -in /etc/ssl/private/server.csr -out /etc/ssl/private/server.crt Signature ok subject=CN = *.lvh.me Getting Private key </code></pre> <p>OK。</p> <h3 id="Apache の設定ファイル"><a href="#Apache+%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB">Apache の設定ファイル</a></h3> <h4 id="ssl.conf"><a href="#ssl.conf">ssl.conf</a></h4> <pre><code class="bash"># cat /etc/httpd/conf.d/ssl.conf ## 略 </code></pre> <p>表示させたSSL用の設定を現行のものと差し替えます。ただし、現行の設定をなるべく引き継ぐように。これは他の設定ファイルも同様です。</p> <pre><code class="conf">- SSLRandomSeed startup file:/dev/urandom 256 - SSLRandomSeed connect builtin - SSLCipherSuite HIGH:3DES:!aNULL:!MD5:!SEED:!IDEA </code></pre> <p>現行にあったこれらの行は削除。</p> <pre><code class="conf">+ SSLHonorCipherOrder on </code></pre> <p>これは新しく追加されていました。</p> <pre><code class="conf">SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 </code></pre> <p>プロトコルについては念のため現行の設定を引き継ぎ。流石にもう SSLv2 なんて存在しないでしょうけど……。</p> <pre><code class="conf">- <Files ~ "\.(cgi|shtml|phtml|php3?)$"> + <FilesMatch "\.(cgi|shtml|phtml|php)$"> SSLOptions +StdEnvVars - </Files> + </FilesMatch> </code></pre> <p>微妙にディレクティブの表現が変わっていますが、影響はなさそうなので変更を受け入れます。</p> <h4 id="php.conf"><a href="#php.conf">php.conf</a></h4> <pre><code class="bash"># cat /etc/httpd/conf.d/php.conf ## 略 # Redirect to local php-fpm if mod_php (5 or 7) is not available <IfModule !mod_php5.c> <IfModule !mod_php7.c> # Enable http authorization headers SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1 <FilesMatch \.(php|phar)$> SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost" </FilesMatch> </IfModule> </IfModule> </code></pre> <p>こちらは最後の部分に追記がありますね。これはそのまま受け入れ。</p> <h4 id="php.ini"><a href="#php.ini">php.ini</a></h4> <pre><code class="bash"># cat /etc/php.ini ## 略 </code></pre> <p>こちらも設定を引き継ぎ、といっても現行の設定と変更点はなく(自前の設定を引き継いだ) 差分としてはコメントの文字列くらい。</p> <h3 id="AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message"><a href="#AH00558%3A+httpd%3A+Could+not+reliably+determine+the+server%27s+fully+qualified+domain+name%2C+using+192.0.2.1.+Set+the+%27ServerName%27+directive+globally+to+suppress+this+message">AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message</a></h3> <p>Apache を起動しようとしたところ、</p> <blockquote> <p>AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message</p> </blockquote> <p>のエラーメッセージがログに記録されて起動してこない現象に遭遇。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/a6deebbad207d627b598">[Docker]AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using ***.***.*.*. Set the 'ServerName' directive globally to suppress this message - Qiita</a></li> </ul> <p>そういえば元々の Dockerfile には記述していましたが、 <code>httpd.conf</code> への追記をしていませんでした。</p> <pre><code class="bash">echo ServerName www.example.com:80 >> /etc/httpd/conf/httpd.conf </code></pre> <p>これでOK。</p> <pre><code class="bash"># httpd -v Server version: Apache/2.4.37 (centos) Server built: Nov 12 2021 04:57:27 </code></pre> <p>バージョンも拾えました。</p> <h3 id="(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed"><a href="#%282%29No+such+file+or+directory%3A+AH02454%3A+FCGI%3A+attempt+to+connect+to+Unix+domain+socket+%2Frun%2Fphp-fpm%2Fwww.sock+%28%2A%29+failed">(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed</a></h3> <p>これで Apache も起動してきたのでブラウザで表示確認、ということで <code>phpinfo()</code> でも表示させようかとファイルを用意して <code>curl</code> してみると……</p> <blockquote> <p>Service Unavailable</p> <p>The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.</p> </blockquote> <p>503エラーが出てしまいました。</p> <blockquote> <p>(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed</p> </blockquote> <p>Apache のエラーログには上述のログが記録されていました。</p> <p>今までとは異なる方法で PHP をインストールしたら、どうやら php-fpm (CGIモード) で動作しようとしてしまっているようです。</p> <p>今回の環境ではCGI版かモジュール版かはあまり考慮しておらず、Webサーバも Apache のためモジュール版でも良いと判断。</p> <p>動作を切り替えます。</p> <pre><code class="bash"># less /etc/httpd/conf/httpd.conf ## 略 Include conf.modules.d/*.conf </code></pre> <p>念のためモジュール読み込みの記述があることを確認。</p> <pre><code class="bash"># ls /etc/httpd/conf.modules.d/ 00-base.conf 00-lua.conf 00-optional.conf 00-ssl.conf 01-cgi.conf 10-proxy_h2.conf README 00-dav.conf 00-mpm.conf 00-proxy.conf 00-systemd.conf 10-h2.conf 15-php.conf </code></pre> <p>関係しそうなのは <code>00-mpm.conf</code>, <code>01-cgi.conf</code>, <code>15-php.conf</code> 辺りでしょうか。</p> <pre><code class="bash"># less /etc/httpd/conf.modules.d/15-php.conf # Cannot load both php5 and php7 modules <IfModule !mod_php5.c> <IfModule prefork.c> LoadModule php7_module modules/libphp7.so </IfModule> </IfModule> </code></pre> <p>ここはそのまま。</p> <pre><code class="bash"># less /etc/httpd/conf.modules.d/01-cgi.conf # This configuration file loads a CGI module appropriate to the MPM # which has been configured in 00-mpm.conf. mod_cgid should be used # with a threaded MPM; mod_cgi with the prefork MPM. <IfModule mpm_worker_module> LoadModule cgid_module modules/mod_cgid.so </IfModule> <IfModule mpm_event_module> LoadModule cgid_module modules/mod_cgid.so </IfModule> <IfModule mpm_prefork_module> LoadModule cgi_module modules/mod_cgi.so </IfModule> </code></pre> <p>ここもそのまま。</p> <pre><code class="bash"># less /etc/httpd/conf.modules.d/00-mpm.conf # Select the MPM module which should be used by uncommenting exactly # one of the following LoadModule lines. See the httpd.conf(5) man # page for more information on changing the MPM. # prefork MPM: Implements a non-threaded, pre-forking web server # See: http://httpd.apache.org/docs/2.4/mod/prefork.html # # NOTE: If enabling prefork, the httpd_graceful_shutdown SELinux # boolean should be enabled, to allow graceful stop/shutdown. # #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so # worker MPM: Multi-Processing Module implementing a hybrid # multi-threaded multi-process web server # See: http://httpd.apache.org/docs/2.4/mod/worker.html # #LoadModule mpm_worker_module modules/mod_mpm_worker.so # event MPM: A variant of the worker MPM with the goal of consuming # threads only for connections with active processing # See: http://httpd.apache.org/docs/2.4/mod/event.html # LoadModule mpm_event_module modules/mod_mpm_event.so </code></pre> <p>変更するのはここ。</p> <pre><code>- #LoadModule mpm_prefork_module modules/mod_mpm_prefork.so + LoadModule mpm_prefork_module modules/mod_mpm_prefork.so - LoadModule mpm_event_module modules/mod_mpm_event.so # LoadModule mpm_event_module modules/mod_mpm_event.so </code></pre> <p>上述のうち2ヶ所を反転させます。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.out48.com/archives/5636/">CentOSにApacheとPHP入れてPHPファイルを表示しようとしたら503エラー Output48</a></li> </ul> <p>これでOK。起動も確認できました。</p> <p>テンプレートとして上述を反転させた <code>00-mpm.conf</code> を用意し、エントリポイントでファイルコピーして設定を上書きすることで対処しました。</p> <p><a href="https://crieit.now.sh/upload_images/abe7eb81a2b7f6580feb784cb81cdf1361cb21ea49eef.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/abe7eb81a2b7f6580feb784cb81cdf1361cb21ea49eef.jpg?mw=700" alt="最終的に AlmaLinux の Docker Compose で起動、確認した phpinfo() の画面" /></a></p> <p>最終的に AlmaLinux の Docker Compose を作成して起動し、ブラウザで該当の PHPファイル にアクセスし、 <code>phpinfo()</code> の画面が表示されることを確認できました。OK。</p> <h3 id="SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty"><a href="#SSLCertificateFile%3A+file+%27%2Fetc%2Fpki%2Ftls%2Fcerts%2Flocalhost.crt%27+does+not+exist+or+is+empty">SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty</a></h3> <p>上述のエラーの他に</p> <blockquote> <p>SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty</p> </blockquote> <p>localhost のSSL証明書がない、というエラーにも遭遇しました。</p> <p>なければ作れば良いだけ、上述で</p> <pre><code class="bash">echo ServerName www.example.com:80 >> /etc/httpd/conf/httpd.conf </code></pre> <p>としているので、このダミードメインを使います。</p> <pre><code class="bash"># openssl req -new -newkey rsa:2048 -nodes -out /etc/pki/tls/certs/localhost.csr -keyout /etc/pki/tls/private/localhost.key -subj &quot;/C=/ST=/L=/O=/OU=/CN=www.example.com&quot; ## 略 # openssl x509 -days 365 -req -signkey /etc/pki/tls/private/localhost.key -in /etc/pki/tls/certs/localhost.csr -out /etc/pki/tls/certs/localhost.crt Signature ok subject=CN = www.example.com Getting Private key </code></pre> <p>これでOK。 <code>ssl.conf</code> には記述はあるのですが……。</p> <h3 id="MySQL"><a href="#MySQL">MySQL</a></h3> <p>次に MySQL 側を。こちらはほぼ手を加える必要はないという想定です。</p> <pre><code class="bash"># dnf localinstall https://dev.mysql.com/get/mysql80-community-release-el8-2.noarch.rpm ## 略 Package mysql80-community-release-el8-2.noarch is already installed. Dependencies resolved. Nothing to do. Complete! </code></pre> <p>CentOS8 用ということでリポジトリの入れ方を少し変更。</p> <pre><code class="bash"># ls /etc/yum.repo.d/ CentOS-Linux-AppStream.repo CentOS-Linux-Media.repo epel-testing.repo CentOS-Linux-BaseOS.repo CentOS-Linux-Plus.repo mysql-community.repo CentOS-Linux-ContinuousRelease.repo CentOS-Linux-PowerTools.repo mysql-community-source.repo CentOS-Linux-Debuginfo.repo CentOS-Linux-Sources.repo remi-modular.repo CentOS-Linux-Devel.repo epel-modular.repo remi.repo CentOS-Linux-Extras.repo epel-playground.repo remi-safe.repo CentOS-Linux-FastTrack.repo epel.repo CentOS-Linux-HighAvailability.repo epel-testing-modular.repo </code></pre> <p><code>mysql-community.repo</code>, <code>mysql-community-source.repo</code> の2つが追加されたことを確認。</p> <pre><code class="bash"># dnf module disable mysql ## 略 Complete! </code></pre> <p>デフォルトの MySQL がいると邪魔になるので無効化して</p> <pre><code class="bash"># dnf -y install mysql-community-devel mysql-community-server ## 略 Installed: mysql-community-client-8.0.27-1.el8.x86_64 mysql-community-client-plugins-8.0.27-1.el8.x86_64 mysql-community-common-8.0.27-1.el8.x86_64 mysql-community-devel-8.0.27-1.el8.x86_64 mysql-community-libs-8.0.27-1.el8.x86_64 mysql-community-server-8.0.27-1.el8.x86_64 net-tools-2.0-0.52.20160912git.el8.x86_64 Complete! </code></pre> <p>インストール完了。</p> <pre><code class="bash"># mysqld --version /usr/sbin/mysqld Ver 8.0.27 for Linux on x86_64 (MySQL Community Server - GPL) </code></pre> <p>デフォルトでは <code>8.0.26</code> だったバージョンが地味に <code>8.0.27</code> に上がりました。</p> <pre><code class="bash"># grep 'temporary password' /var/log/mysqld.log yyyy-mm-ddThh:ii:ss.zzzzzzZ 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: XXXXXXXXXXXX </code></pre> <p>初期パスワードの出現位置が半角スペース区切りで13番目であることを確認。これはエントリポイントで <code>awk</code> を使って抽出、初期パスワードを環境変数の値で書き換える処理で使用するため個人的には重要ポイント。ここは変更しなくて良さそうです。</p> <pre><code class="bash"># cat /etc/my.cnf [mysqld] ## 略 datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid </code></pre> <p>デフォルトで4つパラメータが指定されています。うち3つはテンプレートの設定ファイルに記述がされているので不要、 <code>pid-file</code> だけ現時点では指定がないため、これだけ <code>base.cnf</code> に移植します。</p> <p><code>/etc/my.cnf</code> には上述4つを削除した代わりに <code>!includedir /etc/my.cnf.d</code> を末尾に追加したものを現行の <code>my.cnf</code> と差し替え。</p> <p>これで MySQL 側は良さそうです。</p> <p>なお、 phpMyAdmin は公式イメージをそのまま使っているので変更なし。</p> <p>以上で一通り動作検証できたので、これをベースに AlmaLinux 用に書き換えれば良さそうです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="Docker"><a href="#Docker">Docker</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/_/almalinux?tab=tags">almalinux Tags | Docker Hub</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/_/centos?tab=tags">centos Tags | Docker Hub</a></li> </ul> <h3 id="AlmaLinux"><a href="#AlmaLinux">AlmaLinux</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.apar.jp/linux/15554/">AlmaLinux 8.4 LAMPサーバインストールメモ【Apache2.4+MySQL8.0+PHP7.4】 | あぱーブログ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yamada-hakase/items/17b41d33e30232b69fe2">AlmaLinuxを使ってみた(後編) - Qiita</a></li> </ul> <h3 id="dnf"><a href="#dnf">dnf</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.trippyboy.com/2021/terraform/almalinux%E3%81%ABterraform%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92yum%E3%81%A7%E5%B0%8E%E5%85%A5%E3%81%99%E3%82%8B%E3%81%AE%E5%B7%BB/">Almalinuxにterraformコマンドをyumで導入するの巻 - TrippyBoyの愉快な日々</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/40937056/dnf-missing-config-manager-command">linux - DNF missing config-manager command - Stack Overflow</a></li> </ul> <h3 id="remi"><a href="#remi">remi</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://rpms.remirepo.net/">Remi's RPM repository</a></li> </ul> <h3 id="AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message"><a href="#AH00558%3A+httpd%3A+Could+not+reliably+determine+the+server%27s+fully+qualified+domain+name%2C+using+192.0.2.1.+Set+the+%27ServerName%27+directive+globally+to+suppress+this+message">AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192.0.2.1. Set the 'ServerName' directive globally to suppress this message</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sachiko-kame/items/a6deebbad207d627b598">[Docker]AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using ***.***.*.*. Set the 'ServerName' directive globally to suppress this message - Qiita</a></li> </ul> <h3 id="PHP"><a href="#PHP">PHP</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mebee.info/2020/03/12/post-7365/">CentOs8 php7.4をインストールする | mebee</a></li> </ul> <h3 id="(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed"><a href="#%282%29No+such+file+or+directory%3A+AH02454%3A+FCGI%3A+attempt+to+connect+to+Unix+domain+socket+%2Frun%2Fphp-fpm%2Fwww.sock+%28%2A%29+failed">(2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php-fpm/www.sock (*) failed</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://stackoverflow.com/questions/58365479/no-such-file-or-directory-ah02454-fcgi-attempt-to-connect-to-unix-domain-sock">apache - No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /var/run/php/php5.6-fpm.sock (*) failed - Stack Overflow</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.out48.com/archives/5636/">CentOSにApacheとPHP入れてPHPファイルを表示しようとしたら503エラー Output48</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.stackoverflow.com/questions/46727/amazon-linux2%E3%81%A7%E3%82%A8%E3%83%A9%E3%83%BCah02454%E3%82%84ah01079%E3%81%AA%E3%81%A9%E3%81%8C%E5%87%BA%E3%81%A6httpd%E3%81%8C%E3%81%86%E3%81%BE%E3%81%8F%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%BE%E3%81%9B%E3%82%93">php - amazon-linux2でエラーAH02454やAH01079などが出てhttpdがうまく起動しません - スタック・オーバーフロー</a></li> </ul> <h3 id="php-fpm"><a href="#php-fpm">php-fpm</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kotarella1110/items/634f6fafeb33ae0f51dc">nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する - Qiita</a></li> </ul> <h3 id="SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty"><a href="#SSLCertificateFile%3A+file+%27%2Fetc%2Fpki%2Ftls%2Fcerts%2Flocalhost.crt%27+does+not+exist+or+is+empty">SSLCertificateFile: file '/etc/pki/tls/certs/localhost.crt' does not exist or is empty</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://daredemose.com/linux/ssl/">SSL/TLS | 初心者向けプログラミング講座</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://hacknote.jp/archives/51779/">いつの間にか作られてる /etc/pki/tls/certs/localhost.crt と /etc/pki/tls/private/localhost.key の謎 | ハックノート</a></li> </ul> <h3 id="MySQL"><a href="#MySQL">MySQL</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yasushi-jp/items/1579c301075d693a2a36">MySQL 8.0 を CentOS 8.1 にインストールする手順 - Qiita</a></li> </ul> arm-band