質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

解決済

2回答

1037閲覧

MySQLで VARCHAR(100) のとき、それをphpとJavaScriptで検証する方法

nikuatsu

総合スコア177

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

1グッド

1クリップ

投稿2022/09/13 18:21

編集2022/09/13 19:01

前提

・掲示板サイトを作っています。
CREATE TETABLE で 掲示板のタイトルを VARCHAR(100) に指定しました。
・掲示板のタイトルはユーザーがPOSTし、絵文字などすべて入力できます。

実現したいこと

掲示板のタイトルが VARCHAR(100)以下の文字数であることを、phpとJavaScriptで判定したいです。

発生している問題

文字数のカウント方法が、言語間にも、文字毎にも整合性がなく、VARCHAR(100) 以下と判定する正確な方法がわかりません。

該当のソースコード

こちらのthread_ttlが対象カラムです。

SQL

1CREATE TABLE tbl_threads ( 2 `ID` INT, 3 `thread_ttl` VARCHAR(100) -- 対象カラム 4);

phpではmb_strlenのみで問題なく数えることができるようで、以下すべて「1」です。

php

1$arr = ["1","a","あ","😃","🏴"]; 2$result = array_map( function( $str ) { 3 return mb_strlen( $str ); 4}, $arr ); 5var_export( $result );

しかしJavaScriptですが、以下最後の旗が「5」になってしまいます。

JavaScript

1const arr = ["1","a","あ","😃","🏴󠁧󠁢󠁥󠁮"]; 2const result = arr.map( function( str ) { 3 return [...str].length; 4}, arr ); 5console.log( result );

このように、言語間にも、文字毎にも整合性がないのが問題です。

試したこと

ということは、phpでは次のように検証して、

php

1// ユーザーが入力した掲示板のタイトル 2$thread_ttl = $_POST['thread_ttl']; 3 4// 掲示板のタイトルの文字数を検証 5if ( mb_strlen( $thread_ttl ) > 100 ) { 6 echo "VARCHAR(100)を超えてます"; 7 die(); 8} else { 9 // tbl_threads へ INSERT 10}

そしてJavaScriptでは次のように検証する。ということになるのでしょうか?

JavaScript

1// ユーザーが入力した掲示板のタイトル 2const threadTtl = "掲示板の🏴󠁧󠁢󠁥󠁮タイトル🏴󠁧󠁢󠁥󠁮"; 3 4// 普通に数えることができない例外文字を { 文字: 文字数 } と指定 5const excObj = { 6 "🏴󠁧󠁢󠁥󠁮": 5 7} 8 9// 掲示板のタイトルに含まれる、例外文字の数と、例外文字の文字数を取得 10let allExcCount = 0; // 例外文字の数 11let allExcLen = 0; // 例外文字の文字数 12Object.keys( excObj ).forEach( excStr => { 13 const excCount = ( threadTtl.match( new RegExp( excStr, "g" ) ) || [] ).length; 14 const exclen = excObj[excStr] * excCount; 15 allExcCount = allExcCount + excCount; 16 allExcLen = allExcLen + exclen; 17}); 18 19// 掲示板のタイトルの文字数を検証 20const threadTtlLen = [...threadTtl].length - allExcLen + allExcCount; 21if ( threadTtlLen > 100 ) { 22 alert( "VARCHAR(100)を超えてます"); 23} else { 24 // POST 25}

やや違和感を覚えますし、上記の旗みたいな例外文字を excObj として網羅しないといけませんよね…
もっと良い方法はございませんでしょうか?

そして万が一これがベストならば、excObjを網羅できるような一覧データというのはどこかにございますでしょうか?

バージョン

PHP 8.0
MySQL 5.7.31
jQuery 3.6.0

kemusi👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/09/13 22:27

ユーザー入力の検証(文字数制限) をまずクライアント側で (JavaScript で) 行い、さらにサーバー側で (PHP で) 行って、検証 NG ならユーザーにエラーメッセージを表示して再入力を促すという話なのですか?
退会済みユーザー

退会済みユーザー

2022/09/13 22:40

> phpではmb_strlenのみで問題なく数えることができるようで サロゲートペアとか特定の絵文字などはダメだそうですが? 【PHP】絵文字等にも使える見た目の文字数を数え上げるPHP組み込み関数 grapheme_strlen の紹介 https://www.google.co.jp/amp/s/cpoint-lab.co.jp/article/202205/22719/amp/
m.ts10806

2022/09/13 22:51 編集

