- ベストアンサー
RSSをデータベースに格納できない
- 昨日教えていただいたコードで格納して表示するところまで成功しました。自分の不注意でデータベースネームと違う、新規作成したテーブルに格納していたため起こったエラーでした。申し訳ありません。
- 再度全体のコードと組み合わせてみたのですがエラーが起こり困っております。Uncaught Error: Call to a member function insert() on null
- <?php $url1 = [ 'http://blog.livedoor.jp/dqnplus/index.rdf', 'http://alfalfalfa.com/index.rdf', 'http://himasoku.com/index.rdf', ]; foreach ($url1 as $url) { $count = 0; $rss = simplexml_load_file($url); foreach ($rss->item as $item) { if ($count >= 8) { break; } ++$count; $title = (string) $item->title; //「$item->title」だけではうまくいかないのでstringにキャスト $link = (string) $item->link; //以下同じ $thumb = (string) $item->thumb->url; $content = (string) $item->description; $wpdb->insert('rssfeed', ['title' => $title, 'link' => $link, 'thumb' => $thumb, 'content' => $content], ['%s', '%s', '%s', '%s']); } } $results = $wpdb->get_results('SELECT * FROM rssfeed'); foreach ($results as $item) { echo $item->title.'<br>'; } ?> <?php $wpdb->show_errors(); ?>
- みんなの回答 (70)
- 専門家の回答
質問者が選んだベストアンサー
お礼 2022/02/09 18:47 ・cronを同じサイトで複数実装してもエラーにはならないでしょうか? WordPressのCronを試してみました。 CronスケジュールひとつにCronイベントを複数登録できました。 エラーにはならいようです。
その他の回答 (69)
- dell_OK
- ベストアンサー率13% (766/5721)
補足 2022/02/08 17:10 補足 2022/02/08 22:22 returnの部分は機能していますが、returnが実行されるとそれ以降の処理は無視されてしまいますので、 $stmt = ~は実行されません。 回答No.51で私が書いたコードに誤りがあったため、回答No.57のコードと混ざってしまっていますが、このget_rss_site_url関数はURLを返すためのものなので、ここには$stmtのことは不要でした。 それでは$stmtのことをどこに書くのか、の前に、データベースにアクセスしている手順から説明しておきたいと思います。 簡単には3つの手順で以下のようになっています。 ①$dbhにデータベース接続オブジェクトを生成する。 ②$stmtに$dbh->prepare()でSQLの準備をする。 ③$stmt->execute()でSQLを実行する。 現在のindex.phpが以下のようになっているとすると、$stmt->execute()は③の処理なので、それ以前に①と②が必要です。 $url1 = get_rss_site_url(); foreach ($url1 as $url) { ※省略 $stmt->execute([$title, $link, $date, $img, $title, $link, $date, $img]); } } 次の2つを上記のコードより上に入れてみてください。 $dbh = connect_db();//① $stmt = $dbh->prepare('insert into rss_feed (title, link, date, img) values (?, ?, ?, ?) on duplicate key update title=?, link=?, date=?, img=?');//②
補足
表示されました。ありがとうございます。 index.phpのurl1のURLは残したままで良いのでしょうか? url1 [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ]; functions.phpのstmtをindex.phpとfunctions.phpで2度読み込んでいるように見えるのですが、使い回しをする場合重複していることにはならないのでしょうか? function get_rss_site_url() { return [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ]; $stmt = $dbh->prepare('insert into rss_feed (title, link, date, img) values (?, ?, ?, ?) on duplicate key update title=?, link=?, date=?, img=?'); }
- dell_OK
- ベストアンサー率13% (766/5721)
補足 2022/02/08 03:00 すみません。 私が間違って、function insert_rss($dbh)の中に書いてしまっていました。 対応方法は先の回答の通りなので、直してみてください。
お礼
たくさんの質問をぶつけてしまい申し訳ないのですが、 データベースセキュリティ対策について知識がないためお聞きしたいのですが、下記の問題を克服するために現コードのものから大きく変える必要があるのでしょうか? (1) 不正な値が入力されないか →表示設定で「$current_page = $_REQUEST['page'] ?? 1; //現在ページ」とありますが、このリクエストに数値以外が入力されても問題ないか →それ以外に外部から入力された値($_REQUESTや$_POST、$_GETなどで取得できる値)は適切に不正な値が混入されないようにできているか (2) 他所に迷惑をかけていないか →他所からRSSを取得するタイミングは適切か。秒単位で大量に何度もアクセスしたりしないか (3) DBに接続できないとき正しくエラーはハンドリングできているか →PDOに接続できない場合に、処理を中断できるように出来ているか。画面上にエラーメッセージだだもれになったりしないか
補足
以下の2つが原因ではないかと考えているのですが、どうでしょうか? $stmt->execute([$title, $link, $date, $img, $title, $link, $date, $img]); $stmt = $dbh->prepare('insert into rss_feed (title, link, date, img) values (?, ?, ?, ?) on duplicate key update title=?, link=?, date=?, img=?'); prepare()関数の呼び出しの不備でエラーになっているのでしょう(存在の前提でなく接続済みが前提)。 ※知恵袋より参照 valuesの関数の数とexecuteの関数の数が一致していない? $url1 = [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ]; url1でindex.phpにRSSを読み込んでいるからエラーが起きる? function get_rss_site_url() { return [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ]; returnの部分をurl1に変えると同様のエラーがfunctions.phpでも起こりますので、 index.phpのurl1が間違っている? $url1 = [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ];
- dell_OK
- ベストアンサー率13% (766/5721)
どちらのforeachがエラーになっているかわかりますか。 foreach ($url1 as $url) { foreach ($rss->item as $item) { たぶん、上の方だと思うのですが、そうだとしたら$url1の定義がないためかと思われます。 直前に、もともとあった $url1=[~]; を入れてみてください。 $url1の定義の仕方もいろいろあるので検討してみていただければと思います。 ひとつはfunctions.phpかどこか他でもいいのですが、サイトのURLを返す関数を用意する方法です。 functions.phpで再定義の回避が必要でしたらこんな感じになるとおもいます。 ---- if (!function_exists('get_rss_site_url')) { function get_rss_site_url() { return [ '', '', '', ]; } } ---- で、ループの直前でこの関数を呼びます。 ---- $url1 = get_rss_site_url(); foreach ($url1 as $url) { ---- 他には、データベースかテキストファイルにURLを設定しておいてそれを読み込む方法です。 このようなものは、これもデータなので、コード内に列挙しない方がいいですし、追加、削除、変更などメンテナンスの点でもいいと思います。 方法は基本的に上と同じですが、直接配列を返さないので、 ---- return [ '', '', '', ]; ---- これのかわりに、読み込み処理などのコードになります。 質問者さまご自身でできるようでしたらやってみてください。 どこからなにをしたらいいかわからなければ、改めて聞いてください。
補足
以下のように変更してみたところ、該当する箇所でエラーが起きました。 関数url1がなくなったためforeach文が機能していないように見えるのですが、構文の間違いがわかりません。 returnの部分は機能しているのでしょうか? ※エラー文 Uncaught Error: Call to a member function execute() on null ※該当箇所 $stmt->execute([$title, $link, $date, $img, $title, $link, $date, $img]); ※index.php $url1 = get_rss_site_url(); foreach ($url1 as $url) { if (($rss = @simplexml_load_file($url)) === false) { continue; } foreach ($rss->item as $item) { $dc = $item->children('dc', true); $date = date('Y-m-d H:i:s', strtotime($dc->date)); //※削除対象日付より古いRSSは保存しない if ($date < $delete_date) { continue; } $title = $item->title; $link = $item->link; $content = $item->children('content', true); $result = preg_match('/<img[^>]*src=\"([^"]+)\"[^>]*>/i', $content->encoded, $matches); if (1 == $result) { $img = $matches[1]; } else { $img = ''; } $stmt->execute([$title, $link, $date, $img, $title, $link, $date, $img]); } } ※functions.php if (!function_exists('get_rss_site_url')) { function get_rss_site_url() { return [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ]; $stmt = $dbh->prepare('insert into rss_feed (title, link, date, img) values (?, ?, ?, ?) on duplicate key update title=?, link=?, date=?, img=?'); } }
- dell_OK
- ベストアンサー率13% (766/5721)
もしそれで、エラー「Cannot redeclare」が発生するようでしたら、以下のようにすることで再定義されないようになるみたいです。 if (!function_exists('connect_db')) {//※追加 function connect_db() { } }//※追加
お礼
foreachの中身はこちらです。 foreach ($url1 as $url) { if (($rss = @simplexml_load_file($url)) === false) { continue; } foreach ($rss->item as $item) { $dc = $item->children('dc', true); $date = date('Y-m-d H:i:s', strtotime($dc->date)); //※削除対象日付より古いRSSは保存しない if ($date < $delete_date) { continue; } $title = $item->title; $link = $item->link; $content = $item->children('content', true); $result = preg_match('/<img[^>]*src=\"([^"]+)\"[^>]*>/i', $content->encoded, $matches); if (1 == $result) { $img = $matches[1]; } else { $img = ''; } $stmt->execute([$title, $link, $date, $img, $title, $link, $date, $img]); } }
補足
コードを試してみたところエラーが解消できました。 ありがとうございます。 表示はされているのですが、php.iniでエラーをオンにしてみるとWarning: Invalid argument supplied for foreach() というエラーが追加されていて、foreach文に渡された引数が間違っているという文言が表示されてのですが… 考えられる原因はありますでしょうか?
- dell_OK
- ベストアンサー率13% (766/5721)
すみません。 名前を間違えていました。 誤:function db_connect() 正:function connect_db()
補足
原因を調べてみたのですがわかりませんでした。 foreach文に渡された引数が間違っている=$url1が正常にわたっていない?=前文299行目のinsert_rss($dbh);//接続オブジェクトを渡す 上記の記述が間違っている? コードのミスは確認しましたが間違いはありませんでした。
- dell_OK
- ベストアンサー率13% (766/5721)
エラー「Cannot redeclare」が発生したと言う事は、functions.phpがすでに読み込まれていたことになります。 なのでテーマ内のindex.phpやpage.phpの実行前に、WordPressが自動で読み込んでいるのでindex.phpやpage.phpで「require_once 'functions.php';」を書かなくても大丈夫です。 エラー「Uncaught Error: Call to undefined function connect_db()」が発生したのは、もしかして、functions.phpの関数定義をしていないのではないでしょうか。 「※コメントは追記したところです。」と書いたのでそこしか見なかったのかも知れませんが、関数定義「function db_connect()」の記述が必要です。 function db_connect() { try { $dsn = 'mysql:dbname=hlxclitx_wp1;host=localhost'; $user = 'hlxclitx_wp1'; $password = 'E.HrypHWxNmltXgC5eS26'; $dbh = new PDO($dsn, $user, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //エラーが発生した時に、例外を投げる echo 'データベースへの接続が出来ました'; return $dbh; //※接続したオブジェクトを返す } catch (PDOException $e) { echo $e->getMessage(); // err時はメッセージを表示 exit; } }
補足
下記のようにfunctions.phpに記述していますがエラーが出ております… function db_connect() { try { $dsn = 'mysql:dbname=hlxclitx_wp1;host=localhost'; $user = 'hlxclitx_wp1'; $password = 'E.HrypHWxNmltXgC5eS26'; $dbh = new PDO($dsn, $user, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //エラーが発生した時に、例外を投げる echo 'データベースへの接続が出来ました'; return $dbh; //※接続したオブジェクトを返す } catch (PDOException $e) { echo $e->getMessage(); // err時はメッセージを表示 exit; } } function delete_old_rss($dbh)//※接続オブジェクトを受け取る { $sql = 'DELETE FROM rss_feed WHERE date < ?'; $stmt = $dbh->prepare($sql); $delete_date = date('Y-m-d H:i:s', strtotime('-1 week')); //※削除対象日付 $stmt->execute([$delete_date]); } function insert_rss($dbh)//※接続オブジェクトを受け取る { $url1 = [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ]; $stmt = $dbh->prepare('insert into rss_feed (title, link, date, img) values (?, ?, ?, ?) on duplicate key update title=?, link=?, date=?, img=?'); }
- dell_OK
- ベストアンサー率13% (766/5721)
ファイル名は「functions.php」になっていますよね。 先日「functions.php」を調べていて、どこかのサイトに堂々と説明されていたものが、sのない「function.php」となっていたので、ちょっと気になりました。
補足
なってますね。 1つ小耳に入れておいて欲しい情報があるのですが、functions.phpでエンジニアの方のブログを拝見したところ以下のように書いてありました functions.phpを読み込まないとfunctions.phpに書いた関数を使うことはできないので「functions.phpにdb接続を書いて、読み込ませず直に書く」という使い方ができるものではありません。 ですので一つのファイルで完結させたい場合は、list.phpにデータベース接続の関数を書くか、関数を作らずにデータベース接続のコードを直接書く方法になるかと思います。
- dell_OK
- ベストアンサー率13% (766/5721)
大丈夫ではないですね。 functions.phpが自動で読み込まれないなにかがあるようです。 試しに、index.phpにonceのない「require 'functions.php';」を追加して読み込んでみてください。
お礼
index.phpと間違えておりました。functions.phpの23行目でエラーが起きています。 //従来のウィジェットエディターに戻す function example_theme_support() { remove_theme_support('widgets-block-editor');//エラーが起きている個所 } add_action('after_setup_theme', 'example_theme_support');
補足
コードを追記させていただいたところ、再宣言しているとの文言が帰ってきました。 上部に記載済みのランキング記事でエラーが起きています。 <?php $args = array( 'post_type' => 'post', 'numberposts' => 12, //表示数 'meta_key' => 'pv_count', 'orderby' => 'meta_value_num', 'order' => 'DESC', ); $posts = get_posts($args); if ($posts) : ?> <ul class="ranking-box"> <?php foreach ($posts as $post) : setup_postdata($post); ?> <li> <a href="<?php the_permalink(); ?>" width: 97px;height: 130px;> <div class="dairy-ranking"><?php the_post_thumbnail('thumbnail'); ?> </div> <h5><?php the_title(); ?></h5> </a> </li> <?php endforeach; wp_reset_postdata(); ?> </ul> <?php else : ?> <p>アクセスランキングはまだ集計されていません。</p> <?php endif; ?> </section> </div> </div> ※エラー内容はこちらになります。 wp-content/themes/sample_theme/functions.php ファイルの23行目のエラーのため、PHP コードの変更をロールバックしました。修正し、もう一度保存してください。 Cannot redeclare example_theme_support()
- dell_OK
- ベストアンサー率13% (766/5721)
補足 2022/02/05 23:24 functions.phpに書くことはお話ししましたが、書いたものを使う説明をしていなかったですね。 まず、functions.phpに書くものはすべてfunctionで定義してください。 functionの名前のとおり、これは機能と言う意味だと思ってくださるといいです。 つまり機能ごとに定義して、それぞれを必要なところで呼ぶようにします。 functions.phpはこんな感じです。 ※コメントは追記したところです。 ---- <?php function db_connect() { try { $dsn = 'mysql:dbname=hlxclitx_wp1;host=localhost'; $user = 'hlxclitx_wp1'; $password = 'E.HrypHWxNmltXgC5eS26'; $dbh = new PDO($dsn, $user, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //エラーが発生した時に、例外を投げる echo 'データベースへの接続が出来ました'; return $dbh; //※接続したオブジェクトを返す } catch (PDOException $e) { echo $e->getMessage(); // err時はメッセージを表示 exit; } } function delete_old_rss($dbh)//※接続オブジェクトを受け取る { $sql = 'DELETE FROM rss_feed WHERE date < ?'; $stmt = $dbh->prepare($sql); $delete_date = date('Y-m-d H:i:s', strtotime('-1 week')); //※削除対象日付 $stmt->execute([$delete_date]); } function insert_rss($dbh)//※接続オブジェクトを受け取る { $url1 = [ 'http://nns2ch.net/index.rdf', 'http://aqua2ch.net/index.rdf', 'https://worldfn.net/index.rdf', ]; $stmt = $dbh->prepare('insert into rss_feed (title, link, date, img) values (?, ?, ?, ?) on duplicate key update title=?, link=?, date=?, img=?'); } ---- それで、使う側は、こんな感じになります。 $dbh = connect_db();//接続オブジェクトを保持する delete_old_rss($dbh);//接続オブジェクトを渡す insert_rss($dbh);//接続オブジェクトを渡す すると、functions.phpを読み込んでも、その時点ではデータベース接続やそれ以降の処理が勝手に実行されることはありません。 $dbhをglobalで使うこともできるのですが、私はこのように戻り値や引数を使った方がいいと思います。
補足
教えていただいたコードをfunctions.phpに追加して、index.php(固定ページ)のコードを修正したのですが、エラーが起きています。Uncaught Error: Call to undefined function connect_db() functions.phpが読み込まれていないようですが、大丈夫でしょうか? <?php $dbh = connect_db();//接続オブジェクトを保持する // カテゴリーIDをキーにしたカテゴリー配列を生成 $categories = []; foreach (get_categories() as $category) { $categories[$category->cat_ID] = $category; } // 投稿の情報を追加 function set_other_data($post) { // アイキャッチIDを取得 $post_thumbnail_id = get_post_thumbnail_id($post); // アイキャッチ画像の確認 if ($post_thumbnail_id) { // 存在する $image_src = wp_get_attachment_image_src($post_thumbnail_id); // サムネイルの画像URLを設定 $post->thumbnail = $image_src[0]; } else { // 存在しない $post->thumbnail = 'noimage.jpg'; } // カテゴリーIDを取得 $post->categories = wp_get_post_categories($post->ID); // コメントテキスト if (0 == $post->comment_count) { // コメントなし $post->comments = __('No Comments'); } else { // コメントあり $post->comments = $post->comment_count.'件のコメント'; } // コメントリンク $post->comments_link = get_comments_link($post->ID); } ・ ・ ・ 以下略
- dell_OK
- ベストアンサー率13% (766/5721)
まずはおわびを。 「船頭多くして船山に登る」 私がでしゃばったばかりに惑わせてすみません。 最初はPHPでのプログラミングに関する質問として回答したのですけれども、WordPressのこととなるとちっともわからないのにいろいろと言ってしまいました。 ファイル構成については妥当でありまた正当でもあるのでいいと思いました。 それで単純に三択ですが、この中だと案2がいいと思います。 以前は案1がいいと思っていたのですが、functions.phpをもっと活用した方がいいなと思い直しました。 もしこれだとしても、データベース接続専用にするのではなくて、データベース関連は全部ひとつにまとめてdb.phpがあるのでしたらそれでいいと思います。 案3はファイルを分割しすぎな気がします。 ひとつのファイルが長めなのと、短いファイルがたくさんあるのとでは、後者の方が負荷がかかりそうです。 とは言っても、これくらいの量ではまったく違いがわからないほどです。 WordPressがテーマのindex.phpに到達するまでにおそろしいほどのファイルと処理が実行されているからです。 と言うのと同じで、管理画面にも関係するfunctions.phpですが、その中に定義するだけなので気にすることはないと思います。 実行されない関数の定義があっても、よほどでない限り他に影響を及ぼさないと思います。 「functionsでどこまで括るのが正解なのか」にくださっている回答と私はほぼ同じ考えなので、この三択は微妙と言えば微妙です。 その回答にもありましたが、functions.phpは、自作テーマ内にあって、WordPressでそのテーマを有効にしていれば、自動で読み込まれるはずなので、自分で読み込ませる必要はありません。 先に読み込まれてはいるけど、require_onceなのでエラーになっていだけで、requireにするとエラーになるのではないでしょうか。 他の回答でもありましたように、これで納得して完成しても、後で見たら、別のやり方ができたとか、よかったとか、思い直すことは多くあります。 今現在最善をつくしたとしてもです。 どれかひとつに決められましたら、それ以上は考えずに、まずはそれを完成させてみてください。 それから数日か一週間ほどしたら、別に自作テーマを用意して、他の案でも完成させてみてください。 また数日ほどしたら、さらにもうひとつの案で完成させてみてください。 次に他のものを作ることがあるのでしたら、それで別案を試すのもいいですが、その時には、今回のと同じでいいかなと楽するためにも、先にいまのうちに思いついている3案を試しておくのがいいと思います。 人の意見は参考程度に、自分が気に入ったもの作りやすいもの直しやすいものと言うのを見つけたり構築していくのが私は一番だと思います。 プログラムには絶対これしかないと言う解はないと思うので、まずは気軽に完成させること、いくつもいくつも完成させること、でおのずと道が開けてくると思います。
補足
最初はPHPでのプログラミングに関する質問として回答したのですけれども、WordPressのこととなるとちっともわからないのにいろいろと言ってしまいました。 A.どうやら元のwordpressの関数を扱っていないことで、wordpressとは似て非なるようですので、いろいろ考えることがありました。 データベース関連は全部ひとつにまとめてdb.phpがあるのでしたらそれでいいと思います。 A.自作ファイルなのでfunctions.phpのように固定ページより先に読み込まれる機能ではないです。 require_onceで読み込ませる方法は他のエンジニアの方のブログに記載があったので疑問でした。その方は接続の部分とそれ以降を分割していたので例外かもしれませんが。 人の意見は参考程度に、自分が気に入ったもの作りやすいもの直しやすいものと言うのを見つけたり構築していくのが私は一番だと思います。 A.ありがとうございます。複数のパターンがある場合、どうしても決断に欠けてしまいます。デフォルトのやり方から優先して組むよう心がけます。
お礼
期間をけてからの質問で申し訳ないのですが、コードを整理中に修正が必要な個所があり原因がわかりません echo "<li class=\"sitedate\">{$item->date}</li>"; で画像やカテゴリーなどは表示できるのでしょうか? 個別に指定して表示する必要があるのでしょうか? 1.投稿に画像やカテゴリーなどが表示されず、タイトルと日付けのみ表示されている 2.RSS画像にURLがついておらず画像のみ表示されている ※該当箇所 if (!empty($item->img)) { echo "<li><img src=\"{$item->img}\" width=\"100\"></li>"; } 3.RSSの画像がない場合ダミー画像を表示させたいが書き方が調べてもわからない
補足
ありがとうございます。 引き続きコードを組んでみます…