tag:crieit.net,2005:https://crieit.net/feed Crieitの最近の投稿 - Crieit Crieitの最近の投稿 2022-05-27T12:36:58+09:00 https://crieit.net/feed tag:crieit.net,2005:PublicArticle/18199 2022-05-27T07:07:11+09:00 2022-05-27T12:36:58+09:00 https://crieit.net/posts/wzpowmod 乗算器のない8bit CPUで高速にべき乗剰余演算するコード <h1 id="目的"><a href="#%E7%9B%AE%E7%9A%84">目的</a></h1> <p>軽量で非常に良くできた<a target="_blank" rel="nofollow noopener" href="https://wzeta.idletime.tokyo/">8bit CPUのオープンソースWZeta</a>を普及させるためにRSA暗号や楕円暗号などの公開鍵暗号で使われるべき乗剰余演算のアセンブラコードをCC0ライセンス(パブリックドメインと同じ)で公開したことのお知らせ。</p> <p><strong>注意!!! CC0であるのは、べき乗剰余演算のWZetaのアセンブラコードのみ</strong></p> <h1 id="WZetaとは"><a href="#WZeta%E3%81%A8%E3%81%AF">WZetaとは</a></h1> <p>WZetaは<strong>トランジスタ数当たりの性能</strong>、命令セット内パリティ、ハードマクロ命令など8bit CPUにして新技術を多数持ち、常識を破る変則的な命令セットですが、高い効率であることが確認されつつあるCPU。</p> <h1 id="1024bitのべき乗剰余演算するコード"><a href="#1024bit%E3%81%AE%E3%81%B9%E3%81%8D%E4%B9%97%E5%89%B0%E4%BD%99%E6%BC%94%E7%AE%97%E3%81%99%E3%82%8B%E3%82%B3%E3%83%BC%E3%83%89">1024bitのべき乗剰余演算するコード</a></h1> <pre><code>################################################################### ### WZeta powmod 1024bit 2022/05/27 by Naoki Hirayama ### ### These codes are licensed under CC0.(Public Domain) ### http://creativecommons.org/publicdomain/zero/1.0/deed.ja ################################################################### ### --------------------------------------------------------------- ### Subroutine powmod1024 y = g^x mod p ### INOUT g,r(=y) 128,128 !0x100 ### IN x,p 128,128 !0x100 ### r = R^2 mod p ### --------------------------------------------------------------- MEMORY 4 MODEL TINY .PROG LD A,&powmod_gr.H ST %_b_powmod_gr,A LD A,&powmod_px.H ST %_b_powmod_px,A LD A, ^_b_powmod1024.H LD B, ^_b_powmod1024.L BAL A:B LD A,32 ST %_b_powmod_i,A LD A, &ANS.H LD B,0x80 LD C,0x00 copy_ans: # ans <- powmod_y STACP [&powmod_y:B] STACP [&powmod_y:B] STACP [&powmod_y:B] STACP [&powmod_y:B] DEC %_b_powmod_i JRZ0 ^copy_ans $PRINT &ANS 128 NOP $PRINT &powmod_exp 128 EXIT ### --------------------------------------------------------------- ### [%h:%l] =[%h:%l] * g R-1mod p ### IN %h:%l : pointer ### CONST p,g ### --------------------------------------------------------------- _b_powmod1024_mm: ST %_b_powmod_retA,A ST %_b_powmod_retB,B LD A,&_b_powmod_a.H # a = 0 LD B,&_b_powmod_a.L LD C,128 # repeat 129 LOOPZERO ZERO %_b_powmod_m _b_powmod1024_mm_loop_m: # for m=0 to 127 LD A,8 ST %_b_powmod_n,A LD A, %_b_powmod_m ADD A, %_b_powmod_l LD B,A LD A, %_b_powmod_h LD A,[A:B] ST %_b_powmod_x,A _b_powmod1024_mm_loop_n: # for n=0 to 7 ### loop body start -------------------------- LD A, %_b_powmod_x LD B, 0 AND A,[&powmod_g:B] XOR A,[&_b_powmod_a:B] ST %_b_powmod_u,A SHRC %_b_powmod_x JRC0 ^_b_powmod1024_mm2 # yi = 0 skip a += g LD B,0 # a += g CLC LD C,15 _b_powmod1024_mm1: LD A,[&powmod_g:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_g:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_g:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_g:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_g:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_g:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_g:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_g:B] ADDC [&_b_powmod_a:B],A LOOPINC ^_b_powmod1024_mm1 INCC [&_b_powmod_a:B] _b_powmod1024_mm2: SHRC %_b_powmod_u JRC0 ^_b_powmod1024_mm4 # u=0 skip +p LD B,0 # a += p CLC LD C,15 _b_powmod1024_mm3: LD A,[&powmod_p:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !ADDC [&_b_powmod_a:B],A LD A,[&powmod_p:B] ADDC [&_b_powmod_a:B],A LOOPINC ^_b_powmod1024_mm3 INCC [&_b_powmod_a:B] _b_powmod1024_mm4: # a >>= 1 LD A, &_b_powmod_a.H LD B,128 LD C,128 CLC LOOPSHRC ### loop body end -------------------------- DEC %_b_powmod_n JRZ0 ^_b_powmod1024_mm_loop_n INCX %_b_powmod_m # A=m++ XOR A,0x7F JRZ0 ^_b_powmod1024_mm_loop_m ### Final Substruct INC %_b_powmod_n # n=1 _b_powmod1024_mm5: LD A,32 # y=a(before sub) ST %_b_powmod_m,A # 32 = 128 / 4 LD A,%_b_powmod_h LD C,%_b_powmod_l LD B,0 _b_powmod1024_mm6: STACP [&_b_powmod_a:B] STACP [&_b_powmod_a:B] STACP [&_b_powmod_a:B] STACP [&_b_powmod_a:B] DEC %_b_powmod_m JRZ0 ^_b_powmod1024_mm6 LD C,%_b_powmod_n LOOPINC ^_b_powmod1024_mm8 # if C!=0 jump _b_powmod1024_mm7: # return LD A,%_b_powmod_retA LD B,%_b_powmod_retB JMP A:B _b_powmod1024_mm8: LD B,0 # a -= p LD A,0 SUB A,C # CF=1 LD C,31 _b_powmod1024_mm9: LD A,[&powmod_p:B] !SUBC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !SUBC [&_b_powmod_a:B],A LD A,[&powmod_p:B] !SUBC [&_b_powmod_a:B],A LD A,[&powmod_p:B] SUBC [&_b_powmod_a:B],A LOOPINC ^_b_powmod1024_mm9 DECC [&_b_powmod_a:B],A JRC0 ^_b_powmod1024_mm7 ZERO %_b_powmod_n JR ^_b_powmod1024_mm5 ### --------------------------------------------------------------- ### --------------------------------------------------------------- ### Subroutine powmod1024 ### INOUT g,r(=y) 128,128 !0x100 ### const IN x,p 128,128 !0x100 ### work _b_powmod_a 129 !0x100 ### r : R2modP ### --------------------------------------------------------------- _b_powmod1024: ST %_b_powmod_RETA,A ST %_b_powmod_RETB,B ### CALL powmod_1024mm LD A, %_b_powmod_gr # y = y(r) * g R-1mod p ST %_b_powmod_h , A LD A, 0x80 ST %_b_powmod_l , A BR ^_b_powmod1024_mm ### COPY y -&gt; g LD A, %_b_powmod_gr LD C,0x7F LD B,0xFF copy_y_g: STAC [A:B] LOOPDEC ^copy_y_g # if c--==0 jump -1 # powmod_y = 1 start LD A, %_b_powmod_gr LD B,0x80 # y LD C, 1 !ST [A:B],C # y[0] = 1 LD C,126 # repeat 127 LOOPZERO ZERO %_b_powmod_i,A # for i=0 to 127 powmod_loop_i: LD A,8 ST %_b_powmod_j,A # for j=0 to 7 LD A, &powmod_x.L ADD A, %_b_powmod_i LD B,A LD A,[&powmod_x:B] ST %_b_powmod_y,A powmod_loop_j: ### ### loop main ### SHRC %_b_powmod_y JRC0 ^powmod1024_lbl3 # skip yi=0 ### CALL powmod_1024mm (y*g) LD A, &powmod_y.H ST %_b_powmod_h , A LD A, &powmod_y.L ST %_b_powmod_l , A LD B, ^_b_powmod1024_mm.L BAL ^_b_powmod1024_mm:B powmod1024_lbl3: ### CALL powmod_1024mm (g*g) LD A, &powmod_g.H ST %_b_powmod_h , A LD A, &powmod_g.L ST %_b_powmod_l , A LD B, ^_b_powmod1024_mm.L BAL ^_b_powmod1024_mm:B DEC %_b_powmod_j JRZ0 ^powmod_loop_j INCX %_b_powmod_i # A=[i++] XOR A,0x7F JRZ0 ^powmod_loop_i LD A,%_b_powmod_RETA LD B,%_b_powmod_RETB JMP A:B .DATA REG 16 _reg02 ALIAS _b_powmod_gr _reg02 0 ALIAS _b_powmod_px _reg02 1 ALIAS _b_powmod_x _reg02 2 ALIAS _b_powmod_y _reg02 3 ALIAS _b_powmod_i _reg02 4 ALIAS _b_powmod_j _reg02 5 ALIAS _b_powmod_m _reg02 6 ALIAS _b_powmod_n _reg02 7 ALIAS _b_powmod_h _reg02 8 ALIAS _b_powmod_l _reg02 9 ALIAS _b_powmod_u _reg02 10 ALIAS _b_powmod_retA _reg02 11 # child sub ALIAS _b_powmod_retB _reg02 12 # child sub ALIAS _b_powmod_RETA _reg02 13 ALIAS _b_powmod_RETB _reg02 14 MEM 129 _system_work !0x100 ALIAS _b_powmod_a _system_work 0 MEM 256 powmod_gr !0x100 \ 0x3A 0x43 0xCE 0xEB 0xEC 0x52 0x88 0x94 0x4B 0x42 0x09 0xF6 0x91 0x70 0x7B 0x7F 0x25 0x1F 0xB8 0x44 0x32 0x0F 0x05 0x7F 0xF9 0xDD 0xA5 0x94 0x58 0x32 0x70 0x1E 0xCD 0x91 0x16 0xAC 0x21 0x16 0x90 0x79 0xCC 0xC2 0x66 0xC1 0x88 0x85 0x06 0x3B 0xF5 0x66 0xF2 0x31 0x89 0x74 0x2C 0x87 0x1D 0xBF 0x93 0x85 0x80 0x40 0x20 0xD3 0x18 0x47 0x16 0xE1 0xC5 0xB7 0xEB 0x70 0xF6 0xF0 0x2C 0x34 0x1A 0xBA 0xD5 0x9C 0x8C 0xA4 0xEE 0xDD 0x3C 0xD8 0xD9 0x7B 0xC4 0xA6 0x15 0xB3 0xAC 0x26 0x3D 0x45 0x21 0x7B 0x67 0x43 0xA7 0x63 0x16 0x7B 0x83 0x3C 0x1B 0xAE 0x6A 0x8F 0x9E 0x49 0xBE 0x99 0x01 0x92 0x1A 0xDE 0x6A 0xC5 0x7B 0x48 0xBA 0xF9 0x0A 0x29 0x82 0x46 0x3E 0x9C 0xD5 0x81 0xDB 0xC7 0x70 0xFD 0xEB 0x6C 0x52 0x37 0x07 0xF8 0x61 0x3F 0x79 0x31 0x57 0x32 0xC4 0x44 0x51 0x54 0x10 0x14 0x98 0x82 0x6B 0x0D 0xE5 0x50 0xA8 0xC0 0xC2 0x11 0x48 0xF1 0x38 0x4B 0x86 0xAC 0xF0 0xA5 0x34 0x37 0x52 0x67 0x89 0x88 0x93 0x4A 0x08 0x1E 0x92 0x17 0xE5 0x15 0xE5 0x14 0xB9 0x0C 0xCE 0x89 0xC7 0x0F 0x92 0xC5 0x1C 0xA3 0xF6 0x77 0xB8 0x88 0x50 0xF0 0x57 0xC8 0x62 0xED 0x34 0x89 0xE8 0x33 0xD2 0x22 0x6D 0x1B 0x45 0xC7 0xEB 0x84 0x32 0x13 0x18 0x84 0x84 0x69 0x24 0xD5 0x87 0xC0 0x5D 0x82 0xE8 0x55 0x2A 0x1A 0x77 0xEC 0x04 0xA5 0x21 0x86 0x16 0x04 0x25 0x9D 0x6D 0x66 0x11 0x9A 0x0E 0xBD 0xA9 0xB1 0xD1 0x4A ALIAS powmod_g powmod_gr 0x00 ALIAS powmod_y powmod_gr 0x80 # = R2modP MEM 256 powmod_px !0x100 \ 0xA1 0x63 0xD2 0x90 0x0B 0xD0 0x3A 0xAC 0xB4 0x15 0x75 0xEA 0x5E 0x05 0x57 0x86 0x55 0x12 0x36 0xE2 0xF8 0x07 0xCD 0x3E 0xE5 0x65 0x6E 0x70 0xE0 0x57 0xAE 0x5E 0x2D 0x21 0x53 0xE1 0xF0 0xC4 0xE5 0x96 0xB9 0x83 0xB3 0x5C 0x37 0xE2 0x3C 0x54 0xBF 0xF7 0x0F 0x4A 0xD8 0x19 0x2C 0xCA 0x3C 0xCA 0xD9 0x28 0xC3 0x76 0x75 0x5B 0x13 0x93 0xA3 0xC4 0x98 0x4E 0x6B 0x99 0x33 0x79 0xDC 0xCE 0x9C 0xB0 0xE8 0xBA 0x71 0xCD 0x27 0x5E 0xFA 0x8D 0xB0 0x7C 0x08 0xCF 0xF6 0x24 0x1B 0xD4 0xDA 0xCC 0x09 0x27 0x72 0x9C 0x5F 0x8D 0x08 0x6D 0x95 0x33 0x28 0xEE 0xAF 0x5D 0xED 0xB2 0x6E 0xB0 0xC3 0xE4 0xA3 0x70 0x4E 0x6B 0xF2 0xF9 0x6E 0x9C 0x4F 0xAB 0xFB 0xD8 0xC0 0xFA 0x69 0x4E 0x7C 0xBF 0x6D 0x94 0x18 0xE7 0x5C 0xB7 0xE8 0xE2 0xF4 0xCB 0x4B 0x75 0x3B 0x93 0x78 0x55 0xFC 0x0A 0x0D 0x00 0xEE 0xFE 0x16 0x0A 0x1D 0xAF 0x28 0xD0 0x9B 0xF2 0x09 0x4B 0x31 0xDE 0xF5 0x54 0x0E 0xCD 0xFE 0x4E 0x98 0x73 0x31 0x07 0x7E 0x90 0x1E 0xC6 0x98 0xD0 0xD8 0x1F 0xE8 0x0B 0x34 0xB7 0xFF 0x73 0x4A 0x86 0x82 0x48 0x20 0x52 0x9C 0x57 0xD4 0x38 0x5F 0x8F 0x43 0x33 0xB6 0x91 0xC1 0x14 0x5D 0xA6 0xAF 0x7B 0x13 0xA8 0xFB 0x89 0xAC 0xC8 0xFA 0x40 0x1F 0x21 0x14 0x7C 0xE7 0x64 0xCE 0xFF 0x48 0x49 0x7F 0xFD 0x9B 0x1A 0xA5 0x97 0x42 0x4A 0x94 0xB9 0xD6 0x70 0x83 0x38 0xAE 0xB3 0x78 0xBF 0xA6 0x48 0x24 0x4D 0x81 0x8C ALIAS powmod_p powmod_px 0x00 ALIAS powmod_x powmod_px 0x80 MEM 128 ANS &0xF00 MEM 128 powmod_exp \ 0xB4 0xBB 0x5B 0xB6 0x58 0x7A 0x9D 0x7A 0xEB 0x1C 0xE9 0x79 0xBF 0x94 0xF6 0x60 0x57 0x37 0x87 0x60 0x20 0x85 0xB3 0xB6 0xDD 0x9B 0xF4 0xA5 0xD8 0xFA 0x4C 0xC6 0xAD 0x84 0x39 0xCC 0xEC 0xD3 0xDD 0x23 0x86 0x4A 0x2C 0x50 0x93 0xEC 0xD6 0xAD 0x0D 0xD6 0xC4 0xBC 0xA1 0xD7 0x36 0x4F 0x2A 0x80 0x3F 0xC3 0x0C 0x0F 0xCD 0x4F 0x6C 0x6D 0xF1 0x5F 0x3D 0x58 0xAF 0xB5 0x1A 0x5B 0x85 0xF4 0xB8 0x77 0x11 0xD0 0x48 0x94 0xD7 0xC4 0xFF 0xFD 0x21 0xC1 0x41 0xA4 0x34 0xBB 0xC3 0x01 0x3A 0x36 0x4A 0x30 0xC3 0x3E 0x91 0xB2 0x86 0x2E 0x82 0xB0 0x87 0x63 0x81 0xCF 0x9E 0x33 0xC0 0x22 0x73 0x3B 0x05 0x95 0x8D 0xEA 0x80 0xA1 0xEE 0x33 0x47 0xD7 0xF2 0x84 </code></pre> <h1 id="解説"><a href="#%E8%A7%A3%E8%AA%AC">解説</a></h1> <pre><code>y = g ^ x mod p ( r: R^2 mod p ) </code></pre> <p>rはpの値によって決まるモンゴメリ乗算の事前定数。モンゴメリ乗算の基数2は<a target="_blank" rel="nofollow noopener" href="https://openicf3.idletime.tokyo/">ICF3(1999年)</a>でも使われていました。1024bitべき乗剰余演算のサブルーチンの入力パラメータは256バイトのブロック2個。powmod_grとpowmod_pxです。grは前半128バイトがg、後半128バイトがr。 pxは前半128バイトがp、後半128バイトがx。サブルーチンが終了するとrの場所に演算結果yが入っています。</p> <h1 id="実行環境"><a href="#%E5%AE%9F%E8%A1%8C%E7%92%B0%E5%A2%83">実行環境</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://subnote.icf3.net/wz660/download.html">WZetaシミュレータ</a>で実際に演算させてみることが可能です。</p> <pre><code>> wzsim (アセンブラのファイル) </code></pre> <p><strong>注意 シミュレータにはC言語実装のシミュレータとverilogのバイナリがあります。verilogでは1024bitのべき乗剰余演算に1週間以上かかることもあるので、ご注意ください。</strong></p> <h1 id="実行結果"><a href="#%E5%AE%9F%E8%A1%8C%E7%B5%90%E6%9E%9C">実行結果</a></h1> <p>実行後、演算が終了すると演算結果に続いて期待値が表示されます。期待値はコードの最後にあるpowmod_expの値が、そのまま表示されます。</p> <h1 id="性能について"><a href="#%E6%80%A7%E8%83%BD%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">性能について</a></h1> <p>乗算命令が無いため8bitの加算器1個で演算しています。絶対時間では非常に遅いため軽量な楕円暗号か、数時間の演算時間が許容されるIoTシステムなどでの利用に向いています。BIOSを通してアプリを作成すればアプリを改変することなく超高速な<a target="_blank" rel="nofollow noopener" href="https://snakecube.idletime.tokyo/">暗号プロセッサSnakeCube</a>に置き換えられることを考えているため、性能が必要になったところで、暗号プロセッサSnakeCubeに置き換えることが可能になる予想です。シミュレータの実行終了後、命令数が表示されます。WZetaのSDogコアでは1命令4サイクルなので命令数を4倍して周波数を計算すると1024bitのべき乗剰余演算1回の時間が求まります。値に依存しますが、上記、サンプルコードでは666530315命令。CPUの周波数に8MHzを想定した場合、1サイクル125[ns]なので<br /> 666530315 × 4 × 125 ÷ 10^9 ≒ 333秒 ( 512bitのべき乗剰余演算だと約42秒 )</p> <h1 id="脆弱性対策"><a href="#%E8%84%86%E5%BC%B1%E6%80%A7%E5%AF%BE%E7%AD%96">脆弱性対策</a></h1> <p>今回のコードは脆弱性対策の無い単純に最も高速なプログラムになっています。また最上位ビットを高速モードとして使っています。サイドチャネル攻撃を対策をするには値に依存しないようにダミーの計算時間を入れます。WZetaの命令セットはオペコードに依存せずメモリアクセスをするという低消費電力でない特性がありますが、これが電力解析などのサイドチャネル攻撃への耐性を高めることにもなっています。トランジスタ数が少ないのでオペコードに依存しない動作で0、1の切り替えが多くなったとしても、全体としての消費電力は少ないという予想です。</p> spinlock tag:crieit.net,2005:PublicArticle/18198 2022-05-26T20:41:56+09:00 2022-05-26T20:41:56+09:00 https://crieit.net/posts/aka-tool-batlauncher PowerShell:指定フォルダのbatを実行するLauncherを作ってみた <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>お仕事とかでテスト用batとか作ってしょっちゅうたたくけどいちいちコマンドプロンプトに打つのも面倒なので画面のはじっこにでもbat実行用のLauncherでも置いとけばいいかなと思って作ってみた。<br /> 最初は適当にexe作ろうと思ったけどたまにはPowerShellでForm作るの復習がてらやってみようってことで昼休みに作ってたらまんまと罠にはまって一旦諦め。<br /> 解決はしたものの悔しさと絶対覚えてられない自身があったので記事に残す。</p> <h1 id="やりたいこと"><a href="#%E3%82%84%E3%82%8A%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8">やりたいこと</a></h1> <ul> <li>引数で渡されたPathにある.batファイルをそれぞれボタンコントロールにして一列に並べる。</li> <li>ボタンをクリックすると対応するbatを実行する</li> <li>後ろに隠れると不便だからTopMost</li> <li>batファイルがなければエラーメッセージでもだして終了</li> </ul> <h1 id="単純にFormの話"><a href="#%E5%8D%98%E7%B4%94%E3%81%ABForm%E3%81%AE%E8%A9%B1">単純にFormの話</a></h1> <p>最初のおまじないさえ知っておけばそんなに難しくない</p> <p>こういうの(アセンブリ)使うよって教えてあげて</p> <pre><code>Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing </code></pre> <p>Form(外側)作ってあげて</p> <pre><code>$Form = New-Object System.Windows.Forms.Form $Form.Text = "BatToolLauncher" $Form.Width = 180 $Form.Height = $BatList.Count*30 + 50 $Form.TopMost = $true </code></pre> <p>ボタンを配置する</p> <pre><code>$Button = New-Object System.Windows.Forms.Button $Button.Location = New-Object System.Drawing.Point(0,0) $Button.Top = $Button.Top + $i * 30 $Button.Size = New-Object System.Drawing.Size(160,30) $Button.Text = $x.BaseName $Form.Controls.Add($Button) </code></pre> <p>※$BatListはbatの一覧(Get-ChildItemの結果)<br /> ※$xは$BatListをforeachで回した時の一時変数</p> <h1 id="ボタンクリック時の挙動を設定する(今回の鬼門)"><a href="#%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E6%99%82%E3%81%AE%E6%8C%99%E5%8B%95%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%28%E4%BB%8A%E5%9B%9E%E3%81%AE%E9%AC%BC%E9%96%80%29">ボタンクリック時の挙動を設定する(今回の鬼門)</a></h1> <p>ボタンクリック時の挙動を書くことだけは簡単。<br /> 「Add_イベント」の形で定義してあげればいい。クリックイベントなら下の{}の中に書けばいい。</p> <pre><code>$Button.Add_Click({}) </code></pre> <p>簡単。なはずだった。<br /> とにもかくにも失敗例↓</p> <pre><code>$Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}) </code></pre> <p>これは$x.FullNameでbatのフルパスが入るからそいつをStart-Processするだけ簡単だよね、と思って頭を抱えたもの。<br /> 何が起こるかというと、実際のクリック時の$xを参照してくれる。foreachでぐるぐる回していたので、$xが指すのは最後のbat。つまり、どのボタンを押しても最後のbatファイルが実行される。<br /> というわけで半日かけて試行錯誤・調査して解決策↓</p> <pre><code>$Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}.GetNewClosure()) </code></pre> <p>GetNewClosure()<br /> を書くことでこの{}で囲まれた部分はスクリプトブロックじゃなくてクロージャだよ、となるらしい。今回の話に沿わせるとこの行を書いた時点の変数の内容でFixしてくれるらしい。<br /> ちょっと説明の言葉が怪しい自覚はある。</p> <h1 id="スクリプト全体"><a href="#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E5%85%A8%E4%BD%93">スクリプト全体</a></h1> <p>結果的にこんな感じ。</p> <pre><code>Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing if($args.Length -gt 0) { $TargetDir = $args[0] } else { $MyPath = $MyInvocation.MyCommand.Path $TargetDir = Split-Path -Parent $MyPath } # batファイルのリスト作成 $BatList = Get-ChildItem $TargetDir -Filter "*.bat" if($BatList -eq $null) { [System.Windows.Forms.MessageBox]::Show("no bat files", "Error") return } # Form作成 $Form = New-Object System.Windows.Forms.Form $Form.Text = "BatToolLauncher" $Form.Width = 180 $Form.Height = $BatList.Count*30 + 50 $Form.TopMost = $true # ボタン作成 $i = 0 foreach($x in $BatList) { $Button = New-Object System.Windows.Forms.Button $Button.Location = New-Object System.Drawing.Point(0,0) $Button.Top = $Button.Top + $i * 30 $Button.Size = New-Object System.Drawing.Size(160,30) $Button.Text = $x.BaseName # GetNewClosure様をたたえる! $Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}.GetNewClosure()) $Form.Controls.Add($Button) $i++ } # 表示 $Form.ShowDialog() </code></pre> <h1 id="おまけ"><a href="#%E3%81%8A%E3%81%BE%E3%81%91">おまけ</a></h1> <p>ps1ファイルってそのままたたけないのでいつも汎用性持たせて作ってるbat</p> <pre><code>powershell -ExecutionPolicy RemoteSigned %~dpn0.ps1 %* </code></pre> <p>同じ場所にある同名ps1を実行するbat<br /> いつもの使いまわしファイルがどこかにいったので久しぶりに手書きしたけどだいたいこんなだったはず。</p> RedCol tag:crieit.net,2005:PublicArticle/18197 2022-05-24T09:09:10+09:00 2022-05-24T09:12:51+09:00 https://crieit.net/posts/Flutter-Hive-type-UnspecifiedInvalidResult-is-not-a-subtype-of-type-LibraryElementResult-in-type-cast FlutterのHiveで type 'UnspecifiedInvalidResult' is not a subtype of type 'LibraryElementResult' in type cast エラー <p>FlutterのHiveでbuild_runnerしたところ下記のようなエラーが出た。</p> <pre><code class="plain">type 'UnspecifiedInvalidResult' is not a subtype of type 'LibraryElementResult' in type cast [SEVERE] hive_generator:hive_generator on lib/models/message.dart: </code></pre> <p>調べてみたところいくつかの箇所で一旦pubspec.lockを削除したらいける、とのことで試してみたらなおった。依存関係が更新されてしまうのでちょっと怖いが。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/google/json_serializable.dart/issues/924">https://github.com/google/json_serializable.dart/issues/924</a></p> <p>追記)やっぱ仕様変更したライブラリがあって困ったがそれはまた別の話…</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/18196 2022-05-23T23:59:20+09:00 2022-05-23T23:59:20+09:00 https://crieit.net/posts/docker-compose-set-default-enviroment-variables-20220522 Docker Compose で使用する環境変数にデフォルト値を設定する <p>Docker Compose で使用する <code>.env</code> の環境変数にデフォルト値を設定する方法についてメモ。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>PHP Webアプリケーション用のコンテナからDB用のコンテナに接続する際、DB名やユーザ名・パスワードを任意の値に設定したい。</p> <p>ただし <code>.env</code> は Git 管理にはしたくないので標準では <code>sample.env</code> 等に逃がしており、うっかりファイルをコピー・リネームし忘れていたり、あるいはキー名をタイポして値が消えてしまったりすると困る……ということでデフォルト値を設定できないか、と考えました。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <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: php: build: context: ./_docker/dockerfiles/ dockerfile: Dockerfile working_dir: /var/www command: php -S 0.0.0.0:8080 -t public ports: - 8080:8080 db: image: mariadb restart: always ports: - ${MYSQL_PORT:-3306}:${MYSQL_PORT:-3306} environment: - MYSQL_ROOT_PASSWORD=pwd - MYSQL_DATABASE=${MYSQL_DBNAME:-test} - MYSQL_USER=${MYSQL_USER:-user} - MYSQL_PASSWORD=${MYSQL_PASSWORD:-pwd} </code></pre> <h3 id=".env (サンプル)"><a href="#.env+%28%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%29">.env (サンプル)</a></h3> <pre><code class="env">MYSQL_DBNAME=foo_db MYSQL_USER=foo_user MYSQL_PASSWORD="Password-1234" MYSQL_HOST=192.0.2.1 MYSQL_PORT=3306 </code></pre> <p>早速ですが用意したコード。</p> <p>まず基本的に <code>.env</code> を用意すれば、 <code>${HOGE}</code> の形式で環境変数をセットできます。</p> <p>今回はそこに <code>${HOGE:-FUGA}</code> と、コロン(<code>:</code>)とハイフン(<code>-</code>)を付けた値を追加しました。これにより、「<code>.env</code> に <code>HOGE=PIYO</code> のように <code>HOGE</code> に値がセットされていれば <code>PIYO</code> を、そうでなければデフォルト値として <code>:-</code> の後の文字列 (この場合は <code>FUGA</code>) をセット」となります。</p> <p>例えば上述の <code>- MYSQL_DATABASE=${MYSQL_DBNAME:-test}</code> の部分では、 MariaDB のコンテナイメージに対して <code>MYSQL_DATABASE</code> キーへ <code>.env</code> の <code>MYSQL_DBNAME</code> に値がセットされていればその値を、そうでなければ <code>test</code> をセット、となります。</p> <p>なお <code>-</code> がない場合は empty も許容されるので、空文字列が環境変数にセットされるようです。今回はどれも空文字列では都合が宜しくないので全てハイフンありで統一。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="デフォルト値"><a href="#%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E5%80%A4">デフォルト値</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/fagai/items/b944950b26af19453c02">案外知られてないdocker-composeの環境変数定義の記法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.docker.com/compose/compose-file/#interpolation">Compose specification | Docker Documentation</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://matsuand.github.io/docs.docker.jp.onthefly/compose/environment-variables/">Compose における環境変数 | Docker ドキュメント</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18195 2022-05-23T23:58:56+09:00 2022-05-23T23:58:56+09:00 https://crieit.net/posts/docker-compose-change-enviroment-variables-file-20220523 Docker Compose に渡す環境変数ファイルを変更する <p>Docker Compose に渡す環境変数に .env 以外のファイルから渡したいと考えたのでメモしておきます。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>アプリケーション開発環境等で開発環境用の <code>.development.env</code> と本番環境用の <code>.production.env</code> を用意したとして、この2つのどちらかから Docker Compose に環境変数を渡したい。</p> <p>開発環境なのでアプリ側で使用する環境変数と Dockerコンテナ 内で使用する環境変数を統一したい、と。</p> <p>ただし、デフォルトでは Docker Compose は <code>.env</code> からしか環境変数を読み取ってくれません。これをどうにかしたい。</p> <h2 id="コマンド"><a href="#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89">コマンド</a></h2> <pre><code class="bash">> docker-compose --env-file .development.env up -d </code></pre> <p>ということでコマンドですが、 <code>docker-compose</code> コマンドに <code>--env-file</code> でパラメータを渡してあげると <code>.env</code> 以外のファイルも環境変数のソースとして渡すことができます。</p> <h2 id="(余談) コマンド実行時の --env-file パラメータと .envファイル と docker-compose.yml の env_file キー"><a href="#%28%E4%BD%99%E8%AB%87%29+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E5%AE%9F%E8%A1%8C%E6%99%82%E3%81%AE+--env-file+%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%A8+.env%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB+%E3%81%A8+docker-compose.yml+%E3%81%AE+env_file+%E3%82%AD%E3%83%BC">(余談) コマンド実行時の --env-file パラメータと .envファイル と docker-compose.yml の env_file キー</a></h2> <p>さて、ここから少しややこしい話を。</p> <p>Docker Compose 周りでは環境変数関連で見出しに挙げた3つの方法があるようです。</p> <p><code>.env</code> については Docker Compose で使用できる環境変数ファイル。これは良いですね。</p> <p>この <code>.env</code> をそれ以外のファイル名・ディレクトリのファイルから読み取るように変更できるのが <code>docker-compose</code>コマンド 実行時の <code>--env-file</code> パラメータ。</p> <p>ここまでは <strong><code>docker-compose.yml</code> 内で使用できる</strong> 環境変数です。</p> <p>それでは最後、 <code>docker-compose.yml</code> の <code>env_file</code>キー は何か。</p> <pre><code>version: '3.7' services: db: env_file: - .dvelopment.env </code></pre> <p>最後の <code>env_file</code> は <code>docker-compose.yml</code> 内に記述できるパラメータで、これを使って環境変数のファイルを指定することができます。</p> <p>ただし、このパラメータで指定した環境変数は、 <strong>起動したコンテナ内からは参照できますが、 <code>docker-compose.yml</code> 内からは参照できません</strong>。</p> <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://zenn.dev/rhene/scraps/781fdbecd340d3">docker-composeで環境変数が読み込まれない?</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/SolKul/items/989727aeeafcae28ecf7">docker-composeのenv_fileと.envファイルの違い - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.docker.jp/compose/environment-variables.html">Compose における環境変数 — Docker-docs-ja 19.03 ドキュメント</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://docs.docker.com/compose/environment-variables/">Environment variables in Compose | Docker Documentation</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/18190 2022-05-17T16:16:36+09:00 2022-05-17T19:29:17+09:00 https://crieit.net/posts/Moodle-URL-Google-Forms-Moodle 【Moodle】URL変数を使って、Google FormsにMoodleのコース名を自動入力させる <h2 id="やりたいこと"><a href="#%E3%82%84%E3%82%8A%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8">やりたいこと</a></h2> <ul> <li>Google FormsでMoodleのコースについて受講者にアンケートをとっているが、フォームの入力項目としてMoodleのコース名があり、受講者に手入力してもらっていたために、入力間違いが多々見受けられたのでMoodleのURL変数機能を使って対策をしたい</li> </ul> <h2 id="動作確認したバージョン"><a href="#%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%9F%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3">動作確認したバージョン</a></h2> <ul> <li>Moodle 3.11.4+</li> </ul> <h2 id="手順"><a href="#%E6%89%8B%E9%A0%86">手順</a></h2> <h3 id="1. Google Formsの指定入力項目のURLパラメーターを取得する"><a href="#1.+Google+Forms%E3%81%AE%E6%8C%87%E5%AE%9A%E5%85%A5%E5%8A%9B%E9%A0%85%E7%9B%AE%E3%81%AEURL%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B">1. Google Formsの指定入力項目のURLパラメーターを取得する</a></h3> <h4 id="1-1. 作成済みフォームにて「事前入力したURLを取得」をクリック"><a href="#1-1.+%E4%BD%9C%E6%88%90%E6%B8%88%E3%81%BF%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%AB%E3%81%A6%E3%80%8C%E4%BA%8B%E5%89%8D%E5%85%A5%E5%8A%9B%E3%81%97%E3%81%9FURL%E3%82%92%E5%8F%96%E5%BE%97%E3%80%8D%E3%82%92%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF">1-1. 作成済みフォームにて「事前入力したURLを取得」をクリック</a></h4> <p><a href="https://crieit.now.sh/upload_images/2bc1c94ac131f712416489d452d91b55628344c5a9b1a.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2bc1c94ac131f712416489d452d91b55628344c5a9b1a.png?mw=700" alt="image" /></a></p> <h4 id="1-2. Moodleのコースを入力している項目で適当な値を入れる"><a href="#1-2.+Moodle%E3%81%AE%E3%82%B3%E3%83%BC%E3%82%B9%E3%82%92%E5%85%A5%E5%8A%9B%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E9%A0%85%E7%9B%AE%E3%81%A7%E9%81%A9%E5%BD%93%E3%81%AA%E5%80%A4%E3%82%92%E5%85%A5%E3%82%8C%E3%82%8B">1-2. Moodleのコースを入力している項目で適当な値を入れる</a></h4> <p><a href="https://crieit.now.sh/upload_images/8c7f19e9f05668c57894a6eaeda7819c628344f995418.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8c7f19e9f05668c57894a6eaeda7819c628344f995418.png?mw=700" alt="image" /></a></p> <h4 id="1-3. 一番下の「リンクを取得」をクリックする"><a href="#1-3.+%E4%B8%80%E7%95%AA%E4%B8%8B%E3%81%AE%E3%80%8C%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E5%8F%96%E5%BE%97%E3%80%8D%E3%82%92%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B">1-3. 一番下の「リンクを取得」をクリックする</a></h4> <p><a href="https://crieit.now.sh/upload_images/67c49ff155ceb4e358afd7ddc1c9c9ee62834520a3474.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/67c49ff155ceb4e358afd7ddc1c9c9ee62834520a3474.png?mw=700" alt="image" /></a></p> <h4 id="1-4. 「リンクをコピー」をクリックして生成されたURLを取得する"><a href="#1-4.+%E3%80%8C%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E3%82%B3%E3%83%94%E3%83%BC%E3%80%8D%E3%82%92%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%81%97%E3%81%A6%E7%94%9F%E6%88%90%E3%81%95%E3%82%8C%E3%81%9FURL%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B">1-4. 「リンクをコピー」をクリックして生成されたURLを取得する</a></h4> <p><a href="https://crieit.now.sh/upload_images/045b4e9585d981c97b024554195f6f286283457759799.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/045b4e9585d981c97b024554195f6f286283457759799.png?mw=700" alt="image" /></a></p> <h4 id="1-5. メモ帳などに生成されたURLを貼り付け、URLパラメーターを確認する"><a href="#1-5.+%E3%83%A1%E3%83%A2%E5%B8%B3%E3%81%AA%E3%81%A9%E3%81%AB%E7%94%9F%E6%88%90%E3%81%95%E3%82%8C%E3%81%9FURL%E3%82%92%E8%B2%BC%E3%82%8A%E4%BB%98%E3%81%91%E3%80%81URL%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B">1-5. メモ帳などに生成されたURLを貼り付け、URLパラメーターを確認する</a></h4> <p><code>https://docs.google.com/forms/d/e/xxxxxxxxxxxxxxxxxxx/viewform?usp=pp_url&entry.88458871=hogehoge</code></p> <p>上記の場合は<strong>entry.88458871</strong>が該当項目のURLパラメーターです。<br /> <strong>https://docs.google.com/forms/d/e/xxxxxxxxxxxxxxxxxxx/viewform?usp=pp_url</strong>はURLモジュールの<strong>外部URL</strong>に指定します</p> <h3 id="2. Moodleのコース内にあるGoogle Formsに飛ぶURLモジュールのURL変数を設定する"><a href="#2.+Moodle%E3%81%AE%E3%82%B3%E3%83%BC%E3%82%B9%E5%86%85%E3%81%AB%E3%81%82%E3%82%8BGoogle+Forms%E3%81%AB%E9%A3%9B%E3%81%B6URL%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AEURL%E5%A4%89%E6%95%B0%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B">2. Moodleのコース内にあるGoogle Formsに飛ぶURLモジュールのURL変数を設定する</a></h3> <h4 id="2-1. 外部URLの設定"><a href="#2-1.+%E5%A4%96%E9%83%A8URL%E3%81%AE%E8%A8%AD%E5%AE%9A">2-1. 外部URLの設定</a></h4> <p><a href="https://crieit.now.sh/upload_images/11c35a0a2e30a0952699d4c84bb45ac062834889dd10e.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/11c35a0a2e30a0952699d4c84bb45ac062834889dd10e.png?mw=700" alt="image" /></a><br /> 上記で確認した<strong>https://docs.google.com/forms/d/e/xxxxxxxxxxxxxxxxxxx/viewform?usp=pp_url</strong>を<strong>外部URL</strong>へ入れます。</p> <h4 id="2-2. URL変数の設定"><a href="#2-2.+URL%E5%A4%89%E6%95%B0%E3%81%AE%E8%A8%AD%E5%AE%9A">2-2. URL変数の設定</a></h4> <p><a href="https://crieit.now.sh/upload_images/f0715dd34b715e3fd32dff68ae360a2e628346fb9ddf8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/f0715dd34b715e3fd32dff68ae360a2e628346fb9ddf8.png?mw=700" alt="image" /></a></p> <p>URL変数の&parameter=変数の箇所にこのような形式で先程確認したURLパラメーターの<strong>entry.88458871</strong>と<strong>自動入力させたい情報</strong>(ここでは長いコース名)入れます。</p> <h3 id="3. URLモジュールからGoogle Formsのリンクを踏んで動作チェック"><a href="#3.+URL%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%8B%E3%82%89Google+Forms%E3%81%AE%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E8%B8%8F%E3%82%93%E3%81%A7%E5%8B%95%E4%BD%9C%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF">3. URLモジュールからGoogle Formsのリンクを踏んで動作チェック</a></h3> <p><a href="https://crieit.now.sh/upload_images/52355fdfa98336dec3e566cc5fb6c6d762834a04b1bc3.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/52355fdfa98336dec3e566cc5fb6c6d762834a04b1bc3.png?mw=700" alt="image" /></a></p> <p>Google Formsで指定項目にMoodleのコース名が自動で入力されればOKです</p> arohajiro tag:crieit.net,2005:PublicArticle/18188 2022-05-12T23:28:44+09:00 2022-05-17T11:34:30+09:00 https://crieit.net/posts/RFC3492-Punycode-IDNA-Unicode-Bootstring [RFC3492日本語訳] Punycode: アプリケーションにおいてドメイン名国際化(IDNA)を行うためのUnicodeのBootstringエンコーディング <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>この記事は、RFC3492の日本語訳を転載したものです。Punycodeの仕様を参照する上で、JDNA翻訳版と思われる日本語訳ドキュメントが大いに参考になっていたのですが、2020年末から<a target="_blank" rel="nofollow noopener" href="http://jdna.jp/">http://jdna.jp/</a>へのアクセスができないようで、本ドキュメントも参照できない状態となっていますので、その中身をこちらに転載しています。転載にあたって、一部内容を改変・省略しています。</p> <p>RFC3492原文:<a target="_blank" rel="nofollow noopener" href="https://datatracker.ietf.org/doc/html/rfc3492">https://datatracker.ietf.org/doc/html/rfc3492</a><br /> 日本語訳(転載元Webアーカイブ):<a target="_blank" rel="nofollow noopener" href="https://web.archive.org/web/20201019220316/http://jdna.jp/survey/rfc/rfc3492j.html">https://web.archive.org/web/20201019220316/http://jdna.jp/survey/rfc/rfc3492j.html</a></p> <h1 id="Punycode: アプリケーションにおいてドメイン名国際化(IDNA)を行うためのUnicodeのBootstringエンコーディング"><a href="#Punycode%3A+%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AB%E3%81%8A%E3%81%84%E3%81%A6%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E5%90%8D%E5%9B%BD%E9%9A%9B%E5%8C%96%28IDNA%29%E3%82%92%E8%A1%8C%E3%81%86%E3%81%9F%E3%82%81%E3%81%AEUnicode%E3%81%AEBootstring%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0">Punycode: アプリケーションにおいてドメイン名国際化(IDNA)を行うためのUnicodeのBootstringエンコーディング</a></h1> <p>ネットワークワーキンググループ A. Costello<br /> Request for Comments: 3492 Univ. of California, Berkeley<br /> 分類: 標準化過程<em>(Standards Track)</em> 2003年3月</p> <h2 id="本文書の位置づけ"><a href="#%E6%9C%AC%E6%96%87%E6%9B%B8%E3%81%AE%E4%BD%8D%E7%BD%AE%E3%81%A5%E3%81%91">本文書の位置づけ</a></h2> <p>本文書はインターネットコミュニティーのためにインターネット標準化過程にあるプロトコルを規定し、その向上のために議論と提案を求めるものである。このプロトコルの標準化の状況については"Internet Official Protocol Standards"(STD 1)の最新版を参照のこと。本文書の配布は制限されない。</p> <h2 id="著作権表示"><a href="#%E8%91%97%E4%BD%9C%E6%A8%A9%E8%A1%A8%E7%A4%BA">著作権表示</a></h2> <p>Copyright (C) The Internet Society (2003). All Rights Reserved.</p> <h2 id="要旨"><a href="#%E8%A6%81%E6%97%A8">要旨</a></h2> <p>Punycodeは、単純で効率的な転送用エンコーディング<em>(transfer encoding)</em>記法であり、IDNA (Internationalized Domain Names in Applications)と共に使用されるように設計されている。PunycodeはUnicode文字列を、一意かつ可逆的にASCII文字列に変換する。ASCII文字は、そのまま文字として表現され、非ASCII文字はホスト名ラベルで使用可能なASCII文字(文字、数字、ハイフン)で表現される。本文書は、Bootstringと呼ぶ一般的アルゴリズムを定義する。本アルゴリズムは、より大きな文字集合に属するコードポイントの並びを、基本コードポイントの並びによって一意に表現することを可能にする。Punycodeは、IDNA向けに本文書で規定したパラメーター値を使用する、Bootstringの適用事例<em>(instance)</em>である。</p> <h2 id="1. はじめに"><a href="#1.+%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">1. はじめに</a></h2> <p>IDNAは国際化ドメイン名をサポートするためのアーキテクチャーを記述している。これは非ASCII文字を含むラベルを、特別なACEプレフィックスで始まるASCII文字だけで構成されたACEラベルによって表現可能にするものである。プレフィックスの後に続けられたラベルの残りは、ある制約条件を満たすようにPunycodeでエンコードされたUnicode文字列である。プレフィックスと制約条件の詳細については、IDNAとNAMEPREPを参照のこと。</p> <p>Punycodeは、Bootstringと呼ばれる、より一般的なアルゴリズムの適用事例<em>(instance)</em>である。このアルゴリズムは、より大きな文字集合に属するコードポイントの並びを、小さな"基本<em>(basic)</em>"コードポイントの集合に含まれるコードポイントの並びによって一意に表現可能にするものである。Punycodeは、Bootstringの特定のパラメーター値をIDNA向けに適切に設定したものである。</p> <h2 id="1.1 機能的特徴"><a href="#1.1+%E6%A9%9F%E8%83%BD%E7%9A%84%E7%89%B9%E5%BE%B4">1.1 機能的特徴</a></h2> <p>Bootstringは、以下の機能的特徴を持つように設計された。</p> <ul> <li><p>完全性: 拡張文字列(任意のコードポイントの並び)は、基本文字列(基本コードポイントの並び)によって表現できる。許容される文字列、文字列の長さ等の制約は、上位層で導入可能である。</p></li> <li><p>一意性: 与えられた拡張文字列を表現する基本文字列は一つしか存在しない。</p></li> <li><p>可逆性: 任意の拡張文字列を基本文字列に変換した場合、その基本文字列から変換前の拡張文字列を再生可能である。</p></li> <li><p>効率的エンコーディング: 拡張文字列の長さと、それを表現する基本文字列の長さの比率は小さい。処理対象がドメイン名であることを考慮すると、これは重要な意味を持つ。なぜなら、RFC1034はドメインラベルを63文字に制限しているからである。</p></li> <li><p>単純性: エンコーディングとデコーディングのアルゴリズムは、実装には充分に単純なものである。効率性と単純性のゴールは二律背反するものだが、Bootstringはその間で優れたバランスを取ることを目標としている。</p></li> <li><p>読みやすさ: 拡張文字列に含まれる基本コードポイントは、変換後の基本文字列においてもそのまま表現される。(主たる意図は効率を改善することであり、読みやすさを改善することではないにしても)。</p></li> </ul> <p>PunycodeはIDNAのToASCII処理とToUnicode処理で使用されない付加的機能もサポート可能である。エンコーディング前に拡張文字列の大文字、小文字の文字種が統一されている<em>(case-folded)</em>場合、文字種が統一された文字列をどのように文字種混在の文字列に変換したかを示すために、基本文字列は大文字と小文字が混在した文字列を使用することができる。<a href="#A.%20文字種注釈">付録A "文字種注釈<em>(Mixed-case annotation)</em>"</a>を参照のこと。</p> <h2 id="1.2 プロトコルとの関係"><a href="#1.2+%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82">1.2 プロトコルとの関係</a></h2> <p>PunycodeはドメインラベルをASCIIに変換するために、IDNAプロトコルで使用されるものであり、他の目的のためには設計されていない。任意の文章を処理するために設計されたものではないことをここに明記する。</p> <h2 id="2. 用語"><a href="#2.+%E7%94%A8%E8%AA%9E">2. 用語</a></h2> <p>本文書におけるキーワード"しなければならない<em>(MUST)</em>"、"してはならない<em>(MUST NOT)</em>"、"要求される<em>(REQUIRED)</em>"、"するものとする<em>(SHALL)</em>"、"しないものとする<em>(SHALL NOT)</em>"、"すべきである<em>(SHOULD)</em>"、"すべきでない<em>(SHOULD NOT)</em>"、"推奨される<em>(RECOMMENDED)</em>"、"してもよい<em>(MAY)</em>"、"任意である<em>(OPTIONAL)</em>"は、BCP14、RFC2119に記述されているとおりに解釈される。</p> <p>コードポイントは符号化文字集合<em>(coded character set)</em>において、文字に関連付けられる整数値<em>(integral value)</em>である。</p> <p>Unicode標準に記述されているとおり、Unicodeコードポイントは、"U+"に4桁の16進数を続けることによって表現される。また、コードポイントの範囲は、プレフィックス無しで2つの16進数を".."で分割した表現形式を使用する。</p> <p>演算子divとmodは整数の割り算を実行する。<code>(x div y)</code>はxをyで割った商で、余りは捨てられる。また、<code>(x mod y)</code>はxをyで割った余りである。したがって、<code>(x div y) * y + (x mod y) == x</code> となる。Bootstringはこれらの演算子を負でない演算数に対してだけ使用する。したがって、商と余りは常に負でない値を持つ。</p> <p>break構文は、(C言語のように)一番深い(内側の)ループから脱出するものである。</p> <p>オーバーフローは、整数を保存する変数の最大値を超える値を計算しようとする試みである。</p> <h2 id="3. Bootstringの説明"><a href="#3.+Bootstring%E3%81%AE%E8%AA%AC%E6%98%8E">3. Bootstringの説明</a></h2> <p>Bootstringは、任意のコードポイントの並び("拡張文字列")を、基本コードポイントの並び("基本文字列")として表現する。本セクションは、この表現形式について記述する。<a href="#6.%20Bootstringアルゴリズム">セクション6"Bootstringアルゴリズム"</a>では、このアルゴリズムを擬似的なコードとして提示する。<a href="#7.2%20デコーディング処理の検証">セクション7.2"デコーディング処理の検証"</a>と<a target="_blank" rel="nofollow noopener" href="7.3%20エンコーディング処理の検証">7.3"エンコーディング処理の検証"</a>では、入力例に対するアルゴリズムの処理を検証する。</p> <p>以下のセクションでは、Bootstringが使用する4つの手法を記述する。"基本コードポイントの分離<em>(segregation)</em>"は、拡張文字列に含まれる基本コードポイントを対象とする、非常に単純で効果的なエンコーディングである。これらは、単純に全てが直ちにコピーされる。"挿入による整列復元コーディング<em>(insertion unsort coding)</em>"は、非基本コード<em>(non-basic code point)</em>をdelta(*1)としてエンコードする。その際に、コードポイントの処理は、入力されるコードポイントの並びの順番ではなく、コードポイントの値が数値的に小さいものから順に処理される。この結果、一般的にdeltaはより小さくなる。deltaは、"一般化可変長整数<em>(generalized variable-length integer)</em>"として表現される。これは負でない整数を表現するために、基本コードポイントを使用する。この整数表現のパラメーターは、続くdeltaが同様な大きさ<em>(magnitude)</em>を持つ場合には、効率を改善するために、"bias補正<em>(bias adaptation)</em>"を使用して動的に調整される。</p> <blockquote> <p>《脚注》<br /> *1: delta<br /> 原文に含まれる幾つかの語、delta, bias, tmin, tmax, damp は、後に擬似コード内でそのまま扱われるため、原文のままとした。</p> </blockquote> <h2 id="3.1 基本コードポイントの分離"><a href="#3.1+%E5%9F%BA%E6%9C%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%81%AE%E5%88%86%E9%9B%A2">3.1 基本コードポイントの分離</a></h2> <p>拡張文字列に含まれる基本コードポイントは、全てそのままの文字として原文どおりの順序で基本文字列の先頭にコピーされる。基本コードポイントの数がゼロでない場合(に限っては)、区切り文字<em>(delimiter)</em>が続けられる。区切り文字は、残りの基本文字列には決して現れない特定の基本コードポイントである。したがって、デコーダーは、区切り文字が存在する場合、最後の区切り文字を探すことにより、そのまま文字として表現されている部分の終端部を見つけることができる。</p> <h2 id="3.2 挿入による整列復元コーディング"><a href="#3.2+%E6%8C%BF%E5%85%A5%E3%81%AB%E3%82%88%E3%82%8B%E6%95%B4%E5%88%97%E5%BE%A9%E5%85%83%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0">3.2 挿入による整列復元コーディング</a></h2> <p>基本文字列の残り部分(区切り文字がある場合には、最後の区切り文字よりも後ろの部分)は、<a href="#3.3%20一般化可変長整数">セクション3.3</a>に記述されるとおり、一般化可変長整数として表現された、負でない整数のdeltaの並びである。deltaの意味は、デコーダーの観点から考えるのが最も理解しやすい。</p> <p>デコーダーは拡張文字列を徐々に構築する。初めは、拡張文字列は基本文字列のうち、そのまま文字として使用されている部分をコピーしたものである(ただし、最後の区切り文字は除かれる)。デコーダーは非基本コードポイントを各delta毎に拡張文字列に挿入していき、最終的にはデコードされた文字列に到達する。</p> <p>この処理の主要な部分は、2つの状態変数インデックスiとカウンターnを持つ状態マシン<em>(state machine)</em>である。インデックスiは拡張文字列における位置を示し、0(最初の位置)から拡張文字列の現在の長さ(現在の終端を越えた位置に存在する仮想的な位置)の範囲を採る。現在の状態が<code><n,i></code>である場合、iが拡張文字列長よりも短かければ次の状態は<code><n,i+1></code>となり、iが拡張文字列長と等しければ、次の状態は<code><n+1,0></code>となる。言い換えると、各状態の変化はiを増加<em>(increment)</em>させ、必要に応じてゼロに周回する。nは周回した回数をカウントするものである。</p> <p>状態は常に単調に進むことに注意してもらいたい(デコーダーには現在よりも前の状態に戻す方法は存在しない)。各状態において、挿入処理は行われるか行われないかのどちらかである。任意の状態において行われる挿入処理は、多くとも1回である。挿入処理は、拡張文字列のiの位置に、値nを挿入する。deltaはこの一連のイベントの連続長<em>(run-length)</em>エンコーディングである。つまり、deltaは挿入処理後の状態に先立つ挿入処理前の状態の連続の長さである。したがって、各delta毎に、デコーダーはdelta状態の変更を実行し、挿入処理を行った後に、もう1度状態の変更を行う。(実装は各状態の変更を個別に実行する必要はなく、代わりに割り算を使用し、余りの計算を行い、次の挿入状態を直接演算することができる)。挿入されたコードポイントが基本コードポイントの場合、エラーとなる。(<a href="#3.1%20基本コードポイントの分離">セクション3.1</a>に記述されるとおり、基本コードポイントは分離されているはずだからである)。</p> <p>エンコーダーの主な仕事は、期待する文字列をデコーダーに構築させるdeltaの並びを導出することである。この目標は、拡張文字列を走査してデコーダーが挿入しなければならない次のコードポイントを順次探していき、デコーダーが実行しなければならない状態変更の回数をカウントすることによって達成される。デコーダーから出力される拡張文字列は、挿入処理がされたコードポイントだけしか含まないという事実を心に留めておいてもらいたい。<a href="#6.3%20エンコーディング処理">セクション6.3 "エンコーディング処理"</a>で詳細なアルゴリズムを示す。</p> <h2 id="3.3 一般化可変長整数"><a href="#3.3+%E4%B8%80%E8%88%AC%E5%8C%96%E5%8F%AF%E5%A4%89%E9%95%B7%E6%95%B4%E6%95%B0">3.3 一般化可変長整数</a></h2> <p>従来の整数表現では、base(基数)は数字を識別するために使用できる記号の数であり、記号に対応する値は0からbase-1までである。ここで、digit_0が最も低い桁の値を示し、digit_1が次に低い桁の値、以下同様であるとする。すると、表現される値は、<code>digit_j * w(j)</code> の和となる。ただし、<code>w(j) = base^j</code>は、jの桁の位置に対する重み(倍率)である。例えば、基数8の整数437の場合、桁の値はそれぞれ7, 3, 4であり、重みはそれぞれ1, 8, 64である。したがって、表現される値は<code>7 + 3*8 + 4*64 = 287</code> となる。この表現方法には2つの欠点がある。1つめの欠点は、各値に対して複数のエンコーディングが存在することである。(なぜなら、最上位の桁に余分なゼロを追加できるからである)。これは一意なエンコーディングが必要とされる場合には不便である。2つめの欠点は、整数はそれ自身で区切り文字を持たないことである。このため、複数の整数を続けて記述すると、2つの整数の境界はわからなくなる。</p> <p>一般化可変長表現は、この2つの問題を解決する。桁の値は依然として0からbase-1の範囲だが、この整数はしきい値t(j)によって境界が設定される。各桁それぞれについて、しきい値t(j)は0からbase-1の範囲で設定される。そして、最上位の一桁だけが<code>digit_j < t(j)</code>を必ず満たす。したがって、幾つかの整数が続けて記述されている場合でも、それらを分離することは容易である。リトルエンディアン(最下位桁が先頭にくる)の場合は1桁目から、ビッグエンディアン(最上位桁が先頭に来る)の場合は最後の桁から分離処理を開始すればよい。 既に述べたとおり、値は<code>digit_j * w(j)</code> の和であるが、以下に示すとおり重みが異なる。</p> <pre><code>w(0) = 1 w(j) = w(j-1) * (base - t(j-1)) j > 0の場合 </code></pre> <p>例として、リトルエンディアンのbase 8の数の並び734251...を考える。ここで、しきい値はそれぞれ2, 3, 5, 5, 5, 5, ...であるとする。重みは、それぞれ<code>1</code>, <code>1*(8-2) = 6</code>, <code>6*(8-3) = 30,</code> <code>30*(8-5) = 90</code>, <code>90*(8-5) = 270</code>, 以下同様である。ここで、7は2よりも大きく、3は3より小さくはない。しかし4は5よりも小さいことから、4が最後の数字であることがわかる。734が表現する値は、<code>7*1 + 3*6 + 4*30 = 145</code>である。次の整数は251で、その値は<code>2*1 + 5*6 + 1*30 = 62</code>である。この表現のデコーディングは、従来の整数のデコーディングと極めて似ている。現在の値N = 0、重み w = 1から開始する。次の桁の数dを取り込み、Nを <code>d * w</code> だけ増加させる。dが現在のしきい値(t)よりも小さければ処理を終え、そうでない場合はwを<code>(base - t)</code>の係数で増加させ、次の桁の処理に向けてtを更新する。以後、それを繰り返す。</p> <p>この表現のエンコーディングは従来の整数のエンコーディングと同様である。N < tの場合、Nに対する1桁の数を出力し、処理を終える。そうでない場合、<code>t + ((N - t) mod (base - t))</code>に対する桁の数を出力し、Nを<code>(N - t) div (base - t)</code>に置き換え、次の桁の計算に向けてtを更新するといった処理を繰り返す。</p> <p>t(j)の値が採る範囲がどのようなものであるとしても、負でない整数値それぞれに対して、一般化可変長表現が1つだけ存在する。</p> <p>Bootstringは、deltaを先頭から分割できるように、リトルエンディアンの順序づけを使用する。t(j)の値は、定数base, tmin, tmaxと、biasと呼ばれる状態変数を使用して、以下のように定義される。</p> <pre><code>t(j) = base * (j + 1) - bias 値はtminからtmaxの範囲に制限される。 </code></pre> <p>値を制限するとは、与式がtminより小さい値またはtmaxよりも大きい値を導出した場合、それぞれt(j) = tminまたはtmaxとなることを意味する。(<a href="#6.%20Bootstringアルゴリズム">セクション6 "Bootstringアルゴリズム"</a>で提示する擬似コードでは、パフォーマンス向上のために数式 <code>base * (j + 1)</code>をkと記述している)。このようなt(j)の値は、整数の表現をbiasによって決定される特定の範囲内に納める際に有効に作用する。</p> <h2 id="3.4 bias補正"><a href="#3.4+bias%E8%A3%9C%E6%AD%A3">3.4 bias補正</a></h2> <p>各deltaがエンコードまたはデコードされた後に、次のdeltaに対するbiasを以下のように設定する。</p> <ol> <li><p>次の処理段階におけるオーバーフローを回避するために、deltaを縮小させる。</p> <pre><code>let delta = delta div 2 </code></pre> <p>deltaが最初のdeltaである場合には、除数は2ではなく、代わりにdampと呼ばれる定数を使用する。これにより、2番目に生成されるdeltaは、通常最初のdeltaよりも極めて小さくなるという事実を補正する。</p></li> <li><p>次のdeltaは、より長い文字列に挿入されるという事実にあわせて、deltaを増加させる。</p> <pre><code>let delta = delta + (delta div numpoints) </code></pre> <p>numpointsとは、ここまでの段階でエンコード/デコードされたコードポイントの数である。(このdeltaに対応するものと、基本コードポイントを含む)。</p></li> <li><p>deltaは、次のdeltaを表現するために必要な最小桁数を予測するために、しきい値内に収まるまで繰り返し除算される。</p> <pre><code>while delta > ((base - tmin) * tmax) div 2 do let delta = delta div (base - tmin) </code></pre></li> <li><p>biasの設定</p> <pre><code>let bias = (base * 処理段階3で実行された割り算の回数) + (((base - tmin + 1) * delta) div (delta + skew)) </code></pre> <p>この処理の目指すものは、現在のdeltaが次のdeltaの大まかなサイズのヒントを提供するため、最上位桁である可能性がより高い桁の数値に対するt(j)をtmaxに設定し、最下位桁から上位3桁目である可能性が高い桁まではt(j)をtminに設定する。最後に最上位桁から2番目であると期待される桁に対してはt(j)をtminとtmaxの間の値に設定することである。(これは最上位桁であると予想される桁が不要になるという希望と、その桁が最上位桁ではないという危険をバランスさせるということである)。</p></li> </ol> <h2 id="4. Bootstringのパラメーター"><a href="#4.+Bootstring%E3%81%AE%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC">4. Bootstringのパラメーター</a></h2> <p>任意の基本コードポイントの集合に対して、区切り文字が1つ指定されなければならない。baseは残りの識別可能な基本コードポイント数よりも大きくてはいけない。0からbase-1の範囲で桁に表示される値は、それぞれ個別に、区切り文字ではない基本コードポイントに関連付けられなければならない。幾つかの場合においては、複数のコードポイントが同じ桁表示値を持つ必要がある。例えば、基本文字列が大文字小文字を区別しない<em>(case-insensitive)</em>場合、同じ文字の大文字と小文字は等しいものとして扱われる必要がある。</p> <p>nの初期値は、拡張文字列内に現れ得る非基本コードポイントの最小値よりも大きくてはいけない。</p> <p>残りの5つのパラメーター(tmin, tmax, skew, damp, biasの初期値)は以下の制約を満たさなければならない。</p> <pre><code>0 <= tmin <= tmax <= base-1 skew >= 1 damp >= 2 initial_bias mod base <= base - tmin </code></pre> <p>提示された制約が満たされていれば、これら5つのパラメーターは効率に影響を与えるが、正確さには影響を与えない。これらは経験的に最適な値が定められる。</p> <p>文字種注釈のサポートが求められる場合(<a href="#A.%20文字種注釈">付録A</a>参照)、0からtmax-1までに対応するコードポイントが全て大文字と小文字の形式を持っていることを確認すること。</p> <h2 id="5. Punycode用のパラメーター値"><a href="#5.+Punycode%E7%94%A8%E3%81%AE%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC%E5%80%A4">5. Punycode用のパラメーター値</a></h2> <p>Punycodeは以下のBootstringパラメーター値を使用する。</p> <pre><code>base = 36 tmin = 1 tmax = 26 skew = 38 damp = 700 initial_bias = 72 initial_n = 128 = 0x80 </code></pre> <p>Punycodeに入力する整数は負でないものでなければならないという制約が付加されてはいるが、これらのパラメーターは0..10FFFFの範囲の整数を採るUnicodeコードポイントに対して特にうまく動作するように設計されている。(しかし、UnicodeのUTF-16エンコーディングが使用するために予約されているD800..DFFFは対象ではない)。</p> <p>基本コードポイントはASCIIコードポイント(0..7F)であり、そのうちU+002D(-)は区切り文字である。また、他のコードポイントの幾つかは以下に示すように桁表示値を持つ。</p> <pre><code>コードポイント 桁表示値 ------------ --------------------------- 41..5A (A-Z) = それぞれ0から25に対応 61..7A (a-z) = それぞれ0から25に対応 30..39 (0-9) = それぞれ26から35に対応 </code></pre> <p>ハイフン(マイナス)を区切り文字として使用するということは、Unicode文字列が全て基本コードポイントで構成されている場合にはエンコードされた文字列はハイフン(マイナス)で終わっても良いことを意味する。しかし、IDNAはそのような文字列にエンコードされることを禁止している。エンコードされた文字列はハイフン(マイナス)で始まってもよいが、IDNAは先頭にプレフィックスを追加する。以上の結果、Punycodeを使用するIDNAは、ホスト名ラベルがハイフン(マイナス)で開始、終了しないというRFC952のルールに準拠する。</p> <p>デコーダーは、ある文字について大文字と小文字の形態を(大文字と小文字が混在している状態も含めて)認識できなければならない<em>(MUST)</em>。エンコーダーは、文字種注釈(<a href="#A.%20文字種注釈">付録A</a>参照)を使用していない限り、出力を大文字または小文字だけの形態にすべきである<em>(SHOULD)</em>。</p> <p>おそらくは、ほとんどのユーザーはエンコードされた文字列を直接書いたりタイプしたりはしないだろう(むしろカット&ペーストをするだろうから)。しかし、直接書いたりタイプしたりするユーザーに対しては、以下に示すように視覚的にどちらとも取れる可能性がある文字の集合があることを警告する必要がある。</p> <pre><code>G 6 I l 1 O 0 S 5 U V Z 2 </code></pre> <p>このような不明瞭さは通常文脈から判断して解決される。しかし、Punycodeでエンコードされた文字列の場合、そのような文脈は人間に対して何も示されない。</p> <h2 id="6. Bootstringアルゴリズム"><a href="#6.+Bootstring%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0">6. Bootstringアルゴリズム</a></h2> <p>擬似コードの幾つかの部分は、パラメーターが(Punycodeに適した)ある条件を満たす場合には省略できる。省略可能な部分は{括弧}でくくられており、省略可能な条件の説明が、擬似コードのすぐ後ろに記載される。</p> <p>形式上、コードポイントは整数であるため、擬似コードはコードポイントに対して直接算術的処理が実行可能であると想定している。幾つかのプログラミング言語では、コードポイントと整数を明示的に変換する必要があるかもしれない。</p> <h2 id="6.1 bias補正関数"><a href="#6.1+bias%E8%A3%9C%E6%AD%A3%E9%96%A2%E6%95%B0">6.1 bias補正関数</a></h2> <pre><code>function adapt(delta,numpoints,firsttime): if firsttime then let delta = delta div damp else let delta = delta div 2 let delta = delta + (delta div numpoints) let k = 0 while delta > ((base - tmin) * tmax) div 2 do begin let delta = delta div (base - tmin) let k = k + base end return k + (((base - tmin + 1) * delta) div (delta + skew)) </code></pre> <p>adapt()内部におけるdeltaとkの値変更が、エンコーディング/デコーディング処理の内部で使用されている同名の変数に影響を与えるかについては気にしなくてよい。adapt()を呼び出した後に、呼び出した処理<em>(caller)</em>はそれらの変数の値を読み出すことなく、上書きするからである。</p> <h2 id="6.2 デコーディング処理"><a href="#6.2+%E3%83%87%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86">6.2 デコーディング処理</a></h2> <pre><code>let n = initial_n let i = 0 let bias = initial_bias let output = an empty string indexed from 0 consume all code points before the last delimiter (if there is one) and copy them to output, fail on any non-basic code point if more than zero code points were consumed then consume one more (which will be the last delimiter) while the input is not exhausted do begin let oldi = i let w = 1 for k = base to infinity in steps of base do begin consume a code point, or fail if there was none to consume let digit = the code point's digit-value, fail if it has none let i = i + digit * w, fail on overflow let t = tmin if k <= bias {+ tmin}, or tmax if k >= bias + tmax, or k - bias otherwise if digit < t then break let w = w * (base - t), fail on overflow end let bias = adapt(i - oldi, length(output) + 1, test oldi is 0?) let n = n + i div (length(output) + 1), fail on overflow let i = i mod (length(output) + 1) {if n is a basic code point then fail} insert n into output at position i increment i end </code></pre> <p>(nが基本コードポイントであるか検査している){}括弧に囲まれた記述は、initial_nが全ての基本コードポイントよりも大きい場合(Punycodeではこれは正しい)省略可能である。なぜなら、nは決してinitial_nより小さくはならないからである。</p> <p>tの値がtminからtmaxの範囲に制限される条件下では、tの割り当ての際に{+tmin}部分は常に省略可能である。tの値を制限することにより、bias < k < bias + tmin の場合に制約計算が正しくなくなるが、bias計算方法とパラメーターの制約により、そのような事態は発生しない。</p> <p>デコーダーの状態は単調にしか進展しないため、任意のdeltaに対して1つだけしか表現が存在しない。したがって、与えられた整数の並びを表現可能なエンコードされた文字列は1つしか存在しない。エラー条件は、コードポイントが無効な場合、予期しない入力終了の発生、オーバーフロー発生、基本コードポイントをそのまま文字として扱わずにdeltaを使用してエンコードした場合等である。これらのエラーによってデコーダーの処理が失敗するため、異なる2つの入力から同じ出力を生成することはできない。</p> <p>この性質が無ければ、エンコーディングの一意性を保証するために、出力を再度エンコードし、入力と一致するかを検証する必要があっただろう。</p> <h2 id="6.3 エンコーディング処理"><a href="#6.3+%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86">6.3 エンコーディング処理</a></h2> <pre><code>let n = initial_n let delta = 0 let bias = initial_bias let h = b = the number of basic code points in the input copy them to the output in order, followed by a delimiter if b > 0 {if the input contains a non-basic code point < n then fail} while h < length(input) do begin let m = the minimum {non-basic} code point >= n in the input let delta = delta + (m - n) * (h + 1), fail on overflow let n = m for each code point c in the input (in order) do begin if c < n {or c is basic} then increment delta, fail on overflow if c == n then begin let q = delta for k = base to infinity in steps of base do begin let t = tmin if k <= bias {+ tmin}, or tmax if k >= bias + tmax, or k - bias otherwise if q < t then break output the code point for digit t + ((q - t) mod (base - t)) let q = (q - t) div (base - t) end output the code point for digit q let bias = adapt(delta, h + 1, test h equals b?) let delta = 0 increment h end end increment delta and n end </code></pre> <p>(入力がnより小さい非基本コードポイントを含むか検査している){}括弧に囲まれた記述は、nより小さい全てのコードポイントが基本コードポイントである場合(Punycodeでは、コードポイントが割り当てられていなければこれは正しい)には省略可能である。</p> <p>{}括弧に囲まれている条件 "non-basic" と "or c is basic" 部分は、intial_nが全ての基本コードポイントより大きい場合(Punycodeではこれは正しい)省略可能である。なぜなら、検査されるコードポイントは、決してinitial_nよりも小さくはならないからである。</p> <p>tの値がtminからtmaxの範囲に制限される条件下では、tの割り当ての際に{+tmin}部分は常に省略可能である。tの値を制限することにより、bias < k < bias + tmin の場合に制約計算が正しくなくなるが、bias計算方法とパラメーターの制約により、そのような事態は発生しない。</p> <p>入力に非常に大きな値が含まれている場合または入力が非常に長い場合に、無効な出力の生成を回避するために、オーバーフローの検査が必要である。</p> <p>外側のループの最後にあるdeltaの増加(increment)処理はオーバーフローしない。なぜなら、deltaを増加させるまでは <code>delta < length(input)</code> であり、length(input)は常に表現可能(representable)であると想定されるからである。nの増加処理はオーバーフローする可能性があるが、それは <code>h == length(input)</code>の場合だけである。nがオーバーフローした場合、処理は終了となる。</p> <h2 id="6.4 オーバーフローの処理"><a href="#6.4+%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%95%E3%83%AD%E3%83%BC%E3%81%AE%E5%87%A6%E7%90%86">6.4 オーバーフローの処理</a></h2> <p>IDNAにおいて、有効なIDNAラベル全てをオーバーフロー無しで処理するには26ビットの符号無し<em>(unsigned)</em>整数があれば充分である。27ビットのdeltaを必要とする文字列は、コードポイントの限界(0..10FFFF)を越えるか、ラベル長の限界(63文字)を越えるかのどちらかとなるからである。しかし、入力は必ずしも有効なIDNAラベルではないため、オーバーフローの処理は必要である。</p> <p>プログラミング言語がオーバーフロー検知を提供しない場合、以下に述べる手法を使用することができる。A、B、Cは表現可能な負でない整数で、Cはゼロでないとする。その場合、<code>B > maxint - A</code>の場合にだけA+Bはオーバーフローする。また、<code>B > (maxint - A) div C</code>の場合にだけ<code>A + (B * C)</code>はオーバーフローする。ここで、maxintはmaxint + 1が表現不可能な整数の最大値である。この手法をC言語で実証したものについては<a href="#C.%20Punycodeの実装例">付録C "Punycodeの実装例"</a>を参照のこと。</p> <p><a href="#6.2%20デコーディング処理">セクション6.2</a>と<a href="#6.3%20エンコーディング処理">6.3</a>で示したデコーディングアルゴリズム、エンコーディングアルゴリズムは、オーバーフローが発生した時点でそれを検知し、オーバーフロー処理を行う。別の方法として、オーバーフロー発生を抑制するために、入力に制限を課すことが挙げられる。例えば、エンコーダーが入力されたコードポイントがMを越えていないこと、および入力長がLを越えていないことを確認済みであれば、deltaは<code>(M - initial_n) * (L + 1)</code>を越えることはないため、整数を保存する変数がこの大きな値を表現可能であれば、オーバーフローは発生しない。このオーバーフロー抑制という方法は、オーバーフロー検知を使用する方法に比べて、より多くの制限を入力に課すことになるが、幾つかのプログラミング言語ではより簡便であると見なされるかもしれない。</p> <p>理論的には、デコーダーの場合にも、可変長整数における数字の数を制限することにより(これは、最も内側のループで繰り返しの回数を制限することである)、類似した方法を採ることができる。しかし、与えられたdeltaを表現するために充分な桁数で、(補正処理のために)はるかに大きなdeltaを表現できることが時折ある。したがって、この方法はおそらく、32ビットよりも大きな整数を必要とするだろう。</p> <p>デコーダーが使用できるもう一つの方法として、オーバーフロー発生を許容しておき、最終的に出力される文字を再度エンコーディングし、それをデコーダーの入力と比較することによってオーバーフロー発生を検査するというものが挙げられる。(大文字小文字を区別しないASCII比較を使用して)両者が一致しない場合は、オーバーフローが発生している。この後処理型オーバーフロー検知方式<em>(delayed-detection)</em>は、即時型オーバーフロー検知方式<em>(immediate-detection)</em>に比べて入力に課す制約はないため、幾つかのプログラミング言語ではより簡便であると見なされるかもしれない。</p> <p>実際、デコーダーがINDAのToUnicode処理内部だけでしか使用されないのであれば、オーバーフローを検査する必要は全くない。ToUnicode処理はより上位レベルで再度エンコーディングと比較を実行し、そこで一致しなければPunycodeのデコーダーが失敗したのと同じ結果となるからである。</p> <h2 id="7. Punycode例"><a href="#7.+Punycode%E4%BE%8B">7. Punycode例</a></h2> <h2 id="7.1 文字列の例"><a href="#7.1+%E6%96%87%E5%AD%97%E5%88%97%E3%81%AE%E4%BE%8B">7.1 文字列の例</a></h2> <p>以下に示すPunycodeエンコーディングでは、ACEプレフィックスは表示されない。1行で表示するには長すぎる文字列については、改行が挿入される場所にバックスラッシュが示される。</p> <p>初めに示す幾つかの例は全て"Why can't they just speak in (language)?"という文章を変換したものである。(Michael Kaplanの"地方"ページから無償提供された)。単語の分割点<em>(word break)</em>と句読点については、ドメイン名でしばしばそのように処理されることにあわせて除去した。</p> <p>(A) アラビア語 (エジプト語): <code>ليهمابتكلموشعابي؟</code></p> <pre><code class="txt">u+0644 u+064A u+0647 u+0645 u+0627 u+0628 u+062A u+0643 u+0644 u+0645 u+0648 u+0634 u+0639 u+0631 u+0628 u+064A u+061F Punycode: egbpdaj6bu4bxfgehfvwxn </code></pre> <p>(B) 中国語 (簡体字): <code>他们为什么不说中文</code></p> <pre><code class="txt">u+4ED6 u+4EEC u+4E3A u+4EC0 u+4E48 u+4E0D u+8BF4 u+4E2D u+6587 Punycode: ihqwcrb4cv8a8dqg056pqjye </code></pre> <p>(C) 中国語 (繁体字): <code>他們爲什麽不說中文</code></p> <pre><code class="txt">u+4ED6 u+5011 u+7232 u+4EC0 u+9EBD u+4E0D u+8AAA u+4E2D u+6587 Punycode: ihqwctvzc91f659drss3x8bo0yb </code></pre> <p>(D) チェコ語: <code>Pročprostěnemluvíčesky</code></p> <pre><code class="txt">U+0050 u+0072 u+006F u+010D u+0070 u+0072 u+006F u+0073 u+0074 u+011B u+006E u+0065 u+006D u+006C u+0075 u+0076 u+00ED u+010D u+0065 u+0073 u+006B u+0079 Punycode: Proprostnemluvesky-uyb24dma41a </code></pre> <p>(E) ヘブライ語: <code>למההםפשוטלאמדבריםעברית</code></p> <pre><code class="txt">u+05DC u+05DE u+05D4 u+05D4 u+05DD u+05E4 u+05E9 u+05D5 u+05D8 u+05DC u+05D0 u+05DE u+05D3 u+05D1 u+05E8 u+05D9 u+05DD u+05E2 u+05D1 u+05E8 u+05D9 u+05EA Punycode: 4dbcagdahymbxekheh6e0a7fei0b </code></pre> <p>(F) ヒンズー語 (デバナーガリ文字): <code>यहलोगहिन्दऺक्योंनहऺंबोलसकतेहैं</code></p> <pre><code class="txt">u+092F u+0939 u+0932 u+094B u+0917 u+0939 u+093F u+0928 u+094D u+0926 u+0940 u+0915 u+094D u+092F u+094B u+0902 u+0928 u+0939 u+0940 u+0902 u+092C u+094B u+0932 u+0938 u+0915 u+0924 u+0947 u+0939 u+0948 u+0902 Punycode: i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd </code></pre> <p>(G) 日本語 (漢字と平仮名): <code>なぜみんな日本語を話してくれないのか</code></p> <pre><code class="txt">u+306A u+305C u+307F u+3093 u+306A u+65E5 u+672C u+8A9E u+3092 u+8A71 u+3057 u+3066 u+304F u+308C u+306A u+3044 u+306E u+304B Punycode: n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa </code></pre> <p>(H) 韓国語 (ハングル音節): <code>세계의모든사람들이한국어를이해한다면얼마나좋을까</code></p> <pre><code class="txt">u+C138 u+ACC4 u+C758 u+BAA8 u+B4E0 u+C0AC u+B78C u+B4E4 u+C774 u+D55C u+AD6D u+C5B4 u+B97C u+C774 u+D574 u+D55C u+B2E4 u+BA74 u+C5BC u+B9C8 u+B098 u+C88B u+C744 u+AE4C Punycode: 989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j\ psd879ccm6fea98c </code></pre> <p>(I) ロシア語 (キリル文字): <code>почемужеонинеговорятпорусски</code></p> <pre><code class="txt">U+043F u+043E u+0447 u+0435 u+043C u+0443 u+0436 u+0435 u+043E u+043D u+0438 u+043D u+0435 u+0433 u+043E u+0432 u+043E u+0440 u+044F u+0442 u+043F u+043E u+0440 u+0443 u+0441 u+0441 u+043A u+0438 Punycode: b1abfaaepdrnnbgefbaDotcwatmq2g4l </code></pre> <p>(J) スペイン語: <code>PorquénopuedensimplementehablarenEspañol</code></p> <pre><code class="txt">U+0050 u+006F u+0072 u+0071 u+0075 u+00E9 u+006E u+006F u+0070 u+0075 u+0065 u+0064 u+0065 u+006E u+0073 u+0069 u+006D u+0070 u+006C u+0065 u+006D u+0065 u+006E u+0074 u+0065 u+0068 u+0061 u+0062 u+006C u+0061 u+0072 u+0065 u+006E U+0045 u+0073 u+0070 u+0061 u+00F1 u+006F u+006C Punycode: PorqunopuedensimplementehablarenEspaol-fmd56a </code></pre> <p>(K) ベトナム語: <code>TạisaohọkhôngthểchỉnóitiếngViệt</code></p> <pre><code class="txt">U+0054 u+1EA1 u+0069 u+0073 u+0061 u+006F u+0068 u+1ECD u+006B u+0068 u+00F4 u+006E u+0067 u+0074 u+0068 u+1EC3 u+0063 u+0068 u+1EC9 u+006E u+00F3 u+0069 u+0074 u+0069 u+1EBF u+006E u+0067 U+0056 u+0069 u+1EC7 u+0074 Punycode: TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g </code></pre> <p>次に示す幾つかの例は、全て日本の音楽アーティスト名、歌の名前、テレビ番組名である。これらを例として挙げた理由は、単に著者がたまたま手近にこれらの情報を持っていたからである。(しかし、日本語は英数字、仮名、漢字、それらが多様に混在した文字列の例を提供するのに便利である)。</p> <p>(L) <code>3年B組金八先生</code></p> <pre><code class="txt">u+0033 u+5E74 U+0042 u+7D44 u+91D1 u+516B u+5148 u+751F Punycode: 3B-ww4c5e180e575a65lsy2b </code></pre> <p>(M) <code>安室奈美恵-with-SUPER-MONKEYS</code></p> <pre><code class="txt">u+5B89 u+5BA4 u+5948 u+7F8E u+6075 u+002D u+0077 u+0069 u+0074 u+0068 u+002D U+0053 U+0055 U+0050 U+0045 U+0052 u+002D U+004D U+004F U+004E U+004B U+0045 U+0059 U+0053 Punycode: -with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n </code></pre> <p>(N) <code>Hello-Another-Way-それぞれの場所</code></p> <pre><code class="txt">U+0048 u+0065 u+006C u+006C u+006F u+002D U+0041 u+006E u+006F u+0074 u+0068 u+0065 u+0072 u+002D U+0057 u+0061 u+0079 u+002D u+305D u+308C u+305E u+308C u+306E u+5834 u+6240 Punycode: Hello-Another-Way--fc4qua05auwb3674vfr0b </code></pre> <p>(O) <code>ひとつ屋根の下2</code></p> <pre><code class="txt">u+3072 u+3068 u+3064 u+5C4B u+6839 u+306E u+4E0B u+0032 Punycode: 2-u9tlzr9756bt3uc0v </code></pre> <p>(P) <code>MajiでKoiする5秒前</code></p> <pre><code class="txt">U+004D u+0061 u+006A u+0069 u+3067 U+004B u+006F u+0069 u+3059 u+308B u+0035 u+79D2 u+524D Punycode: MajiKoi5-783gue6qz075azm5e </code></pre> <p>(Q) <code>パフィーdeルンバ</code></p> <pre><code class="txt">u+30D1 u+30D5 u+30A3 u+30FC u+0064 u+0065 u+30EB u+30F3 u+30D0 Punycode: de-jg4avhby1noc0d </code></pre> <p>(R) <code>そのスピードで</code></p> <pre><code class="txt">u+305D u+306E u+30B9 u+30D4 u+30FC u+30C9 u+3067 Punycode: d9juau41awczczp </code></pre> <p>最後に示す例は、ホスト名ラベルに関する既存のルールに反するASCII文字列である。(これはIDNA用の例としては妥当なものではない。なぜなら、IDNAは決して何も制約が無い<em>(pure)</em>ASCIIラベルをエンコードしないからである)。</p> <p>(S) <code>-> $1.00 <-</code></p> <pre><code class="txt">u+002D u+003E u+0020 u+0024 u+0031 u+002E u+0030 u+0030 u+0020 u+003C u+002D Punycode: -> $1.00 <-- </code></pre> <h2 id="7.2 デコーディング処理の検証"><a href="#7.2+%E3%83%87%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86%E3%81%AE%E6%A4%9C%E8%A8%BC">7.2 デコーディング処理の検証</a></h2> <p>以下の検証において、処理を進めるデコーダーの状態は、拡張文字列に含まれるコードポイントを表現する16進数の値の並びで示される。アスタリスクは、一番最後に挿入されたコードポイントの直後に現れ、n(アスタリスク直前の値)とi(アスタリスク直後の値)の両方を示す。他の数値は10進数で表現される。</p> <p><a href="#7.1%20文字列の例">セクション7.1</a>の例(B)をデコーディング処理する場合の検証は以下のとおりである。</p> <p>nは128、iは0、biasは72である。<br /> 入力は"ihqwcrb4cv8a8dqg056pqjye"である。<br /> 区切り文字は存在しないので、拡張文字列は空<em>(empty)</em>で処理が開始される。<br /> delta "ihq"は19853にデコードされる。<br /> biasは21になる。</p> <pre><code>4E0D * </code></pre> <p>delta "wc"は64にデコードされる。<br /> biasは20になる。</p> <pre><code>4E0D 4E2D * </code></pre> <p>delta "rb"は37にデコードされる。<br /> biasは13になる。</p> <pre><code>4E3A * 4E0D 4E2D </code></pre> <p>delta "4c"は56にデコードされる。<br /> biasは17になる。</p> <pre><code>4E3A 4E48 * 4E0D 4E2D </code></pre> <p>delta "v8a"は599にデコードされる。<br /> biasは32になる。</p> <pre><code>4E3A 4EC0 * 4E48 4E0D 4E2D </code></pre> <p>delta "8d"は130にデコードされる。<br /> biasは23になる。</p> <pre><code>4ED6 * 4E3A 4EC0 4E48 4E0D 4E2D </code></pre> <p>delta "qg"は154にデコードされる。<br /> biasは25になる。</p> <pre><code>4ED6 4EEC * 4E3A 4EC0 4E48 4E0D 4E2D </code></pre> <p>delta "056p"は46301にデコードされる。<br /> biasは84になる。</p> <pre><code>4ED6 4EEC 4E3A 4EC0 4E48 4E0D 4E2D 6587 * </code></pre> <p>delta "qjye"は88531にデコードされる。<br /> biasは90になる。</p> <pre><code>4ED6 4EEC 4E3A 4EC0 4E48 4E0D 8BF4 * 4E2D 6587 </code></pre> <p><a href="#7.1%20文字列の例">セクション7.1</a>の例(L)をデコーディング処理する場合の検証は以下のとおりである。</p> <p>nは128、iは0、biasは72である。<br /> 入力は"3B-ww4c5e180e575a65lsy2b"である。<br /> そのまま文字として表現されている部分は"3B-"であるから、以下の拡張文字列<br /> から処理が開始される。</p> <pre><code>0033 0042 </code></pre> <p>delta "ww4c"は62042にデコードされる。<br /> biasは27になる。</p> <pre><code>0033 0042 5148 * </code></pre> <p>delta "5e"は139にデコードされる。<br /> biasは24になる。</p> <pre><code>0033 0042 516B * 5148 </code></pre> <p>delta "180e"は16683にデコードされる。<br /> biasは67になる。</p> <pre><code>0033 5E74 * 0042 516B 5148 </code></pre> <p>delta "575a"は34821にデコードされる。<br /> biasは82になる。</p> <pre><code>0033 5E74 0042 516B 5148 751F * </code></pre> <p>delta "65l"は14592にデコードされる。<br /> biasは67になる。</p> <pre><code>0033 5E74 0042 7D44 * 516B 5148 751F </code></pre> <p>delta "sy2b"は42088にデコードされる。<br /> biasは84になる。</p> <pre><code>0033 5E74 0042 7D44 91D1 * 516B 5148 751F </code></pre> <h2 id="7.3 エンコーディング処理の検証"><a href="#7.3+%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E5%87%A6%E7%90%86%E3%81%AE%E6%A4%9C%E8%A8%BC">7.3 エンコーディング処理の検証</a></h2> <p>以下の検証において、コードポイント値は16進数で表記され、他の数値は10進数で表記される。</p> <p><a href="#7.1%20文字列の例">セクション7.1</a>の例(B)をエンコーディング処理する場合の検証は以下のとおりである。</p> <p>biasは72である。<br /> 入力は以下のとおりであり、基本コードポイントは存在しないので、そのまま文字として表現されている部分は存在しない。</p> <pre><code>4ED6 4EEC 4E3A 4EC0 4E48 4E0D 8BF4 4E2D 6587 </code></pre> <p>挿入されるべき次のコードポイントは4E0Dである。<br /> deltaは19853になる必要があるので、"ihq"とエンコードされる。<br /> biasは21になる。<br /> 挿入されるべき次のコードポイントは4E2Dである。<br /> deltaは64になる必要があるので、"wc"とエンコードされる。<br /> biasは20になる。<br /> 挿入されるべき次のコードポイントは4E3Aである。<br /> deltaは64になる必要があるので、"rb"とエンコードされる。<br /> biasは13になる。<br /> 挿入されるべき次のコードポイントは4E48である。<br /> deltaは64になる必要があるので、"4c"とエンコードされる。<br /> biasは17になる。<br /> 挿入されるべき次のコードポイントは4EC0である。<br /> deltaは599になる必要があるので、"v8a"とエンコードされる。<br /> biasは32になる。<br /> 挿入されるべき次のコードポイントは4ED6である。<br /> deltaは599になる必要があるので、"8d"とエンコードされる。<br /> biasは23になる。<br /> 挿入されるべき次のコードポイントは4EECである。<br /> deltaは154になる必要があるので、"qg"とエンコードされる。<br /> biasは25になる。<br /> 挿入されるべき次のコードポイントは6587である。<br /> deltaは154になる必要があるので、"056p"とエンコードされる。<br /> biasは84になる。<br /> 挿入されるべき次のコードポイントは8BF4である。<br /> deltaは154になる必要があるので、"qjye"とエンコードされる。<br /> biasは90になる。<br /> 出力は"ihqwcrb4cv8a8dqg056pqjye"になる。</p> <p><a href="#7.1%20文字列の例">セクション7.1</a>の例(L)をエンコーディング処理する場合の検証は以下のとおりである。</p> <p>biasは72である。<br /> 入力は以下のとおりである。</p> <pre><code>0033 5E74 0042 7D44 91D1 516B 5148 751F </code></pre> <p>基本コードポイント(0033, 0042)はそのまま文字としてコピーされるので、"3B-"となる。<br /> 挿入されるべき次のコードポイントは5148である。<br /> deltaは62042になる必要があるので、"ww4c"とエンコードされる。<br /> biasは27になる。<br /> 挿入されるべき次のコードポイントは516Bである。<br /> deltaは139になる必要があるので、"5e"とエンコードされる(*2)。<br /> biasは24になる。</p> <blockquote> <p>《脚注》<br /> *2: "5e"とエンコードされる。<br /> 原文では"encodes as 516B"と書かれているが、ここは文脈から原文の記述が誤っていることが明らかであるため、訳文では修正している</p> </blockquote> <p>挿入されるべき次のコードポイントは5E74である。<br /> deltaは16683になる必要があるので、"180e"とエンコードされる。<br /> biasは67になる。<br /> 挿入されるべき次のコードポイントは751Fである。<br /> deltaは34821になる必要があるので、"575a"とエンコードされる。<br /> biasは82になる。<br /> 挿入されるべき次のコードポイントは7D44である。<br /> deltaは14592になる必要があるので、"65l"とエンコードされる。<br /> biasは67になる。<br /> 挿入されるべき次のコードポイントは91D1である。<br /> deltaは42088になる必要があるので、"sy2b"とエンコードされる。<br /> biasは84になる。<br /> 出力は "3B-ww4c5e180e575a65lsy2b"となる。</p> <h2 id="8. セキュリティーに関する考察"><a href="#8.+%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%BC%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E8%80%83%E5%AF%9F">8. セキュリティーに関する考察</a></h2> <p>ユーザーは、DNSにおいて各ドメイン名がそれぞれ単一の権威<em>(authority)</em>によって制御されることを期待する。ドメインラベルとして使用されるUnicode文字列が複数のACEラベルに変換される可能性がある場合、1つの国際化ドメイン名が複数のASCIIドメイン名に変換される可能性がある。複数のASCIIドメイン名はそれぞれ異なる権威によって制御されるかもしれないため、その中の幾つかは、本来他に送られるべきサービスリクエストをハイジャックするような偽装をする可能性がある。これらの可能性を排除するため、PunycodeはUnicode文字列それぞれが単一のエンコーディングを持つように設計されている。</p> <p>しかし、依然として"同じ"文章に対して複数のUnicode表現が存在する可能性が残される。これは"同じ"という様々な定義に由来するものである。この問題は正規化という話題の下で、Unicode標準に対する幾つかの拡張を行う方向で努力がなされている。この作業をドメイン名に対して適用したものがNameprepである。</p> <h2 id="9. 参考文献"><a href="#9.+%E5%8F%82%E8%80%83%E6%96%87%E7%8C%AE">9. 参考文献</a></h2> <h2 id="9.1 必須の参考文献"><a href="#9.1+%E5%BF%85%E9%A0%88%E3%81%AE%E5%8F%82%E8%80%83%E6%96%87%E7%8C%AE">9.1 必須の参考文献</a></h2> <p>[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.</p> <h2 id="9.2 有用な参考文献"><a href="#9.2+%E6%9C%89%E7%94%A8%E3%81%AA%E5%8F%82%E8%80%83%E6%96%87%E7%8C%AE">9.2 有用な参考文献</a></h2> <p>[RFC952] Harrenstien, K., Stahl, M. and E. Feinler, "DOD Internet Host Table Specification", RFC 952, October 1985.</p> <p>[RFC1034] Mockapetris, P., "Domain Names - Concepts and Facilities", STD 13, RFC 1034, November 1987.</p> <p>[IDNA] Faltstrom, P., Hoffman, P. and A. Costello, "Internationalizing Domain Names in Applications (IDNA)", RFC 3490, March 2003.</p> <p>[NAMEPREP] Hoffman, P. and M. Blanchet, "Nameprep: A Stringprep Profile for Internationalized Domain Names (IDN)", RFC 3491, March 2003.</p> <p>[ASCII] Cerf, V., "ASCII format for Network Interchange", RFC 20, October 1969.</p> <p>[PROVINCIAL] Kaplan, M., "The 'anyone can be provincial!' page", <a target="_blank" rel="nofollow noopener" href="http://www.trigeminal.com/samples/provincial.html">http://www.trigeminal.com/samples/provincial.html</a>.</p> <p>[UNICODE] The Unicode Consortium, "The Unicode Standard", <a target="_blank" rel="nofollow noopener" href="http://www.unicode.org/unicode/standard/standard.html">http://www.unicode.org/unicode/standard/standard.html</a>.</p> <h2 id="A. 文字種注釈"><a href="#A.+%E6%96%87%E5%AD%97%E7%A8%AE%E6%B3%A8%E9%87%88">A. 文字種注釈</a></h2> <p>大文字小文字を区別しない文字列に対してPunycodeを使用するために、上位層では、Punycodeエンコーディングに先立ち、文字列の文字種統一を行う必要がある。エンコードされた文字列では、大文字と小文字を混在して使用可能であり、エンコードされた文字列を表示する際に、文字種が統一された文字列をどのように文字種混在の文字列に変換したかを示す注釈として使用される。しかし、文字種注釈はIDNAで規定されるToASCII処理とToUnicode処理では使用されないので、IDNAの実装者はこの付録を無視できることに注意してもらいたい。</p> <p>基本コードポイントは、混在した文字種を直接使用することができる。デコーダーはこれらのコードポイントを文字通りコピーするので、小文字のコードポイントは小文字のまま残され、大文字のコードポイントは大文字のまま残される。非基本コードポイントは、それぞれdeltaで表現される。deltaは基本コードポイントの並びで表現され、その最後で注釈を提供する。大文字であった場合には、(可能であれば)非基本コードポイントを大文字に変換し、小文字であった場合には、(可能であれば)非基本コードポイントを小文字に変換することを示す。</p> <p>これらの注釈は、デコーダーから返されるコードポイントを変更しない。注釈は独立して返され、その処理を呼び出したもの<em>(caller)</em>はその値を使用するか無視するかを選択する。エンコーダーはコードポイントに加えて、注釈を受理できる。しかし注釈はASCII文字の大文字/小文字の形態に影響を与える以外には、出力を変更しない。</p> <p>Punycodeエンコーダーとデコーダーはこれらの注釈をサポートする必要はなく、また上位層もこれらを使用する必要はない。</p> <h2 id="B. 免責条項と使用許諾"><a href="#B.+%E5%85%8D%E8%B2%AC%E6%9D%A1%E9%A0%85%E3%81%A8%E4%BD%BF%E7%94%A8%E8%A8%B1%E8%AB%BE">B. 免責条項と使用許諾</a></h2> <p>本文書全体またはその一部(擬似コードとCコードも含む)について、著者は一切の保証を行わないし、その使用の結果生じたあらゆる損害について責任を負わない。著者はこれらを任意の方法で使用、修正、配布を確定的に許可する<em>(irrevocable permission)</em>。ただし、他者が自由にこれらを使用、修正、配布する権限を損なわないこと、またこれらから派生したものを再配布する際には、著者やバージョン情報に関して誤解を招く情報を含めないことが条件である。また、派生物の使用許諾条件がこれらのものと同様である必要はない。</p> <h2 id="C. Punycodeの実装例"><a href="#C.+Punycode%E3%81%AE%E5%AE%9F%E8%A3%85%E4%BE%8B">C. Punycodeの実装例</a></h2> <pre><code class="c">/* punycode.c from RFC 3492 [http://www.nicemice.net/idn/](http://www.nicemice.net/idn/) Adam M. Costello [http://www.nicemice.net/amc/](http://www.nicemice.net/amc/) This is ANSI C code (C89) implementing Punycode (RFC 3492). */ /************************************************************/ /* Public interface (would normally go in its own .h file): */ enum punycode_status { punycode_success, punycode_bad_input, /* Input is invalid. */ punycode_big_output, /* Output would exceed the space provided. */ punycode_overflow /* Input needs wider integers to process. */ }; typedef unsigned int punycode_uint; typedef unsigned long punycode_uint; enum punycode_status punycode_encode( punycode_uint input_length, const punycode_uint input[], const unsigned char case_flags[], punycode_uint *output_length, char output[] ); /* punycode_encode() converts Unicode to Punycode. The input */ /* is represented as an array of Unicode code points (not code */ /* units; surrogate pairs are not allowed), and the output */ /* will be represented as an array of ASCII code points. The */ /* output string is *not* null-terminated; it will contain */ /* zeros if and only if the input contains zeros. (Of course */ /* the caller can leave room for a terminator and add one if */ /* needed.) The input_length is the number of code points in */ /* the input. The output_length is an in/out argument: the */ /* caller passes in the maximum number of code points that it */ /* can receive, and on successful return it will contain the */ /* number of code points actually output. The case_flags array */ /* holds input_length boolean values, where nonzero suggests that */ /* the corresponding Unicode character be forced to uppercase */ /* after being decoded (if possible), and zero suggests that */ /* it be forced to lowercase (if possible). ASCII code points */ /* are encoded literally, except that ASCII letters are forced */ /* to uppercase or lowercase according to the corresponding */ /* uppercase flags. If case_flags is a null pointer then ASCII */ /* letters are left as they are, and other code points are */ /* treated as if their uppercase flags were zero. The return */ /* value can be any of the punycode_status values defined above */ /* except punycode_bad_input; if not punycode_success, then */ /* output_size and output might contain garbage. */ enum punycode_status punycode_decode( punycode_uint input_length, const char input[], punycode_uint *output_length, punycode_uint output[], unsigned char case_flags[] ); /* punycode_decode() converts Punycode to Unicode. The input is */ /* represented as an array of ASCII code points, and the output */ /* will be represented as an array of Unicode code points. The */ /* input_length is the number of code points in the input. The */ /* output_length is an in/out argument: the caller passes in */ /* the maximum number of code points that it can receive, and */ /* on successful return it will contain the actual number of */ /* code points output. The case_flags array needs room for at */ /* least output_length values, or it can be a null pointer if the */ /* case information is not needed. A nonzero flag suggests that */ /* the corresponding Unicode character be forced to uppercase */ /* by the caller (if possible), while zero suggests that it be */ /* forced to lowercase (if possible). ASCII code points are */ /* output already in the proper case, but their flags will be set */ /* appropriately so that applying the flags would be harmless. */ /* The return value can be any of the punycode_status values */ /* defined above; if not punycode_success, then output_length, */ /* output, and case_flags might contain garbage. On success, the */ /* decoder will never need to write an output_length greater than */ /* input_length, because of how the encoding is defined. */ /**********************************************************/ /* Implementation (would normally go in its own .c file): */ /*** Bootstring parameters for Punycode ***/ enum { base = 36, tmin = 1, tmax = 26, skew = 38, damp = 700, initial_bias = 72, initial_n = 0x80, delimiter = 0x2D }; /* basic(cp) tests whether cp is a basic code point: */ /* delim(cp) tests whether cp is a delimiter: */ /* decode_digit(cp) returns the numeric value of a basic code */ /* point (for use in representing integers) in the range 0 to */ /* base-1, or base if cp is does not represent a value. */ static punycode_uint decode_digit(punycode_uint cp) { return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base; } /* encode_digit(d,flag) returns the basic code point whose value */ /* (when used for representing integers) is d, which needs to be in */ /* the range 0 to base-1. The lowercase form is used unless flag is */ /* nonzero, in which case the uppercase form is used. The behavior */ /* is undefined if flag is nonzero and digit d has no uppercase form. */ static char encode_digit(punycode_uint d, int flag) { return d + 22 + 75 * (d < 26) - ((flag != 0) << 5); /* 0..25 map to ASCII a..z or A..Z */ /* 26..35 map to ASCII 0..9 */ } /* flagged(bcp) tests whether a basic code point is flagged */ /* (uppercase). The behavior is undefined if bcp is not a */ /* basic code point. */ /* encode_basic(bcp,flag) forces a basic code point to lowercase */ /* if flag is zero, uppercase if flag is nonzero, and returns */ /* the resulting code point. The code point is unchanged if it */ /* is caseless. The behavior is undefined if bcp is not a basic */ /* code point. */ static char encode_basic(punycode_uint bcp, int flag) { bcp -= (bcp - 97 < 26) << 5; return bcp + ((!flag && (bcp - 65 < 26)) << 5); } /*** Platform-specific constants ***/ /* maxint is the maximum value of a punycode_uint variable: */ static const punycode_uint maxint = -1; /* Because maxint is unsigned, -1 becomes the maximum value. */ /*** Bias adaptation function ***/ static punycode_uint adapt( punycode_uint delta, punycode_uint numpoints, int firsttime ) { punycode_uint k; delta = firsttime ? delta / damp : delta >> 1; /* delta >> 1 is a faster way of doing delta / 2 */ delta += delta / numpoints; for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) { delta /= base - tmin; } return k + (base - tmin + 1) * delta / (delta + skew); } /*** Main encode function ***/ enum punycode_status punycode_encode( punycode_uint input_length, const punycode_uint input[], const unsigned char case_flags[], punycode_uint *output_length, char output[] ) { punycode_uint n, delta, h, b, out, max_out, bias, j, m, q, k, t; /* Initialize the state: */ n = initial_n; delta = out = 0; max_out = *output_length; bias = initial_bias; /* Handle the basic code points: */ for (j = 0; j < input_length; ++j) { if (basic(input[j])) { if (max_out - out < 2) return punycode_big_output; output[out++] = case_flags ? encode_basic(input[j], case_flags[j]) : input[j]; } /* else if (input[j] < n) return punycode_bad_input; */ /* (not needed for Punycode with unsigned code points) */ } h = b = out; /* h is the number of code points that have been handled, b is the */ /* number of basic code points, and out is the number of characters */ /* that have been output. */ if (b > 0) output[out++] = delimiter; /* Main encoding loop: */ while (h < input_length) { /* All non-basic code points < n have been */ /* handled already. Find the next larger one: */ for (m = maxint, j = 0; j < input_length; ++j) { /* if (basic(input[j])) continue; */ /* (not needed for Punycode) */ if (input[j] >= n && input[j] < m) m = input[j]; } /* Increase delta enough to advance the decoder's */ /* <n,i> state to <m,0>, but guard against overflow: */ if (m - n > (maxint - delta) / (h + 1)) return punycode_overflow; delta += (m - n) * (h + 1); n = m; for (j = 0; j < input_length; ++j) { /* Punycode does not need to check whether input[j] is basic: */ if (input[j] < n /* || basic(input[j]) */ ) { if (++delta == 0) return punycode_overflow; } if (input[j] == n) { /* Represent delta as a generalized variable-length integer: */ for (q = delta, k = base; ; k += base) { if (out >= max_out) return punycode_big_output; t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */ k >= bias + tmax ? tmax : k - bias; if (q < t) break; output[out++] = encode_digit(t + (q - t) % (base - t), 0); q = (q - t) / (base - t); } output[out++] = encode_digit(q, case_flags && case_flags[j]); bias = adapt(delta, h + 1, h == b); delta = 0; ++h; } } ++delta, ++n; } *output_length = out; return punycode_success; } /*** Main decode function ***/ enum punycode_status punycode_decode( punycode_uint input_length, const char input[], punycode_uint *output_length, punycode_uint output[], unsigned char case_flags[] ) { punycode_uint n, out, i, max_out, bias, b, j, in, oldi, w, k, digit, t; /* Initialize the state: */ n = initial_n; out = i = 0; max_out = *output_length; bias = initial_bias; /* Handle the basic code points: Let b be the number of input code */ /* points before the last delimiter, or 0 if there is none, then */ /* copy the first b code points to the output. */ for (b = j = 0; j < input_length; ++j) if (delim(input[j])) b = j; if (b > max_out) return punycode_big_output; for (j = 0; j < b; ++j) { if (case_flags) case_flags[out] = flagged(input[j]); if (!basic(input[j])) return punycode_bad_input; output[out++] = input[j]; } /* Main decoding loop: Start just after the last delimiter if any */ /* basic code points were copied; start at the beginning otherwise. */ for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) { /* in is the index of the next character to be consumed, and */ /* out is the number of code points in the output array. */ /* Decode a generalized variable-length integer into delta, */ /* which gets added to i. The overflow checking is easier */ /* if we increase i as we go, then subtract off its starting */ /* value at the end to obtain delta. */ for (oldi = i, w = 1, k = base; ; k += base) { if (in >= input_length) return punycode_bad_input; digit = decode_digit(input[in++]); if (digit >= base) return punycode_bad_input; if (digit > (maxint - i) / w) return punycode_overflow; i += digit * w; t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */ k >= bias + tmax ? tmax : k - bias; if (digit < t) break; if (w > maxint / (base - t)) return punycode_overflow; w *= (base - t); } bias = adapt(i - oldi, out + 1, oldi == 0); /* i was supposed to wrap around from out+1 to 0, */ /* incrementing n each time, so we'll fix that now: */ if (i / (out + 1) > maxint - n) return punycode_overflow; n += i / (out + 1); i %= (out + 1); /* Insert n at position i of the output: */ /* not needed for Punycode: */ /* if (decode_digit(n) <= base) return punycode_invalid_input; */ if (out >= max_out) return punycode_big_output; if (case_flags) { memmove(case_flags + i + 1, case_flags + i, out - i); /* Case of last character determines uppercase flag: */ case_flags[i] = flagged(input[in - 1]); } memmove(output + i + 1, output + i, (out - i) * sizeof *output); output[i++] = n; } *output_length = out; return punycode_success; } /******************************************************************/ /* Wrapper for testing (would normally go in a separate .c file): */ /* For testing, we'll just set some compile-time limits rather than */ /* use malloc(), and set a compile-time option rather than using a */ /* command-line option. */ enum { unicode_max_length = 256, ace_max_length = 256 }; static void usage(char **argv) { fprintf(stderr, "\n" "%s -e reads code points and writes a Punycode string.\n" "%s -d reads a Punycode string and writes code points.\n" "\n" "Input and output are plain text in the native character set.\n" "Code points are in the form u+hex separated by whitespace.\n" "Although the specification allows Punycode strings to contain\n" "any characters from the ASCII repertoire, this test code\n" "supports only the printable characters, and needs the Punycode\n" "string to be followed by a newline.\n" "The case of the u in u+hex is the force-to-uppercase flag.\n" , argv[0], argv[0]); exit(EXIT_FAILURE); } static void fail(const char *msg) { fputs(msg,stderr); exit(EXIT_FAILURE); } static const char too_big[] = "input or output is too large, recompile with larger limits\n"; static const char invalid_input[] = "invalid input\n"; static const char overflow[] = "arithmetic overflow\n"; static const char io_error[] = "I/O error\n"; /* The following string is used to convert printable */ /* characters between ASCII and the native charset: */ static const char print_ascii[] = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" " !\"#$%&'()*+,-./" "0123456789:;<=>?" "@ABCDEFGHIJKLMNO" "PQRSTUVWXYZ[\\]^_" "`abcdefghijklmno" "pqrstuvwxyz{|}~\n"; int main(int argc, char **argv) { enum punycode_status status; int r; unsigned int input_length, output_length, j; unsigned char case_flags[unicode_max_length]; if (argc != 2) usage(argv); if (argv[1][0] != '-') usage(argv); if (argv[1][2] != 0) usage(argv); if (argv[1][1] == 'e') { punycode_uint input[unicode_max_length]; unsigned long codept; char output[ace_max_length+1], uplus[3]; int c; /* Read the input code points: */ input_length = 0; for (;;) { r = scanf("%2s%lx", uplus, &codept;); if (ferror(stdin)) fail(io_error); if (r == EOF || r == 0) break; if (r != 2 || uplus[1] != '+' || codept > (punycode_uint)-1) { fail(invalid_input); } if (input_length == unicode_max_length) fail(too_big); if (uplus[0] == 'u') case_flags[input_length] = 0; else if (uplus[0] == 'U') case_flags[input_length] = 1; else fail(invalid_input); input[input_length++] = codept; } /* Encode: */ output_length = ace_max_length; status = punycode_encode(input_length, input, case_flags, &output;_length, output); if (status == punycode_bad_input) fail(invalid_input); if (status == punycode_big_output) fail(too_big); if (status == punycode_overflow) fail(overflow); assert(status == punycode_success); /* Convert to native charset and output: */ for (j = 0; j < output_length; ++j) { c = output[j]; assert(c >= 0 && c <= 127); if (print_ascii[c] == 0) fail(invalid_input); output[j] = print_ascii[c]; } output[j] = 0; r = puts(output); if (r == EOF) fail(io_error); return EXIT_SUCCESS; } if (argv[1][1] == 'd') { char input[ace_max_length+2], *p, *pp; punycode_uint output[unicode_max_length]; /* Read the Punycode input string and convert to ASCII: */ fgets(input, ace_max_length+2, stdin); if (ferror(stdin)) fail(io_error); if (feof(stdin)) fail(invalid_input); input_length = strlen(input) - 1; if (input[input_length] != '\n') fail(too_big); input[input_length] = 0; for (p = input; *p != 0; ++p) { pp = strchr(print_ascii, *p); if (pp == 0) fail(invalid_input); *p = pp - print_ascii; } /* Decode: */ output_length = unicode_max_length; status = punycode_decode(input_length, input, &output;_length, output, case_flags); if (status == punycode_bad_input) fail(invalid_input); if (status == punycode_big_output) fail(too_big); if (status == punycode_overflow) fail(overflow); assert(status == punycode_success); /* Output the result: */ for (j = 0; j < output_length; ++j) { r = printf("%s+%04lX\n", case_flags[j] ? "U" : "u", (unsigned long) output[j] ); if (r < 0) fail(io_error); } return EXIT_SUCCESS; } usage(argv); return EXIT_SUCCESS; /* not reached, but quiets compiler warning */ } </code></pre> <h2 id="著者の連絡先"><a href="#%E8%91%97%E8%80%85%E3%81%AE%E9%80%A3%E7%B5%A1%E5%85%88">著者の連絡先</a></h2> <p>Adam M. Costello<br /> University of California, Berkeley<br /> <a target="_blank" rel="nofollow noopener" href="http://www.nicemice.net/amc/">http://www.nicemice.net/amc/</a></p> <h2 id="完全な著作権表示"><a href="#%E5%AE%8C%E5%85%A8%E3%81%AA%E8%91%97%E4%BD%9C%E6%A8%A9%E8%A1%A8%E7%A4%BA">完全な著作権表示</a></h2> <p>Copyright (C) The Internet Society (2003). All Rights Reserved.</p> <p>This document and translations of it may be copied and furnished to<br /> others, and derivative works that comment on or otherwise explain it<br /> or assist in its implementation may be prepared, copied, published<br /> and distributed, in whole or in part, without restriction of any<br /> kind, provided that the above copyright notice and this paragraph are<br /> included on all such copies and derivative works. However, this<br /> document itself may not be modified in any way, such as by removing<br /> the copyright notice or references to the Internet Society or other<br /> Internet organizations, except as needed for the purpose of<br /> developing Internet standards in which case the procedures for<br /> copyrights defined in the Internet Standards process must be<br /> followed, or as required to translate it into languages other than<br /> English.</p> <p>The limited permissions granted above are perpetual and will not be<br /> revoked by the Internet Society or its successors or assigns.</p> <p>This document and the information contained herein is provided on an<br /> "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING<br /> TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING<br /> BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION<br /> HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF<br /> MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.</p> <h2 id="謝辞"><a href="#%E8%AC%9D%E8%BE%9E">謝辞</a></h2> <p>RFCエディターの活動に対する資金は現在Internet Societyによって提供されている。</p> ウラル 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/18185 2022-05-05T20:03:03+09:00 2022-05-05T20:03:03+09:00 https://crieit.net/posts/2021-2028-7-8-CAGR 産業用自動化ソフトウェア市場は、2021年から2028年までの予測期間中に7.8%のCAGRに達すると推定されています <p>世界の 産業用自動化ソフトウェアの市場 Emergen Researchの最新の分析によると、クラウドベースの産業用自動化ソフトウェアの採用の増加は、2021-2028年の市場収益の成長を促進すると予想されます。さらに、人的エラーを減らし、時間効率を高めるための産業用自動化ソフトウェアの必要性が高まると、市場の成長が促進されます。自動化ソフトウェアの需要は急速に高まっています。クラウドベースの産業用自動化ソフトウェアの採用は、石油やガス、化学薬品、医薬品、自動車、機械製造など、さまざまな業界で急速に増加しています。このようなソフトウェアは、さまざまな出力デバイスを介してさまざまな自動システムに安全にアクセスできるようにし、さらに企業がコンピューターを使用してリモートでデータにアクセスできるようにします。これらは、世界の産業用自動化ソフトウェア市場の収益の成長を促進するいくつかの主要な要因です。</p> <p>産業用自動化ソフトウェア市場の成長に影響を与え、業界の成長に影響を与える要因は、レポートで広く研究されています。レポートは、COVID-19パンデミックを産業自動化ソフトウェア市場の主要な影響要因の1つと見なしています。レポートでは、パンデミックが産業用自動化ソフトウェア業界に与えるプラスとマイナスの影響について詳しく説明しています。サプライチェーンの混乱と経済の不安定さは、産業用自動化ソフトウェア業界のダイナミクスを変えました。レポートは、市場とその主要なセグメントおよび地域に対するパンデミックの影響を調査します。また、COVID-19後のシナリオにおける市場成長の予測予測も提供します。</p> <p>レポートの無料サンプルPDFコピーにアクセスするには、ここをクリックしてください@ <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/request-sample/885">https://www.emergenresearch.com/request-sample/885</a><br /> レポート<br /> ヒューマンマシンインターフェース(HMI)セグメントのいくつかの重要なハイライトは、実装と保守の必要性が高まっているため、予測期間中に非常に堅調な収益成長率を記録すると予想されます産業用自動化ソフトウェアの展開の増加の結果としてのサービス。<br /> オンプレミス展開はデバイスにより高いレベルの安全性とセキュリティを提供するため、エンドユーザーの間でオンプレミス産業自動化ソフトウェアの採用が増加するため、オンプレミスセグメントの収益は予測期間中に非常に急速なCAGRで拡大すると予想されます。<br /> シーメンスAG、SAP SE、シュナイダーエレクトリックSE、およびとりわけABBLtd.<br /> レポートの説明と市場レポートの目次をご覧ください@ <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/industry-report/industrial-automation-software-market">https://www.emergenresearch.com/industry-report/industrial-automation-software-market</a><br /> レポートはさらに、新たな成長の機会、課題、市場の脅威、制限、および産業自動化の成長を制限する可能性のある要因に光を当てますソフトウェア市場。レポートはさらに、公海の市場とそれらの地域の新たな傾向について詳細に説明しています。また、競争力のある風景、市場の推進力、産業シナリオ、および最新の製品と技術開発への洞察を提供し、産業用自動化ソフトウェアの市場風景の包括的な概要を提供します。<br /> 産業用自動化ソフトウェアの調査レポートには、業界の著名なプレーヤーの洞察に満ちた調査と、彼らが採用したビジネスの概要、戦略的計画、およびビジネス拡大計画も含まれています。これは、読者と事業主が戦略的拡大と投資計画を策定するのに役立ちます。レポートは、合併と買収、合弁事業、コラボレーション、パートナーシップ、企業と政府の取引などに焦点を当てています。レポートはまた、これらの著名なプレーヤーが市場の主要地域で争っている拡大についても語っています。レポートは、これらの企業によって行われた技術および製品開発の詳細な分析に焦点を当てています。<br /> インダストリアルオートメーションソフトウェアレポートで調査された主要企業は、<br /> Siemens AG、Hitachi Ltd.、General Electric Company、Honeywell International Inc.、SAP SE、Schneider Electric SE、ABB Ltd.、HCL Technologies Limited、Rockwell Automation Inc.、およびParsecです。オートメーション株式会社。<br /> 支払いを行う[独占コピーを購入]@[https ://www.emergenresearch.com/select-license/885](https ://www.emergenresearch.com/select-license/885)<br /> 地域のセグメンテーション<br /> 地域分析に基づいて、市場は次の地域にセグメント化されます: 北米、ラテンアメリカ、ヨーロッパ、アジア太平洋、および中東とアフリカ。 市場は、ヨーロッパ諸国が密接に続く北米諸国によって支配されると予想されます。アジアパシフィックは、最近の進歩と研究開発部門への投資の増加により、大幅な成長を示すと予想されています。<br /> 主要な市場ソリューションを入手するには、以下のリンクにアクセスして ください。[https//www.emergenresearch.com/industry-report/industrial-automation-software-market](https //www.emergenresearch.com/industry-report/industrial-automation-software-market )<br /> Emergen Researchは、タイプ、展開、最終用途に基づいてグローバルな産業用自動化ソフトウェアをセグメント化しました。 、および地域:<br /> タイプOutlook(収益、10億米ドル、2018〜 2028年)<br /> 監視制御およびデータ取得(SCADA)<br /> 分散制御システム(DCS)<br /> 製造実行システム(MES)<br /> ヒューマンマシンインターフェース(HMI)<br /> プログラマブルロジックコントローラー(PLC)<br /> 展開の見通し(収益、10億米ドル、2018〜2028年)<br /> オンプレミス<br /> のオンクラウド<br /> 最終用途の見通し(収益、10億米ドル、2018〜2028年)<br /> プロセス産業<br /> 石油およびガス<br /> 化学品<br /> 紙およびパルプ<br /> 医薬品<br /> 鉱業および金属<br /> 食品および飲料<br /> 電力<br /> その他<br /> ディスクリート産業<br /> 自動車<br /> 機械製造<br /> 半導体および電子機器<br /> 航空宇宙および防衛<br /> 医療機器<br /> その他<br /> このレポートは、次の重要なポイントに対応しています<br /> 。 レポートは、市場の推進力、制約、および産業用自動化ソフトウェア市場の将来の機会の予測を提供します<br /> ・ レポートはさらに変化する市場のダイナミクスを分析します<br /> ・ 市場の成長を支配すると予想される地域とセグメントの分析による市場の地域分析とセグメンテーション<br /> ・ 主要な競合他社のプロファイルを使用した広範な競合状況のマッピング<br /> ・ 主要企業が採用したM&Aなどの事業戦略とコラボレーションの詳細な分析<br /> ・ 収益予測、国の範囲、アプリケーションの洞察、および製品の洞察<br /> レポートのカスタマイズをリクエストする@ <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/request-for-customization/885">https://www.emergenresearch.com/request-for-customization/885</a><br /> レポートをお読みいただきありがとうございます。このレポートのカスタマイズに関する具体的な詳細については、お問い合わせください。取得したレポートがお客様のニーズに適していることを確認します。<br /> EmergenResearchReportsをもっと見る@ <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/">https://www.emergenresearch.com/</a><br /> 抗体薬物複合体市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.biospace.com/article/antibody-drug-conjugates-market-size-to-reach-usd-20-01-billion-in-">https://www.biospace.com/article/antibody-drug-conjugates-market-size-to-reach-usd-20-01-billion-in-</a> 2028-成長率-25-8%-to-emergen-research /</p> <p>エンボロセラピー市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.biospace.com/article/embolotherapy-market-size-to-reach-usd-7-13-billion-in-2028-growing-at-a-cagr-of-9-3-パーセント-emergen-research/によると">https://www.biospace.com/article/embolotherapy-market-size-to-reach-usd-7-13-billion-in-2028-growing-at-a-cagr-of-9-3-パーセント-emergen-research/によると</a></p> <p>植込み型除細動器市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.biospace.com/article/implantable-cardioverter-defiberlator-market-size-to-reach-usd-8-81-billion-in-2028-growing-at-a-cagr-">https://www.biospace.com/article/implantable-cardioverter-defiberlator-market-size-to-reach-usd-8-81-billion-in-2028-growing-at-a-cagr-</a> of-2-7-percent-to-emergen-research/</p> <p>ナノロボティクス市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.biospace.com/article/-nanorobotics-market-size-to-reach-usd-14-03-billion-in-2028-growing-at-a-cagr-of-10-9">https://www.biospace.com/article/-nanorobotics-market-size-to-reach-usd-14-03-billion-in-2028-growing-at-a-cagr-of-10-9</a> -パーセント-emergen-research/による</p> <p>ヘルスケア流通市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.biospace.com/article/healthcare-distribution-market-size-to-reach-usd-1-384-59-billion-in-2028-growing-at-a-cagr-of">https://www.biospace.com/article/healthcare-distribution-market-size-to-reach-usd-1-384-59-billion-in-2028-growing-at-a-cagr-of</a> -7-0-percent-to-emergen-research/</p> <p>ハイブリッド手術室市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.biospace.com/article/hybrid-operating-room-market-size-to-reach-usd-2-366-9-million-in-2028-growing-at-a-">https://www.biospace.com/article/hybrid-operating-room-market-size-to-reach-usd-2-366-9-million-in-2028-growing-at-a-</a> cagr-of-2-7-percent-acording-to-emergen-research /</p> <p>私たちについて:<br /> Emergen Researchは、シンジケート調査レポート、カスタマイズされた調査レポート、およびコンサルティングサービスを提供する市場調査およびコンサルティング会社です。当社のソリューションは、人口統計、業界全体の消費者行動の変化を特定、ターゲティング、分析するというお客様の目的に純粋に焦点を当てており、クライアントがよりスマートなビジネス上の意思決定を行えるように支援します。ヘルスケア、タッチポイント、化学物質、タイプ、エネルギーなど、複数の業界にわたる関連性のある事実に基づく調査を保証するマーケットインテリジェンス調査を提供します。私たちは、クライアントが市場に存在する最新のトレンドを認識できるように、常に調査内容を更新しています。 Emergen Researchには、さまざまな専門分野の経験豊富なアナリストの強力な基盤があります。私たちの業界経験と研究問題に対する具体的な解決策を開発する能力は、クライアントにそれぞれの競合他社に対して優位性を確保する能力を提供します。<br /> 問い合わせ:<br /> EricLee<br /> コーポレートセールススペシャリスト<br /> EmergenResearch | Web: www.emergenresearch.com<br /> 直通電話:+1(604)757-9756<br /> 電子メール:sales@emergenresearch.com<br /> 詳細情報をご覧ください: https<br /> //www.emergenresearch.com/insights カスタムインテリジェンスサービスを | 成長コンサルティングサービス<br /> Facebook | LinkedIn | Twitter | ブログは<br /> 完全なプレスリリースを読む@ https://www.emergenresearch.com/press-release/global-industrial-automation-software-market<br /> 産業用自動化ソフトウェア市場規模、<br /> 産業用自動化ソフトウェア市場の需要、<br /> 産業用自動化ソフトウェア市場の成長、<br /> 産業用自動化ソフトウェア市場の収益、<br /> 産業用自動化ソフトウェア市場予測、</p> jaypal dhangar tag:crieit.net,2005:PublicArticle/18184 2022-05-04T05:16:16+09:00 2022-05-15T23:54:05+09:00 https://crieit.net/posts/Docker-Kubernetes-gitLab DockerとKubernetes <h1 id="0 前提"><a href="#0+%E5%89%8D%E6%8F%90">0 前提</a></h1> <p>Oracle LinuxとWindows</p> <h1 id="1 環境構築"><a href="#1+%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89">1 環境構築</a></h1> <p>準備</p> <h2 id="1-1 Dockerオフラインインストール"><a href="#1-1+Docker%E3%82%AA%E3%83%95%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">1-1 Dockerオフラインインストール</a></h2> <p>参考にしたURLは<code>https://qiita.com/kod314/items/e574ac12c23598e0d903</code>。<br /> オンラインインストールの場合は、<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/kichise/items/f8e56c6d2d08eaf4a6a0">https://qiita.com/kichise/items/f8e56c6d2d08eaf4a6a0</a><br /> の手順に従えば可能。</p> <p>今回はオフラインインストールを実行しようと思う。社内規則によりDockerをインストールするサーバがインターネット接続できない場合がある。その場合、インターネット接続できる自席端末からネットに接続してDockerのインストーラーをダウンロード。それをDockerをインストールしたいサーバに転送して実行させる。</p> <h3 id="1-1-1 OS確認"><a href="#1-1-1+OS%E7%A2%BA%E8%AA%8D">1-1-1 OS確認</a></h3> <p>まずはOSのversionを確認。</p> <pre><code>$ uname -a Linux publicpc1 5.4.17-2136.305.5.3.el8uek.x86_64 #2 SMP Thu Mar 17 10:45:33 PDT 2022 x86_64 x86_64 x86_64 GNU/Linux </code></pre> <p>より詳細</p> <pre><code>#cat /etc/os-release NAME="Oracle Linux Server" VERSION="8.5" ID="ol" ID_LIKE="fedora" VARIANT="Server" VARIANT_ID="server" VERSION_ID="8.5" PLATFORM_ID="platform:el8" PRETTY_NAME="Oracle Linux Server 8.5" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:oracle:linux:8:5:server" HOME_URL="https://linux.oracle.com/" BUG_REPORT_URL="https://bugzilla.oracle.com/" ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8" ORACLE_BUGZILLA_PRODUCT_VERSION=8.5 ORACLE_SUPPORT_PRODUCT="Oracle Linux" ORACLE_SUPPORT_PRODUCT_VERSION=8.5 </code></pre> <p>Linuxカーネルの確認</p> <pre><code>$ hostnamectl Static hostname: publicpc1 Icon name: computer-vm Chassis: vm Machine ID: 4c3dc5bfa43b47b8b66789ce778a0711 Boot ID: 226a196eedc746e2848a6f15e9afeb0b Virtualization: kvm Operating System: Oracle Linux Server 8.5 CPE OS Name: cpe:/o:oracle:linux:8:5:server Kernel: Linux 5.4.17-2136.305.5.3.el8uek.x86_64 Architecture: x86-64 </code></pre> <p>x86_64と記載されているので、Intel(AMD)の64bitオペレーティングシステムであることがわかる。</p> <h3 id="1-1-2 Docker engineをインストール"><a href="#1-1-2+Docker+engine%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">1-1-2 Docker engineをインストール</a></h3> <p>Docker本体であるDocker Engineと、同時に複数のコンテナを操作するツールであるDocker Composeをダウンロードする。<br /> 参考にした公式:https://docs.docker.com/engine/install/binaries/</p> <p>まずはdocker本体<br /> <a target="_blank" rel="nofollow noopener" href="https://download.docker.com/linux/static/stable/x86_64/">https://download.docker.com/linux/static/stable/x86_64/</a><br /> →バージョンがいろいろあるので、最新のdocker-20.10.9.tgz をダウンロードしてみた。<br /> 自分の端末(クライアント側にダウンロードしたファイルをLinuxサーバに転送してから解凍する。</p> <pre><code>$ ls -al /home/opc/docker-20.10.9.tgz -rw-r--r--. 1 opc opc 63350495 May 4 04:23 /home/opc/docker-20.10.9.tgz </code></pre> <p>展開</p> <pre><code>$ tar zxvf docker-20.10.9.tgz </code></pre> <p>すると、カレントディレクトリにdockerディレクトリが作成される。<br /> dockerディレクトリの中身を<code>/usr/bin/</code>配下にコピーする。</p> <pre><code>$ sudo cp docker/* /usr/bin/ </code></pre> <p>試しに起動するには</p> <pre><code>$ sudo dockerd & </code></pre> <h3 id="1-1-3 Docker Composeをインストール"><a href="#1-1-3+Docker+Compose%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">1-1-3 Docker Composeをインストール</a></h3> <p>続いてはdocker-compose<br /> インストール手順は以下に記載されているので以降この指示に従う。<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/docker/compose/blob/v2/README.md">https://github.com/docker/compose/blob/v2/README.md</a></p> <p>さきほど(1-1-1節)でOSとCPUを確認したので、<br /> それに従って<code>docker-compose-linux-x86_64</code>をダウンロードする。<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/docker/compose/releases">https://github.com/docker/compose/releases</a></p> <p>自分のwindows端末(クライアント側)にダウンロードした<code>docker-compose-linux-x86_64</code>をdockerをインストールしたLinuxサーバに転送。<br /> 転送したら、バイナリファイルの名前を<code>docker-compose</code>にリネーム。</p> <pre><code>$ mv docker-compose-linux-x86_64 docker-compose </code></pre> <p>docker-composeをいどうする。移動する先は目的に応じて移動先が異なる。<br /> 今回は</p> <pre><code>$ sudo mv docker-compose /usr/local/bin/ $ sudo chmod +x /usr/local/bin/docker-compose $ docker-compose -v Docker Compose version v2.5.0 </code></pre> <h3 id="1-1-4 Docker イメージのオフラインで利用する"><a href="#1-1-4+Docker+%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E3%82%AA%E3%83%95%E3%83%A9%E3%82%A4%E3%83%B3%E3%81%A7%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B">1-1-4 Docker イメージのオフラインで利用する</a></h3> <pre><code>$ tar zxvf helloworld_img.tar.gz $ docker load < helloworld_img # dockerイメージをload $ docker images # hello-worldイメージが存在することを確認 </code></pre> <h3 id="1-1-5 Dockerグループ作成"><a href="#1-1-5+Docker%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E4%BD%9C%E6%88%90">1-1-5 Dockerグループ作成</a></h3> <p>Docker daemonはrootユーザで実行しなければならず、毎回sudoを使うのは面倒である。<br /> その場合、dockerグループを作成してそこに任意のユーザを所属させてあげる。</p> <pre><code>groupadd docker usermod -aG docker opc </code></pre> <h3 id="1-1-6 トラブルシュート"><a href="#1-1-6+%E3%83%88%E3%83%A9%E3%83%96%E3%83%AB%E3%82%B7%E3%83%A5%E3%83%BC%E3%83%88">1-1-6 トラブルシュート</a></h3> <p>docklerをサービスでenebleにしようと思ったができなかった。</p> <pre><code>[root@publicpc1 system]# systemctl enable docker Failed to enable unit: Unit file docker.service does not exist. </code></pre> <p>以下のサイトを参考に対応してみた。<br /> <a target="_blank" rel="nofollow noopener" href="https://jhooq.com/docker-daemon-centos/">https://jhooq.com/docker-daemon-centos/</a></p> <p>/usr/lib/systemd/system配下にdocker.socketファイルを作成し以下の内容を記載した。</p> <pre><code>[Unit] Description=Docker Socket for the API [Socket] ListenStream=/var/run/docker.sock SocketMode=0660 SocketUser=root SocketGroup=docker [Install] WantedBy=sockets.target </code></pre> <p>続いて、/usr/lib/systemd/system/配下に<code>docker.service</code>を作成する。</p> <pre><code>[Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target docker.socket firewalld.service containerd.service Wants=network-online.target Requires=docker.socket containerd.service [Service] Type=notify #the default is not to use systemd for cgroups because the delegate issues still #exists and systemd currently does not support the cgroup feature set required #for containers run by docker ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID TimeoutSec=0 RestartSec=2 Restart=always #Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229. #Both the old, and new location are accepted by systemd 229 and up, so using the old location #to make them work for either version of systemd. StartLimitBurst=3 #Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230. #Both the old, and new name are accepted by systemd 230 and up, so using the old name to make #this option work for either version of systemd. StartLimitInterval=60s #Having non-zero Limit*s causes performance problems due to accounting overhead #in the kernel. We recommend using cgroups to do container-local accounting. LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity #Comment TasksMax if your systemd version does not support it. #Only systemd 226 and above support this option. TasksMax=infinity #set delegate yes so that systemd does not reset the cgroups of docker containers Delegate=yes #kill only the docker process, not all processes in the cgroup KillMode=process OOMScoreAdjust=-500 [Install] WantedBy=multi-user.target </code></pre> <p>この中身をみると<code>Requires=docker.socket containerd.service</code>と記載されている通り、containerd.serviceが必要。なので/usr/lib/systemd/system配下にcontainerd.serviceサービスを作成する必要がある。</p> <pre><code>#Copyright The containerd Authors. #Licensed under the Apache License, Version 2.0 (the "License"); #you may not use this file except in compliance with the License. #You may obtain a copy of the License at #http://www.apache.org/licenses/LICENSE-2.0 #Unless required by applicable law or agreed to in writing, software #distributed under the License is distributed on an "AS IS" BASIS, #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #See the License for the specific language governing permissions and #limitations under the License. [Unit] Description=containerd container runtime Documentation=https://containerd.io After=network.target local-fs.target [Service] ExecStartPre=-/sbin/modprobe overlay ExecStart=/usr/bin/containerd Type=notify Delegate=yes KillMode=process Restart=always RestartSec=5 #Having non-zero Limit*s causes performance problems due to accounting overhead #in the kernel. We recommend using cgroups to do container-local accounting. LimitNPROC=infinity LimitCORE=infinity LimitNOFILE=infinity #Comment TasksMax if your systemd version does not supports it. #Only systemd 226 and above support this version. TasksMax=infinity OOMScoreAdjust=-999 [Install] WantedBy=multi-user.target </code></pre> <p>上記を作成する。</p> <p>ちゃんとリロードしとく。</p> <pre><code>systemctl daemon-reload </code></pre> <h2 id="1-2.Kubernetesの準備"><a href="#1-2.Kubernetes%E3%81%AE%E6%BA%96%E5%82%99">1-2.Kubernetesの準備</a></h2> <h3 id="1-2-1. インストール"><a href="#1-2-1.+%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">1-2-1. インストール</a></h3> <p>オフライン環境でKubernetesを構築する手順。<br /> 手順の参考にしたのは以下<br /> <a target="_blank" rel="nofollow noopener" href="https://docs.genesys.com/Documentation/GCXI/latest/Dep/DockerOffline">https://docs.genesys.com/Documentation/GCXI/latest/Dep/DockerOffline</a></p> <p>まずはネットにつながる端末から</p> <pre><code>$ pwd /etc/yum.repos.d $ cat kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg </code></pre> <p>以下のコマンドを実行してファイルをダウンロード。<br /> <code><your_rpm_dir></code>の部分は自分で適当なディレクトリを作成して<br /> そこのパスを指定。</p> <pre><code>yumdownloader --assumeyes --destdir= --resolve yum-utils kubeadm-1.21.* kubelet-1.21.* kubectl-1.21.* ebtables </code></pre> <p>続いて、ダウンロードしたファイルをインストールしていく。</p> <pre><code>yum install -y --cacheonly --disablerepo=* /*.rpm </code></pre> <p>インストールする際、バージョンが異なるファイルが存在してインストールエラーになるかもしれない。<br /> その場合は、必要のないバージョンのファイルは削除してから<br /> 再度インストールするとうまくいく。<br /> うまくいくと以下のようになる。</p> <pre><code>Complete! </code></pre> <p>必要なイメージが揃っているか確認してみよう。</p> <pre><code>$ kubeadm config images list I0504 23:15:36.977910 309762 version.go:254] remote version is much newer: v1.24.0; falling back to: stable-1.21 k8s.gcr.io/kube-apiserver:v1.21.12 k8s.gcr.io/kube-controller-manager:v1.21.12 k8s.gcr.io/kube-scheduler:v1.21.12 k8s.gcr.io/kube-proxy:v1.21.12 k8s.gcr.io/pause:3.4.1 k8s.gcr.io/etcd:3.4.13-0 k8s.gcr.io/coredns/coredns:v1.8.0 </code></pre> <h3 id="1-2-2. kubectlの概要"><a href="#1-2-2.+kubectl%E3%81%AE%E6%A6%82%E8%A6%81">1-2-2. kubectlの概要</a></h3> <p>参考URL:https://kubernetes.io/ja/docs/reference/kubectl/_print/<br /> Kubernetesクラスタを操作するコマンド</p> <h1 id="2.Docker入門"><a href="#2.Docker%E5%85%A5%E9%96%80">2.Docker入門</a></h1> kawai_mizugorou tag:crieit.net,2005:PublicArticle/18182 2022-05-02T18:40:08+09:00 2022-05-02T18:40:08+09:00 https://crieit.net/posts/2021-2028-21-1-CAGR リテールクラウド市場は、2021年から2028年までの予測期間中に21.1%のCAGRに達すると推定されています <p>世界の 小売クラウド市場 規模は2028年に1099.8億米ドルに達し、予測期間中に21.1%のCAGRを記録すると予想されています。従来の小売業に比べて小売業のクラウドによって提供される多くのメリット、幅広いアプリケーションを提供する急速な技術の進歩、eコマースセクターの台頭などの要因が市場の収益成長を促進しています。</p> <p>ハイブリッドクラウドの展開により、予測期間中に大幅に高速な収益CAGRが記録されると予想されます。ハイブリッドクラウドにより、データとアプリケーションを2つの環境間で移動できます。今日の顧客中心の市場での関連性を維持するには、既存の顧客を維持し、新しい顧客を獲得するためのシームレスな顧客体験を提供することが重要であり、それを実現するには柔軟なクラウドインフラストラクチャが不可欠です。ハイブリッドクラウドは、パブリックおよびプライベートの展開と比較して、柔軟性、展開オプション、セキュリティ、コンプライアンスなどの複数の利点を組織に提供します。<br /> さらに、レポートは、市場の成長を促進または阻害する可能性のある要因の包括的な分析を提供します今後数年間で。レポートは、現在展開しているCOVID-19パンデミックを主要な市場影響力者と見なしています。レポートは、パンデミックがリテールクラウド市場とその重要なセグメントに与える全体的な影響の完全な見積もりを提供します。レポートはまた、市場のさまざまな地域にわたるパンデミックの影響についても説明します。また、パンデミックがリテールクラウド市場に与える影響の現在および将来の評価も提供します。<br /> レポートのサンプルを入手@ <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/request-sample/684">https://www.emergenresearch.com/request-sample/684</a><br /> レポートからのいくつかの重要なハイライト<br /> 2020年6月、Microsoft Corp.は、Microsoft Storeの物理的な場所の閉鎖を含む、小売業の戦略的変更を発表しました。同社の小売チームのメンバーは、引き続きMicrosoft Corporate施設の顧客にサービスを提供し、遠隔地への販売、トレーニング、およびサポートを提供し続けます。 Microsoftはまた、Microsoft.comのデジタルストアフロント、XboxとWindowsのストアへの投資を継続し、190の市場で毎月12億人以上にリーチする計画を発表しました。<br /> 2019年4月、オラクルはOracleCommerceCloudのバージョン19AMPをリリースしました。この新しいバージョンは、店頭で直接価格を編集したり、変更のフィルタリングされたリストを公開したり、地理的位置に基づいてパーソナライズされた重要なマーケティングおよびマーチャンダイジング機能を追加したりする機能など、機能効率が追加された多数の新機能を提供します。<br /> 北米は2020年に最大の収益シェアを占め、予測期間中も首位を維持すると予想されます。着実な収益成長は、小売市場の急速な成長、小売クラウドベースのソリューションに対する需要の増加、およびこの地域の国々における主要な主要プレーヤーの存在に起因する可能性があります。さらに、組織は顧客にパーソナライズされた店内ショッピング体験を提供するようにシフトしています。これは、この地域の市場の成長を促進すると予想される主要な要因です。<br /> レポートはさらに小売クラウド市場をタイプ、アプリケーション、エンドユーザー産業、技術、市場の主要地域などの主要セグメントに分割します。レポートはまた、リテールクラウド市場で有望な成長を示しているセグメントと地域に光を当てます。<br /> 主要な市場ソリューションを入手するには、以下のリンクにアクセスして ください。https<br /> 、サービス、展開、および地域に基づいて小売クラウド市場をセグメント化しました。<br /> ソリューション(収益、10億米ドル、2018〜 2028年)<br /> サプライチェーン管理<br /> 顧客管理<br /> マーチャンダイジング<br /> 労働力管理<br /> レポートおよび分析<br /> データセキュリティ<br /> オムニチャネル<br /> その他<br /> サービスの見通し(収益、10億米ドル、2018〜 2028年)<br /> SaaS<br /> PaaS<br /> IaaS<br /> 展開の見通し(収益、10億米ドル、 2018-2028)<br /> パブリッククラウド<br /> プライベートクラウド<br /> ハイブリッドクラウド<br /> 地域の見通し(収益、10億米ドル、2018-2028)<br /> ・ 北米<br /> 1。 米国<br /> 2。 カナダ<br /> 3。 メキシコ<br /> ・ ヨーロッパ<br /> 1。 英国<br /> 2。 ドイツ<br /> 3。 フランス<br /> 4。 イタリア<br /> 5。 スペイン<br /> 6。 ベネルクス<br /> ・ アジア太平洋<br /> 1。 中国<br /> 2。 インド<br /> 3。 日本<br /> 4。 韓国<br /> 5。 APACの残りの部分<br /> ・ ラテンアメリカ<br /> 1。 ブラジル<br /> 2。 LATAMの残りの部分<br /> ・ 中東およびアフリカ<br /> 1。 サウジアラビア<br /> 2。 アラブ首長国連邦<br /> 3。 イスラエル<br /> 4。 南アフリカ<br /> 5。 トルコ<br /> 6。 MEAの残りの部分<br /> はレポートの割引を要求します@ <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/request-discount/684">https://www.emergenresearch.com/request-discount/684</a><br /> レポートはまた、ビジネス戦略、M&A活動、投資計画、製品ポートフォリオ、財務状況、粗利益率の分析とともに、リテールクラウド市場の主要企業を調査します。 、および生産能力と製造能力。<br /> レポートで分析されたプレーヤーには、<br /> Oracle Corporation、Cisco Systems、Inc.、SAP SE、International Business Machines Corporation、Microsoft Corporation、Fujitsu Limited、Infor、Inc.、Epicor Software Corporation、JDA Software Group、Inc.、およびSyntelがあります。レポート<br /> の詳細については、@にアクセスしてください <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/industry-report/retail-cloud-market">https://www.emergenresearch.com/industry-report/retail-cloud-market</a><br /> レポートによって回答された重要な質問:<br /> 今後数年間でどの地域が市場を支配すると予想されますか?<br /> 市場で起こっている最近の技術と製品の進歩は何ですか?<br /> リテールクラウド市場の著名なプレーヤーが採用した主要な戦略は何ですか?<br /> リテールクラウド業界の主要な製品タイプとアプリケーションは何ですか?<br /> SWOT分析とポーターのファイブフォース分析の結果は何ですか?<br /> リテールクラウド市場の競争環境はどうですか?<br /> レポートのカスタマイズをリクエスト@ <a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/request-for-customization/684">https://www.emergenresearch.com/request-for-customization/684</a><br /> レポートをお読みいただきありがとうございます。レポートは、クライアントの要件に応じてカスタマイズできます。カスタマイズの詳細やお問い合わせは、弊社までご連絡ください。お客様のニーズに最適なレポートをご提供いたします。<br /> EmergenResearchReportsをもっと見る@<a target="_blank" rel="nofollow noopener" href="https://www.emergenresearch.com/">https://www.emergenresearch.com/</a><br /> グラフデータベース市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565501942/graph-database-market-demand-and-growth-analysis-with-forecast-up-to-2028">https://www.einpresswire.com/article/565501942/graph-database-market-demand-and-growth-analysis-with-forecast-up-to-2028</a></p> <p>ゲーミングコントローラー市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565502414/gaming-controller-market-to-grow-at-cagr-of-8-9-during-forecast-period-observes-emergen-research-study">https://www.einpresswire.com/article/565502414/gaming-controller-market-to-grow-at-cagr-of-8-9-during-forecast-period-observes-emergen-research-study</a></p> <p>予測および処方分析市場<br /> [https://www.einpresswire.com/article/565586480/predictive-and-prescriptive-analytics-market-to-surpass-valuation-of-usd-64-58-bn-by-2028-notes -emergen-research-study](https://www.einpresswire.com/article/565586480/predictive-and-prescriptive-analytics-market-to-surpass-valuation-of-usd-64-58-bn-by-2028-notes -emergen-research-study)</p> <p>スマートフードマーケット<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565586666/smart-food-market-research-by-emergen-trends-key-players-type-and-application-future-growth-to-2028">https://www.einpresswire.com/article/565586666/smart-food-market-research-by-emergen-trends-key-players-type-and-application-future-growth-to-2028</a></p> <p>IoTセキュリティ市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565586795/iot-security-market-booming-demand-leading-to-exponential-cagr-growth-by-2028">https://www.einpresswire.com/article/565586795/iot-security-market-booming-demand-leading-to-exponential-cagr-growth-by-2028</a></p> <p>スマートキッチン家電市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565586976/smart-kitchen-appliances-market-demand-and-growth-analysis-with-forecast-up-to-2028">https://www.einpresswire.com/article/565586976/smart-kitchen-appliances-market-demand-and-growth-analysis-with-forecast-up-to-2028</a></p> <p>次世代ディスプレイ材料市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565587382/next-generation-display-materials-market-to-surpass-valuation-of-usd-380-03-bn-in-2028-ノート-emergen-research-study">https://www.einpresswire.com/article/565587382/next-generation-display-materials-market-to-surpass-valuation-of-usd-380-03-bn-in-2028-ノート-emergen-research-study</a></p> <p>戦場管理システム市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565666505/battlefield-management-systems-market-to-witness-robust-growth-by-2027-top-players-the-boeing-company-bae-システム">https://www.einpresswire.com/article/565666505/battlefield-management-systems-market-to-witness-robust-growth-by-2027-top-players-the-boeing-company-bae-システム</a></p> <p>カウンターUAS市場<br /> <a target="_blank" rel="nofollow noopener" href="https://www.einpresswire.com/article/565666509/counter-uas-market-worth-4-27-bn-globally-by-2027-at-26-8-cagr-exclusive-report-by">https://www.einpresswire.com/article/565666509/counter-uas-market-worth-4-27-bn-globally-by-2027-at-26-8-cagr-exclusive-report-by</a> -emergen-研究</p> <p>私たちについて:<br /> Emergen Researchは、シンジケート調査レポート、カスタマイズされた調査レポート、およびコンサルティングサービスを提供する市場調査およびコンサルティング会社です。当社のソリューションは、人口統計、業界全体の消費者行動の変化を特定、ターゲティング、分析するというお客様の目的に純粋に焦点を当てており、クライアントがよりスマートなビジネス上の意思決定を行えるように支援します。ヘルスケア、タッチポイント、化学薬品、タイプ、エネルギーなど、複数の業界にわたる関連性のある事実に基づく調査を保証するマーケットインテリジェンス調査を提供します。私たちは、クライアントが市場に存在する最新のトレンドを認識できるように、常に調査内容を更新しています。 Emergen Researchには、さまざまな専門分野の経験豊富なアナリストの強力な基盤があります。私たちの業界経験と研究問題に対する具体的な解決策を開発する能力は、クライアントにそれぞれの競合他社に対して優位性を確保する能力を提供します。<br /> 問い合わせ:<br /> EricLee<br /> コーポレートセールススペシャリスト<br /> EmergenResearch | Web:<a target="_blank" rel="nofollow noopener" href="www.emergenresearch.com">www.emergenresearch.com</a><br /> 直通電話:+1(604)757-9756<br /> 電子メール:sales@emergenresearch.com<br /> 詳細情報をご覧ください:[https//www.emergenresearch.com/insights]( https //www.emergenresearch.com/insights )カスタムインテリジェンスサービスを | 成長コンサルティングサービス<br /> Facebook | LinkedIn | Twitter | ブログは<br /> 完全なプレスリリースを読む@ https://www.emergenresearch.com/press-release/global-retail-cloud-market</p> <p>リテールクラウド市場のサイズ、<br /> リテールクラウド市場の需要、<br /> リテールクラウド市場の成長、<br /> リテールクラウド市場の収益、<br /> リテールクラウド市場の予測、</p> jaypal dhangar tag:crieit.net,2005:PublicArticle/18181 2022-04-30T00:03:10+09:00 2022-04-30T00:03:10+09:00 https://crieit.net/posts/omit-jquery-note-20220502 脱jQuery メモ <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>Bootstrap でブレークポイント未満(スマホ時)のナビゲーションリンクにアンカーリンクがある場合、アンカーリンクをタップしてもナビゲーションリンクのメニューが開いたままアンカーリンクへ遷移するので、それを制御するコードを自前で書いていました。その部分を脱 jQuery したのでメモしておきます。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <h3 id="jQuery"><a href="#jQuery">jQuery</a></h3> <p>まずは Bootstrap 4 までの jQuery でのコード。</p> <pre><code class="js">// ナビゲーションバー const $navbar = $('#navbar'); // ブランド名とドロップダウンコンポーネント以外のナビゲーションリスト $navbar.find('.navbar-brand, .nav-item:not(.dropdown) a, .dropdown-item').on('click', function (e) { // 略 const navBarListID = 'navbarList'; if ($(e.currentTarget).closest('#' + navBarListID).length > 0) { let breakpoint = 0; if (/(^|\s)navbar-expand-(\S*)/g.test($navbar.children('.navbar').prop('class'))) { switch (RegExp.$2) { case 'sm': breakpoint = 576; break; case 'md': breakpoint = 768; break; case 'lg': breakpoint = 992; break; case 'xl': breakpoint = 1200; break; default: breakpoint = 0; break; } } if ($(window).outerWidth() < breakpoint && !$navbar.find('.navbar-toggler[data-target="#' + navBarListID + '"]').hasClass('collapsed')) { // 現在の表示がハンバーガーメニューの場合 $navbar.find('.navbar-toggler[data-target="#' + navBarListID + '"]').trigger('click'); // 移動したらハンバーガーを折りたたむ } else if ($(e.currentTarget).hasClass('dropdown-item') && $(e.currentTarget).closest('.dropdown').hasClass('show')) { // 現在の表示がハンバーガーメニューではなく、ドロップダウン内のメニューをクリックした場合 $(e.currentTarget).closest('.dropdown').trigger('click'); // 移動したらドロップダウンを折りたたむ } } return false; } }; </code></pre> <p>ざっくりこんなコードでした。やっていることとしては</p> <ul> <li>ブランド(<code>.navbar-brand</code>), ドロップダウンではないナビゲーションリンク(<code>.nav-item:not(.dropdown) a</code>, ドロップダウン内のリンク(<code>.dropdown-item</code>) がクリックされた場合 <ul> <li>クリック(タップ)された要素の直近の祖先要素に <code>#navbarList</code> の id属性 がある要素が存在する場合 <ul> <li>ナビゲーションバー要素のクラスにある <code>.navbar-expand-XX</code> のクラスのブレークポイントの文字列を取得してブレークポイント値をセット</li> <li>その値と現在のウィンドウの幅を比較してウインドウ幅の方が小さい (=ハンバーガーメニューに表示が切り替わっている) 、かつ <code>.navbar-toggler</code> 要素が <code>.collapsed</code> のclass属性 を持っている (=メニュー展開) 場合 <ul> <li>ハンバーガーメニューを折りたたむ (ハンバーガーアイコンを1度クリックする)</li> </ul></li> <li>またはクリックされた要素が <code>.dropdown-item</code> class属性 を持っている (=ドロップダウンメニュー) 、かつ直近の祖先要素で <code>.dropdown</code> class属性 を持つ要素が <code>.show</code> class属性 を持っている (=ドロップダウンが開かれている) 場合 <ul> <li>ドロップダウン要素を折りたたむ</li> </ul></li> </ul></li> </ul></li> </ul> <p>という挙動。</p> <h3 id="プレーン JavaScript"><a href="#%E3%83%97%E3%83%AC%E3%83%BC%E3%83%B3+JavaScript">プレーン JavaScript</a></h3> <p>これを Bootstrap 5 対応でプレーンな JavaScirpt に書き換え。</p> <pre><code class="js">const navbar = document.querySelector('#navbar'); const navBarListID = 'navbarList'; if ( typeof e.currentTarget.closest(`#${navBarListID}`) !== 'undefined' && e.currentTarget.closest(`#${navBarListID}`) !== null ) { let breakpoint = 0; if (/(^|\s)navbar-expand-(\S*)/g.test(navbar.className)) { switch (RegExp.$2) { case 'sm': breakpoint = 576; break; case 'md': breakpoint = 768; break; case 'lg': breakpoint = 992; break; case 'xl': breakpoint = 1200; break; case 'xxl': breakpoint = 1400; break; default: breakpoint = 0; break; } } if (window.innerWidth < breakpoint) { // ブレークポイント未満の幅のとき const navbarTogglers = navbar.querySelectorAll(`.navbar-toggler[data-bs-target="#${navBarListID}"]`); navbarTogglers.forEach((navbarToggler) => { if(!navbarToggler.classList.contains('collapsed')) { // 現在の表示がハンバーガーメニューの場合、 // 移動したらハンバーガーを折りたたむ navbarToggler.dispatchEvent(new Event('click')); } else if( e.currentTarget.classList.contains('dropdown-item') && e.currentTarget.closest('.dropdown').classList.contains('show') ) { // 現在の表示がハンバーガーメニューではなく、ドロップダウン内のメニューをクリックした場合 // 移動したらドロップダウンを折りたたむ e.currentTarget.closest('.dropdown').dispatchEvent(new Event('click')); } }); } } </code></pre> <p>やっていることは大体同じです。ただし、いくつか置き換えが必要な部分があったので以下その点について触れていきます。</p> <h2 id="置き換えた部分"><a href="#%E7%BD%AE%E3%81%8D%E6%8F%9B%E3%81%88%E3%81%9F%E9%83%A8%E5%88%86">置き換えた部分</a></h2> <h3 id="複数のクラス指定で要素を取得"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E6%8C%87%E5%AE%9A%E3%81%A7%E8%A6%81%E7%B4%A0%E3%82%92%E5%8F%96%E5%BE%97">複数のクラス指定で要素を取得</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://1-notes.com/javascript-multiple-with-getelementsbyclassname/">JavaScript | getElementsByClassNameで複数のclass名を指定して取得する方法 | ONE NOTES</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li> </ul> <pre><code class="js">const elms = document.querySelectorAll('.hoge, .fuga'); </code></pre> <p>jQuery のようにカンマ区切りで <code>document.querySelectorAll()</code> でOK。</p> <h3 id=".on()"><a href="#.on%28%29">.on()</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-on-off/">脱jQuery .on() .off() | q-Az</a></li> </ul> <p>普通に <code>.addEventListener('eventName', function)</code> でOK。</p> <h3 id="複数の要素に対するイベントハンドラ"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E8%A6%81%E7%B4%A0%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9">複数の要素に対するイベントハンドラ</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201805/24_0917.html">どうして!?document.querySelectorAll(selector).addEventListener()が動かないわけ | フロントエンドBlog | ミツエーリンクス</a></li> </ul> <pre><code class="js">const elms = document.querySelectorAll('.hoge, .fuga'); elms.forEach(elm => { elm.addEventListener('click', process); }, false); </code></pre> <p><code>.querySelectorAll()</code> で取得した要素を <code>.forEach()</code> で反復処理させます。</p> <h3 id="親要素・祖先要素"><a href="#%E8%A6%AA%E8%A6%81%E7%B4%A0%E3%83%BB%E7%A5%96%E5%85%88%E8%A6%81%E7%B4%A0">親要素・祖先要素</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://yuyauver98.me/js-parentnode-closest/">【Javascript】親要素や祖先要素を取得する方法 parentNode\/closest | ゆうやの雑記ブログ</a></li> </ul> <p>普通に <code>.closet('.parent')</code> 。</p> <h3 id="子要素"><a href="#%E5%AD%90%E8%A6%81%E7%B4%A0">子要素</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://webty.jp/staffblog/production/post-1745/">脱jQuery!DOM要素取得コードの素のJavaScriptへの書き換え │ Webty Staff Blog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://the-zombis.sakura.ne.jp/wp/blog/2019/09/16/post-4014/">【JavaScript】脱JQuery!?メソッドを比べてみた!要素取得編 - Web.fla</a></li> </ul> <p>jQuery の <code>.children('selectorName')</code> は一応プレーンな JavaScript にも <code>.children</code>プロパティ がある模様。</p> <p>ただし、 jQuery のように <code>.children</code> へセレクタ指定はできなさそうなので、この部分は HTML のクラスを子要素の方に付けることで回避しました。</p> <h3 id="クラス名全てを取得"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E5%90%8D%E5%85%A8%E3%81%A6%E3%82%92%E5%8F%96%E5%BE%97">クラス名全てを取得</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://shanabrian.com/web/javascript/get-class.php">クラス名(class属性の値)をすべて取得 | JavaScript逆引き | Webサイト制作支援 | ShanaBrian Website</a></li> </ul> <p>jQuery では <code>.prop('class')</code> としていたところですが、 <code>.className</code> でOK。</p> <h3 id="子孫要素の中から探す"><a href="#%E5%AD%90%E5%AD%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E4%B8%AD%E3%81%8B%E3%82%89%E6%8E%A2%E3%81%99">子孫要素の中から探す</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li> </ul> <p>jQueryでは <code>.find()</code> だったところを、 <code>elm.querySelector('selectorName')</code> と指定すればOK。</p> <h3 id="ウインドウ幅"><a href="#%E3%82%A6%E3%82%A4%E3%83%B3%E3%83%89%E3%82%A6%E5%B9%85">ウインドウ幅</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wemo.tech/470">スクリーン・ウインドウ・画面サイズをjavascriptで取得する方法まとめ | WEMO</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-innerheight-width-outerheight-width/">脱jQuery .innerHeight() .innerWidth() .outerHeight() .outerWidth() | q-Az</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.javascript.info/size-and-scroll-window">ウィンドウサイズとスクローリング</a></li> </ul> <p>jQuery で <code>$(window).outerWidth()</code> としてたところを、今回の用途では <code>window.innerWidth</code> へ置き換えました。</p> <h3 id="クラスの存在チェック"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%AD%98%E5%9C%A8%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF">クラスの存在チェック</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-addclass-has-remove-toggle/">脱jQuery .addClass() .hasClass() .removeClass() .toggleClass() | q-Az</a></li> </ul> <p>jQuery では <code>.hasClass('className')</code> だったところを、 <code>.classList.contains('className')</code> へ置き換え。</p> <h3 id="イベントハンドラの指定とイベント発火要素の取得"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9%E3%81%AE%E6%8C%87%E5%AE%9A%E3%81%A8%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E5%8F%96%E5%BE%97">イベントハンドラの指定とイベント発火要素の取得</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://note.com/yamanoborer/n/n2e4cc40328b7">【JavaScript】addEventListenerで関数に引数をわたす|北の南|note</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201912/02_0000.html">脱jQueryに向けた第一歩 | フロントエンドBlog | ミツエーリンクス</a></li> </ul> <p>普通に関数指定で <code>elm.addEventListener('click', hoge(elm), false)</code> と書いてしまっていました。</p> <p>しかし、参考記事に拠るとこれだと<strong>その関数の実行結果</strong>が渡されるとのこと。しかもイベント発火時ではなく該当コード読み込み時に実行されてしまうため、挙動がおかしくなってしまいます。</p> <p>これについては第二引数はオブジェクト (または JavaScript の純粋な関数) なので <code>elm.addEventListener('click', hoge, false)</code> と関数名だけにしなければならず、その通りに書けばOK。</p> <p>一方、 <code>const hoge = (e) => { /* 処理 */ };</code> で普通にイベントは渡ってくるので、「クリックされた要素」をイベントハンドラ内で利用したい場合は <code>e.currentTarget</code> とすれば問題ないですね。</p> <p>最初これに気付かずしばらく嵌まっていました。参考記事に感謝。</p> <h3 id="イベント発火"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB">イベント発火</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mae.chab.in/archives/2821#post2821-5">jQueryを使わない書き方 ajax, each, trigger, on\/off, extend, deferred, animate, css編 | maesblog</a></li> </ul> <p>イベントがクラスで渡す必要がありますがほぼ同じで、 <code>elm.dispatchEvent(new Event('click'))</code> とすればOK。</p> <hr /> <p>このような形でガシガシ書き換えていけば大きな問題はなさそうです。</p> <p>とはいえ、この置き換えは地味にアンカーリンクへのスクロールアニメーションを <code>scroll-behavior: smooth;</code> に移行したおかげで上述以外の大半の JS を破棄しても問題ないと判断できたのが非常に大きいですね。そうでなければもっと大きなボリュームと対峙する必要がありました……。</p> <p>しかも <code>Scroll-margin-top</code> で上部固定ナビゲーションバーの裏側にアンカーリンクが隠れてしまう問題を回避できる、というのも JS コードを削減できた要因の一つなので、この2つのプロパティは個人的にかなり神がかっていると感じます。細かいイージングは犠牲になりますが、今回は全然目を瞑ることができるレベルなので良しとします。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="複数のクラス指定で要素を取得"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E6%8C%87%E5%AE%9A%E3%81%A7%E8%A6%81%E7%B4%A0%E3%82%92%E5%8F%96%E5%BE%97">複数のクラス指定で要素を取得</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://1-notes.com/javascript-multiple-with-getelementsbyclassname/">JavaScript | getElementsByClassNameで複数のclass名を指定して取得する方法 | ONE NOTES</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li> </ul> <h3 id=".on()"><a href="#.on%28%29">.on()</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-on-off/">脱jQuery .on() .off() | q-Az</a></li> </ul> <h3 id="複数の要素に対するイベントハンドラ"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E8%A6%81%E7%B4%A0%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9">複数の要素に対するイベントハンドラ</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201805/24_0917.html">どうして!?document.querySelectorAll(selector).addEventListener()が動かないわけ | フロントエンドBlog | ミツエーリンクス</a></li> </ul> <h3 id="親要素・祖先要素"><a href="#%E8%A6%AA%E8%A6%81%E7%B4%A0%E3%83%BB%E7%A5%96%E5%85%88%E8%A6%81%E7%B4%A0">親要素・祖先要素</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://yuyauver98.me/js-parentnode-closest/">【Javascript】親要素や祖先要素を取得する方法 parentNode\/closest | ゆうやの雑記ブログ</a></li> </ul> <h3 id="子要素"><a href="#%E5%AD%90%E8%A6%81%E7%B4%A0">子要素</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://webty.jp/staffblog/production/post-1745/">脱jQuery!DOM要素取得コードの素のJavaScriptへの書き換え │ Webty Staff Blog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://the-zombis.sakura.ne.jp/wp/blog/2019/09/16/post-4014/">【JavaScript】脱JQuery!?メソッドを比べてみた!要素取得編 - Web.fla</a></li> </ul> <h3 id="クラス名全てを取得"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E5%90%8D%E5%85%A8%E3%81%A6%E3%82%92%E5%8F%96%E5%BE%97">クラス名全てを取得</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://shanabrian.com/web/javascript/get-class.php">クラス名(class属性の値)をすべて取得 | JavaScript逆引き | Webサイト制作支援 | ShanaBrian Website</a></li> </ul> <h3 id="子孫要素の中から探す"><a href="#%E5%AD%90%E5%AD%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E4%B8%AD%E3%81%8B%E3%82%89%E6%8E%A2%E3%81%99">子孫要素の中から探す</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-find/">脱jQuery .find() | q-Az</a></li> </ul> <h3 id="ウインドウ幅"><a href="#%E3%82%A6%E3%82%A4%E3%83%B3%E3%83%89%E3%82%A6%E5%B9%85">ウインドウ幅</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://wemo.tech/470">スクリーン・ウインドウ・画面サイズをjavascriptで取得する方法まとめ | WEMO</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/without-jquery-innerheight-width-outerheight-width/">脱jQuery .innerHeight() .innerWidth() .outerHeight() .outerWidth() | q-Az</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://ja.javascript.info/size-and-scroll-window">ウィンドウサイズとスクローリング</a></li> </ul> <h3 id="クラスの存在チェック"><a href="#%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E5%AD%98%E5%9C%A8%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF">クラスの存在チェック</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://q-az.net/none-jquery-addclass-has-remove-toggle/">脱jQuery .addClass() .hasClass() .removeClass() .toggleClass() | q-Az</a></li> </ul> <h3 id="イベントハンドラの指定とイベント発火要素の取得"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%8F%E3%83%B3%E3%83%89%E3%83%A9%E3%81%AE%E6%8C%87%E5%AE%9A%E3%81%A8%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB%E8%A6%81%E7%B4%A0%E3%81%AE%E5%8F%96%E5%BE%97">イベントハンドラの指定とイベント発火要素の取得</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://note.com/yamanoborer/n/n2e4cc40328b7">【JavaScript】addEventListenerで関数に引数をわたす|北の南|note</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.mitsue.co.jp/knowledge/blog/frontend/201912/02_0000.html">脱jQueryに向けた第一歩 | フロントエンドBlog | ミツエーリンクス</a></li> </ul> <h3 id="イベント発火"><a href="#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%99%BA%E7%81%AB">イベント発火</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mae.chab.in/archives/2821#post2821-5">jQueryを使わない書き方 ajax, each, trigger, on\/off, extend, deferred, animate, css編 | maesblog</a></li> </ul> <h3 id="その他"><a href="#%E3%81%9D%E3%81%AE%E4%BB%96">その他</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/nightyknite/items/668c112c40931515ed67">JQueryをVanilla JSに緩やかに置き換える - Qiita</a></li> </ul> <h3 id="Bootstrap 5 のブレークポイント"><a href="#Bootstrap+5+%E3%81%AE%E3%83%96%E3%83%AC%E3%83%BC%E3%82%AF%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88">Bootstrap 5 のブレークポイント</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://bootstrap-guide.com/components/navbar">ナビゲーションバー~Bootstrap5設置ガイド</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://bootstrap-guide.com/layout/breakpoints">ブレークポイント~Bootstrap5設置ガイド</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18180 2022-04-29T23:58:08+09:00 2022-04-29T23:58:08+09:00 https://crieit.net/posts/mojibake-pdf-20220501 PDF からテキストをコピペしたときに稀に見られる潰れた文字のメモ <p>あるとき、エディタ上で「入」の文字が化けてしまって困ったので原因を探ったのでメモしておきます。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>エディタ上でファイル名をコピペしたら「入」の文字が化けてしまいました。エクスプローラ上で見ると一見普通の「入」の文字のように見えたのですが……。</p> <p><a href="https://crieit.now.sh/upload_images/872413ab26ee95ba2c1f3249702a580f626bfc86aeb9e.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/872413ab26ee95ba2c1f3249702a580f626bfc86aeb9e.jpg?mw=700" alt="左が文字化けした入、右が手入力した入" /></a></p> <p>メモ帳に貼り付けたところ、明らかに様子がおかしいことに気付きました。</p> <p>左が文字化けした「入」、右が手入力した「入」です。</p> <p>何故か今回文字化けした「入」は半分くらいに潰れていますね……。字形が異なるということは、もちろん、文字コードも別です。ユニコードで見ると左が <code>\u2F0A</code> 、右が <code>\u5165</code> です。</p> <p>何でしょうこれ……。</p> <h2 id="調査"><a href="#%E8%AA%BF%E6%9F%BB">調査</a></h2> <p>調べてみると、他にもこうした文字があることが分かりました。</p> <pre><code>⼊入 ⼈人 ⻄西 ⾃自 ⾞車 ⼒力 ⼿手 ⾮非 ⼤大 ⼯工 </code></pre> <p>今回採集した例が上述。</p> <p><a href="https://crieit.now.sh/upload_images/a676d153f7a87ba4255ed1b6400ec489626bfc9218be3.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a676d153f7a87ba4255ed1b6400ec489626bfc9218be3.jpg?mw=700" alt="半分潰れたような文字の採集結果" /></a></p> <p>メモ帳で見るといずれも半分くらいの大きさに潰れています。エクスプローラで見るとほとんど違いが分からないのですが、先述の通り文字コードが別なのでファイル名はしっかり別物と認識されます。</p> <p>そのため、同じファイル名が複数あるように見えるというなかなか<del>面白い</del>困った状況に陥ります。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://logmi.jp/tech/articles/324366">PDFをコピペするとなぜ“文字化け”が起きてしまうのか 変換テーブル“ToUnicode CMap”が原因だった - ログミーTech</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://logmi.jp/tech/articles/324412">PDFに文字化けを起こさせない対策法 もらったファイルは正規化で、作成ツールは対応済みを使え - ログミーTech</a></li> </ul> <p>さらに調べた結果、原因が判明しました。原因は PDF でした。</p> <ul> <li>PDFの中のテキストは文字コードとは別の固有の番号で文字を出力しているが、Unicodeのような文字コードとは互換性はない</li> <li>一応文字コードとPDF固有の番号の変換テーブルはある</li> <li>が、この変換テーブルがない PDF が存在する</li> <li>そうした場合、昇順で文字を参照するが本来の文字ではなく部首に当たる文字が先にヒットしてしまう</li> <li>この部首に当たる文字が先の半分潰れたような文字の正体</li> <li>結果、コピペ等 PDF の中からテキストを抜く際に本来の文字ではなく、部首に当たる文字が抜き出される。その文字は通常の文字ではないため、一部の環境では化ける</li> </ul> <p>ざっくり言うとこんな感じの原因の模様。確かに、今回のケースでも PDF の中から見出しのテキストをコピペしてファイル名にしていました。その際に先の条件に当てはまり、通常の <code>入</code> ではなく、 <code>⼊</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://logmi.jp/tech/articles/324366">PDFをコピペするとなぜ“文字化け”が起きてしまうのか 変換テーブル“ToUnicode CMap”が原因だった - ログミーTech</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://logmi.jp/tech/articles/324412">PDFに文字化けを起こさせない対策法 もらったファイルは正規化で、作成ツールは対応済みを使え - ログミーTech</a></li> </ul> <h3 id="(余談)"><a href="#%28%E4%BD%99%E8%AB%87%29">(余談)</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/libraplanet/items/68a3e6b4a3942c83db50">「文字コードを自動判別する」と言う幻想を打ち砕く👊 - Qiita</a></li> </ul> <p>今回の件とは別件ですが、まあ文字コードがらみは厄介ですよね、というお話。</p> arm-band tag:crieit.net,2005:PublicArticle/18178 2022-04-28T00:04:15+09:00 2022-04-28T00:04:15+09:00 https://crieit.net/posts/bootstrap5-scroll-margin-top-darkmode-parallax-demo-20220430 Bootstrap 5 + scroll-margin-top + ダークモード + パララックス についてメモ <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://labor.ewigleere.net/">Bootstrap 5 で JavaScript の記述なしにアンカーリンクへのアニメーション付きスクロールが <code>scroll-behavior</code> で実装されていることを最小限構成で確認した</a>ので、改めて Bootstrap 5 で検証したいと思います。</p> <h2 id="デモ"><a href="#%E3%83%87%E3%83%A2">デモ</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://arm-band.github.io/test-scroll-mt-vault-celestia/">Home - Vault Celestia</a></li> </ul> <p>早速ですがデモを。</p> <h2 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h2> <p>上述の通り、 Bootostrap 5 ではアンカーリンクへのアニメーション付きスクロールは <code>scroll-behavior: smooth;</code> で実装されているのでこの部分はそちらに任せて、 <code>scroll-margin-top</code> のみ自前で記述。</p> <pre><code class="scss">// foundation/_variable.scss $navbar-height: 56px; /* css変数 + Scss変数 */ :root { --navbar-height: #{$navbar-height}; } // object/utility/_u-ssnapmt.scss .u-ssnapmt { scroll-margin-top: var(--navbar-height); } </code></pre> <p>今回は css変数 + Scss の併用で記述してみました。</p> <h2 id="css変数 + ページごとのキービジュアル画像の変更"><a href="#css%E5%A4%89%E6%95%B0+%2B+%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%94%E3%81%A8%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%93%E3%82%B8%E3%83%A5%E3%82%A2%E3%83%AB%E7%94%BB%E5%83%8F%E3%81%AE%E5%A4%89%E6%9B%B4">css変数 + ページごとのキービジュアル画像の変更</a></h2> <p>キービジュアル (ページ最上部にある画像 + 見出し + キャッチコピー等のパーツ) の背景画像について、ページごとに異なる画像を表示させたい、というのはよくある話だとは思います。</p> <pre><code class="html"><head> <style> .c-eyecatch { /* css変数で キービジュアル の画像をページごとに切替 */ background-image: var(--img); } </style> </head> <body> <!-- css変数で キービジュアル の画像をページごとに切替 --> <div class="p-3 p-sm-5 c-eyecatch" style="--img:url('https://source.unsplash.com/ROVBDer29PQ/1920x1080')"> <div class="container"> <h1 class="display-4">ほげ</h1> <p class="lead">ふがふが</p> </div> </div> <main class="l-main"> <section class="container py-5"> <h1 class="display-4">ぴよ</h1> <p>ほげら</p> </section> <!-- 流用 --> <div class="p-3 p-sm-5 mb-4 c-eyecatch" style="--img:url('https://source.unsplash.com/lNxMcE8mvIM/1920x1080')"></div> </body> </code></pre> <p>今回は css変数 を使って</p> <ul> <li>予め <code>head</code>タグ 内の <code>style</code>タグ では <code>background-image: var(--img);</code> でcss変数で指定しておく</li> <li>各要素で変数の値を都度差し替える</li> </ul> <p>という形で画像を切り替える形式を採ってみました。</p> <p>なお、今回はデモなので Jumbotron風 の実装で。</p> <h2 id="ダークモード"><a href="#%E3%83%80%E3%83%BC%E3%82%AF%E3%83%A2%E3%83%BC%E3%83%89">ダークモード</a></h2> <pre><code class="scss">// layout/_l-main.scss @charset "utf-8"; @use "../foundation" as f; .l-main { background-color: f.$bg-color; color: f.$color; #dark > & { /* ダークモード対応 */ @media (prefers-color-scheme: dark) { background-color: f.$color; color: f.$bg-color; } } } </code></pre> <p>メディアクエリ <code>@media (prefers-color-scheme: dark)</code> によるダークモードを試しに記述。</p> <p>ブログのような色に対して中立性の高いコンテンツの場合は使えそうですが、ブランドカラーが決まっている場合は難しそうな感。</p> <p>個人的には基本ダークモードの方がありがたいのですが……。</p> <h2 id="パララックス"><a href="#%E3%83%91%E3%83%A9%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B9">パララックス</a></h2> <p>最後はパララックス。これについてはパッケージを利用しました。</p> <pre><code class="json"> "dependencies": { "ukiyojs": "^3.0.0" }, </code></pre> <pre><code class="js">import Ukiyo from 'ukiyojs'; window.addEventListener( 'load', () => { const ukiyoParallaxes = document.querySelectorAll( '.ukiyo' ); ukiyoParallaxes.forEach( ( ukiyoParallax ) => { new Ukiyo( ukiyoParallax, { scale: 1.75, speed: 1.75, } ); }); }); </code></pre> <p>公式のドキュメント通りです。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/yitengjun/ukiyo-js">GitHub - yitengjun\/ukiyo-js: ⛰️Modern parallax library for picture elements and any images</a></li> </ul> <p>検索すると自前実装も出てくるのですが、後々を考えるとパッケージの方が良さそうな気がするので。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.aizulab.com/blog/sticky-header-overlap/">アンカーリンクの遷移先が隠れる…。追従ヘッダーの重なりを回避するCSS | 会津ラボ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://tech.arms-soft.co.jp/entry/2020/01/29/090000">アンカーリンクのズレをscroll-snapを使って直せるか試してみた - arms inc. Engineers' Blog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wb-hp.com/blog/2021/10/04/google-ie-support-ended.html">scroll-margin-top でヘッダー固定されたページのアンカーリンクの座標を調整する | ホワイトボードオフィシャルブログ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/catnose99/articles/75d3c69bf71bad">【追記: Safariでも動くようになった!】scroll-margin-topがsafariでうまく効かない問題と現状のワークアラウンド</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/CSS/scroll-margin-top">scroll-margin-top - CSS: カスケーディングスタイルシート | MDN</a></li> </ul> <h3 id="Bootstrap 5 での jumbotron"><a href="#Bootstrap+5+%E3%81%A7%E3%81%AE+jumbotron">Bootstrap 5 での jumbotron</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://bootstrap-guide.com/sample/jumbotron-bgimage">背景が画像のジャンボトロンの実例~Bootstrap5設置ガイド</a></li> </ul> <h3 id="css変数 で背景画像指定"><a href="#css%E5%A4%89%E6%95%B0+%E3%81%A7%E8%83%8C%E6%99%AF%E7%94%BB%E5%83%8F%E6%8C%87%E5%AE%9A">css変数 で背景画像指定</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sygnas/items/5a08d2462b6fc46850d7">CSS変数でbackground-imageを指定するとChromeとSafariで挙動が異なるのを解決する - Qiita</a></li> </ul> <h3 id="ダークモード"><a href="#%E3%83%80%E3%83%BC%E3%82%AF%E3%83%A2%E3%83%BC%E3%83%89">ダークモード</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.webcreatorbox.com/tech/dark-mode">Webサイトをダークモードに対応させよう | Webクリエイターボックス</a></li> </ul> <h3 id="パララックス"><a href="#%E3%83%91%E3%83%A9%E3%83%A9%E3%83%83%E3%82%AF%E3%82%B9">パララックス</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/nye/articles/b3cbcf347a0291">モダンアニメーションのパララックス(視差効果)背景のライブラリを作った</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/yitengjun/ukiyo-js">GitHub - yitengjun\/ukiyo-js: ⛰️Modern parallax library for picture elements and any images</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.npmjs.com/package/ukiyojs">ukiyojs - npm</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/18177 2022-04-27T23:58:57+09:00 2022-04-27T23:58:57+09:00 https://crieit.net/posts/css-scroll-behavior-and-scroll-margin-top-note-20220428 (css) scroll-behavior と scroll-margin-top についてメモ <p>css の <code>scroll-behavior</code> と <code>scroll-margin-top</code> について検証したのでメモ。</p> <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p>Bootstrap 5 で何気なく検証をしていたら、特に何をしたわけでもないのに同一ページ内のアンカーリンクにアニメーション付きでスクロールしたので気になりました。</p> <p>自前の JavaScript では何も記述していないプレーンな状態だったので、 Bootstrap 5 の JavaScript かと思ったのですが、該当の CDN からの読み込みの <code>script</code>タグ を削除しても動作したので「すわ css 側か?」となり調べてみることにしました。</p> <h2 id="調査結果"><a href="#%E8%AA%BF%E6%9F%BB%E7%B5%90%E6%9E%9C">調査結果</a></h2> <p>結果、 <code>scroll-behavior</code>プロパティ に依るものと分かりました。具体的には、</p> <pre><code class="css">html { scroll-behavior: smooth; } </code></pre> <p>この記述でページ全体にスクロールアニメーションが付与されていました。</p> <p>しかも調べてみると、別ページのアンカーリンクに遷移する場合は</p> <ol> <li>ページ遷移</li> <li>遷移先のページでアンカーリンクまで自動的にアニメーション付きでスクロール</li> </ol> <p>という挙動をすることが分かりました。</p> <p>css で別ページのアンカーリンクまで対応しているのであれば、上述の挙動が気になる場合を除けばもはやこれ1つで済んでしまうのではないでしょうか……。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-behavior">"scroll-behavior" | Can I use... Support tables for HTML5, CSS3, etc</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yoshitake_1201/items/05a13fd77c18ff380eb6">iOSのバージョンと Safariバージョンの対応表(2022\/4\/6 現在 - Qiita</a></li> </ul> <p>注意点としては、 iOS Safari が15.4(2022/3/14リリース、2022/4/27記事執筆時点で最新版) のみサポートとなっているため、最新版以外の Safari のサポートが必要な場合は polyfill 等の代替手段を講じる必要性がありそうです。</p> <p>PJAX 等のデフォルトではない遷移をした際の挙動については未検証なので何ともですが少し気になります。</p> <p>それと、もう一点気になったのは Bootstrap のケースで言うとナビゲーションバーに <code>.fixed-top</code> を付けていた場合。つまりナビゲーションバーが画面上部に固定表示で追従するパターンですね。</p> <p>頻出するパターンですが、このケースでは工夫しないとアンカーリンク移動時にナビゲーションバーの裏側にコンテンツが隠れてしまうという課題がありました。</p> <p>かつてはこれを <code>margin-top: -80px; padding-top: 80px;</code> 等とネガティブマージンとパディングで調整・相殺するテクニックを使っていましたが……。</p> <h2 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h2> <p>ここで兼ねてより目を付けていたのが <code>scroll-margin-top</code> 。 <code>scroll-snap</code> と呼ばれるスクロール時に特定の位置で止めたり誘導するプロパティの1つで、上述のアンカーリンク遷移時にナビゲーションバーの裏側に隠れてしまう問題を解決する方法として利用できます。</p> <pre><code class="css">.anchor-link { scroll-margin-top: 80px; } </code></pre> <p>これまで上述のようなネガティブマージョンとパディングの相殺や、あるいは <code>before</code>疑似要素 を利用した方法で調整していた手間が、上述の通り1行指定するだけで解決できます。</p> <p>css変数を使用するならば、</p> <pre><code class="css">:root { /* 変数定義 */ --navbar-height: 80px; } html { /* スムーススクロール */ scroll-behavior: smooth; } .header { /* ナビゲーションバーを上部固定表示 */ position: fixed; top: 0; left: 0; width: 100%; z-index: 10; /* css変数を使用して高さを指定 */ height: var(--navbar-height); } .main { /* スクロール可能なコンテンツ領域の上端部分はめり込むのでここは普通に margin-top でナビゲーションバー分下げる。css変数使用。 */ margin-top: var(--navbar-height); } .anchor-link { /* アンカーリンクが裏側に隠れるのを抑止。css変数使用。 */ scroll-margin-top: var(--navbar-height); } </code></pre> <p>このようなイメージですね。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-margin-top">"scroll-margin-top" | Can I use... Support tables for HTML5, CSS3, etc</a></li> </ul> <p>ちなみにこちらは iOS Safari 14.7 以降で有効ですが、やはりそこそこ新しめの Safari である必要があるので、こちらもケースによっては注意が必要そうです。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="scroll-behavior"><a href="#scroll-behavior">scroll-behavior</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://coliss.com/articles/build-websites/operation/work/scroll-page-smoothly-by-css-and-javascript.html">CSSだけでも実装できる!ページ内アンカーやページ上部にアニメーションでスクロールさせるCSS, JavaScriptのまとめ | コリス</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://web.havincoffee.com/design/2020/09/2009281.html#inpage-ank21">CSSでページ内リンクをなめらかにスクロールする|web design-havin' a coffee break|珈琲とウェブデザイン</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-behavior">"scroll-behavior" | Can I use... Support tables for HTML5, CSS3, etc</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yoshitake_1201/items/05a13fd77c18ff380eb6">iOSのバージョンと Safariバージョンの対応表(2022\/4\/6 現在 - Qiita</a></li> </ul> <h3 id="ネガティブマージョンとパディングの調整"><a href="#%E3%83%8D%E3%82%AC%E3%83%86%E3%82%A3%E3%83%96%E3%83%9E%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%A8%E3%83%91%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%AE%E8%AA%BF%E6%95%B4">ネガティブマージョンとパディングの調整</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://designsupply-web.com/media/programming/1999/">ネガティブマージン・パディングで固定ヘッダー使用時のアンカーリンクに対応する</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.softel.co.jp/blogs/tech/archives/6083">【CSS】ページ内リンクのジャンプ先の位置を調整する at softelメモ</a> <ul> <li>疑似要素を使う手法もあり</li> </ul></li> </ul> <h3 id="scroll-margin-top"><a href="#scroll-margin-top">scroll-margin-top</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.aizulab.com/blog/sticky-header-overlap/">アンカーリンクの遷移先が隠れる…。追従ヘッダーの重なりを回避するCSS | 会津ラボ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://tech.arms-soft.co.jp/entry/2020/01/29/090000">アンカーリンクのズレをscroll-snapを使って直せるか試してみた - arms inc. Engineers' Blog</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://wb-hp.com/blog/2021/10/04/google-ie-support-ended.html">scroll-margin-top でヘッダー固定されたページのアンカーリンクの座標を調整する | ホワイトボードオフィシャルブログ</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://zenn.dev/catnose99/articles/75d3c69bf71bad">【追記: Safariでも動くようになった!】scroll-margin-topがsafariでうまく効かない問題と現状のワークアラウンド</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/CSS/scroll-margin-top">scroll-margin-top - CSS: カスケーディングスタイルシート | MDN</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://caniuse.com/?search=scroll-margin-top">"scroll-margin-top" | Can I use... Support tables for HTML5, CSS3, etc</a></li> </ul> arm-band