>echo "VARCHAR(100)を超えてます"; 通常このようなメッセージは出しません。 実際に使うのは一般のユーザー。開発者でないのにこのように出されても困るでしょう? そもそも要件がおかしいと思います。 「100字超過をチェックする」ことはあっても、それはDBの型にあわせてチェックするわけではなく、 あくまで「入力内容がシステム側が許可するものか確認する」です。 「バイト」と「字数」を正確に理解・定義したうえで型も用意し、入力内容のチェックをする必要があります。
退会済みユーザー

退会済みユーザー

2022/09/13 23:06 編集

JavaScript の方は「javascript サロゲートペア 文字数」などをキーワードにググって見ると以下のような記事がヒットします。 JavaScriptで正確に文字数をカウントする https://blog.solunita.net/posts/count-string-accurately-in-javascript/ 自分は未検証・未確認なのでホントにそれでできるかは分かりませんが、試してみては? 特定の文字とかはやっぱりダメそうな気がしますが。
matukeso

2022/09/13 23:03

そもそも旗の絵文字にたいしてmb_strlenで1を返したといっても、MySQLのVARCHAR(100)に旗を100個入れれるかといえば、入らないのでは。VARCHARって基本バイト数でしょ。
退会済みユーザー

退会済みユーザー

2022/09/13 23:20

utf8mb4 を使用しているとすると、varchar(100) は 100 x 4 = 400 バイトになるのでは? utf8 なら 100 x 3 = 300 バイト。
guest

回答2

0

ベストアンサー

このように、言語間にも、文字毎にも整合性がないのが問題です。

ということは、チェックする機構をどこか一つに任せるべきです。

PHP 上では mb_strlen などを使わず、SQL を使って検証するのが良いでしょう。

php

1<?php 2function getCharLength($str) { 3 $db_host = 'localhost'; 4 $db_user = 'mysqluser'; 5 $db_pass = 'mysqlpass'; 6 $db_name = null; 7 8 $mysqli = new MySQLi($db_host, $db_user, $db_pass, $db_name); 9 $mysqli->set_charset('utf8mb4'); 10 $sql = sprintf('SELECT CHAR_LENGTH("%s")', $mysqli->real_escape_string($str)); 11 $result = (int)$mysqli->query($sql)->fetch_column(); 12 $mysqli->close(); 13 14 return $result; 15}

JavaScript 上では、JavaScript コードを書くのではなく、PHP スクリプトを API として呼ぶべきです。

投稿2022/09/13 21:50

arcxor

総合スコア2859

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nikuatsu

2022/09/14 15:51

SELECT CHAR_LENGTH() というのは初耳でした。どうもありがとうございます。
退会済みユーザー

退会済みユーザー

2022/09/14 22:13

横レス失礼します。 > JavaScript 上では、JavaScript コードを書くのではなく、PHP スクリプトを API として呼ぶべきです。 質問者さんの目的がはっきりしませんが、たぶん、ユーザー入力の検証(文字数制限) が目的で、まずクライアント側で (JavaScript で) 行い、さらにサーバー側で (PHP で) 行うという話と想像しています。 その場合、クライアント側での検証は、サーバー側には負担をかけないで、クライアント側だけで行うことを基本に考えるべきと思います。 クライアント側での検証に「PHP スクリプトを API として呼ぶ」と、呼ぶたびにネット、Web サーバー、DB サーバーに負担をかけることになります。また、呼ぶのに非同期要求を使ったりすると組み込みの検証システムとの不整合で苦労するとか、いろいろ注意が必要と思います。 目的がユーザー入力の検証であれば、「PHP スクリプトを API として呼ぶ」ことまでする必要があるかはよく考えた方が良さそうと思います。
nikuatsu

2022/09/14 22:30

SurferOnWwwさん、ご忠告ありがとうございます。 仰る通り「まずクライアント側で (JavaScript で) 行い、さらにサーバー側で (PHP で) 行う」を考えておりました。 なので JavaScript の方はAPIで呼ばずSurferOnWwwさんのご回答にあるリンクである精度まで検証し、php の方で SELECT CHAR_LENGTH() を使い確実に検証するつもりです。(たぶんこれが最善っぽいです。)
退会済みユーザー

退会済みユーザー

2022/09/14 23:08

レスをありがとうございました。 ひとつ気になったことがあります。 > php の方で SELECT CHAR_LENGTH() を使い確実に検証するつもりです。(たぶんこれが最善っぽいです。) 私の回答で書いた grapheme_strlen で要件が満たせるなら、DB サーバーに負担をかけないで、PHP だけで grapheme_strlen を使って検証してはいかがですか? grapheme_strlen は合字はダメだそうですが、CHAR_LENGTH なら OK で (そこは不明です)、要件として合字まで検証する必要があるということなら話は別ですが。 もうひとつ、主題と直接関係ない話ですが、「varchar(100)」=「100 文字」ということではないです。utf8mb4 を使用しているとすると、varchar(100) は 100 x 4 = 400 バイトということになり、ascii 文字なら 400 文字格納できるはずです。
nikuatsu

2022/09/15 17:21

> DB サーバーに負担をかけないで、PHP だけで grapheme_strlen を使って検証してはいかがですか? これが本件においてベストかもしれません。そもそもユーザーはバイトで数えないので入力フォームには「100文字まで」と表記しますが、この表記は SELECT CHAR_LENGTH による計測とズレが生じます。 その点 grapheme_strlen はズレがなくユーザーの数え方により沿った実装になると思いました。 尚 grapheme_strlen の場合もフロントでの実行に際しては ajax で呼ばざるを得ずサーバーの負担が懸念されますが、どうやら次のライブラリを使うと grapheme_strlen と同じ結果を JavaScript で得ることができそうです。 https://github.com/orling/grapheme-splitter ちなみに本件において合字は無視することにしました。 SELECT CHAR_LENGTH を使い、入力フォームには「100文字まで」でなく「100バイトまで」とでも表示すれば、入力フォームの表示から実際に入るデータまで完全に整合できますが、ユーザーにとって分かりにくいうえに使用頻度も低く、合字に則す価値は低いと考えたためです。 以上、ざっくり次のように整理できそうです。 ● データベースに入る値と同じ値であることを優先 → 常に SELECT CHAR_LENGTH のみで計測 → フォームには「バイト」で表記 → 合字に対応し、バイトにおいて完全に整合する ● ユーザーの数え方と同じ値であることを優先 → php では grapheme_strlen で計測、JavaScriptでは grapheme-splitter で計測 → フォームには「文字数」で表記 → 合字を除けば、文字数において完全に整合する 貴重なご意見誠にありがとうございました。
arcxor

2022/09/16 03:39

CHAR_LENGTH が何を返すかについて何か勘違いをされているようです。よく検証してください。
guest

0

ユーザー入力の検証(文字数制限) をまずクライアント側で (JavaScript で) 行い、さらにサーバー側で (PHP で) 行って、検証 NG ならユーザーにエラーメッセージを表示して再入力を促すという話なのですか?

であれば、

phpではmb_strlenのみで問題なく数えることができるようで

サロゲートペアとか特定の絵文字などはダメだそうですが?

【PHP】絵文字等にも使える見た目の文字数を数え上げるPHP組み込み関数 grapheme_strlen の紹介
https://www.google.co.jp/amp/s/cpoint-lab.co.jp/article/202205/22719/amp/

ただし、記事にも書いてありますが、grapheme_strlen でも特定の文字 (複数の文字を一文字として表示する合字) はダメだそうですが。

JavaScript の方は「javascript サロゲートペア 文字数」などをキーワードにググってみると以下のような記事がヒットします。

JavaScriptで正確に文字数をカウントする
https://blog.solunita.net/posts/count-string-accurately-in-javascript/

自分は未検証・未確認なのでホントにそれでできるかは分かりませんが、試してみては?

特定の文字とかはやっぱりダメそうな気がしますが。

投稿2022/09/13 22:57

編集2022/09/13 23:05
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nikuatsu

2022/09/14 15:52

たくさんのリンクをありがとうございます。参考にさせて頂きます。
退会済みユーザー

退会済みユーザー

2022/09/14 22:20

質問のコメントで、 ユーザー入力の検証(文字数制限) をまずクライアント側で (JavaScript で) 行い、さらにサーバー側で (PHP で) 行って、検証 NG ならユーザーにエラーメッセージを表示して再入力を促すという話なのですか? ・・・と聞きましたが、それに答えてもらえませんか?
nikuatsu

2022/09/14 22:34

一応そのように考えています。(初めての掲示板制作なのでその手順を踏むことが正当なのかは疑問ですが…。)
退会済みユーザー

退会済みユーザー

2022/09/14 22:48

返答ありがとうございました。フレームワーク組み込みの検証機能が使えるかもしれませんね。Laravel とかには組み込みの機能があるそうです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問