-
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
-
今回はさらに小ネタですが、XAMPP でのデバッグ時によく遭遇するエラーなのですが PhpSpreadsheet で出力されたエクセルファイルを MS-EXCEL で開いたままで 同じプログラムを実行した時には以下の様な WARNING と ERROR が表示されます。Warning: fopen(test6-a3.xlsx): failed to open stream: Resource temporarily unavailable in C:\xampp\htdocs\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\BaseWriter.php on line 125 Fatal error: Uncaught PhpOffice\PhpSpreadsheet\Writer\Exception: Could not open file "test6-a3.xlsx" for writing. in C:\xampp\htdocs\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\BaseWriter.php:127 Stack trace: #0 C:\xampp\htdocs\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\Xlsx.php(542): PhpOffice\PhpSpreadsheet\Writer\BaseWriter->openFileHandle() #1 C:\xampp\htdocs\_phpspread\excel6-all-3.php(23): PhpOffice\PhpSpreadsheet\Writer\Xlsx->save() #2 {main} thrown in C:\xampp\htdocs\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Writer\BaseWriter.php on line 127
このエラーが発生したプログラムは以下の記事のものを使用しています。
⇒PHP PhpSpreadsheet エクセルのワークシートのセルに日付型データの設定と、日付スタイル設定について2(setCellValue, getStyle)><?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト $objSheet = $objSpreadsheet->getActiveSheet(); // セルにシステム時刻(Unix Time)を設定 $objSheet->setCellValue('A2', Date::PHPToExcel(time())); $objSheet->setCellValue([1, 3], Date::PHPToExcel(time())); // スタイルオブジェクト取得([A2]セル) $objStyle = $objSheet->getStyle('A2')->getNumberFormat()->setFormatCode('yyyy"年"m"月"d"日";@'); $objStyle = $objSheet->getStyle([1, 3, 1, 3])->getNumberFormat()->setFormatCode('yyyy"年"m"月"d"日";@'); // [test6-a2.xlsx]:Excel2007形式で保存する $objWriter = new Xlsx($objSpreadsheet); $objWriter->save('test6-a2.xlsx'); exit(); ?>
エラーが発生しているのは 23行目の save の部分です。 MS-EXCEL で開いたままの状態なので、ファイルにロックが掛かっていて上書きができない状況です。
MS-EXCEL を閉じてから再実行すれば問題無く書込みはされるのですが、これではプログラムとしてはまずいので エラーが発生したことを画面に出力してやります。
その前に、PhpSpreadsheet\Writer\BaseWriter.php のソースの 125行目の部分を覗いてみます。
125行目では fopen 関数でファイルオープンを行っていて、ファイルハンドルが取得できない場合には Exception を投げています。 この Exception を save 関数に実行で Catch してやればエラーが時の処理が書けることが分かります。$fileHandle = $filename ? fopen($filename, $mode) : false; if ($fileHandle === false) { throw new Exception('Could not open file "' . $filename . '" for writing.'); }
それでは save 関数の実行を Try Catch で囲んでやって Catch の中でエラー時の処理を書きます。
(エラー処理といっても、単にエラーメッセージを表示するだけですが。)
なお save 関数の直前で PHP の error_reporting 関数で WARNING のエラー表示をしない様に抑止しています。
これが無いと WARNING が表示されてうるさいので。<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト $objSheet = $objSpreadsheet->getActiveSheet(); // セルにシステム時刻(Unix Time)を設定 $objSheet->setCellValue('A2', Date::PHPToExcel(time())); $objSheet->setCellValue([1, 3], Date::PHPToExcel(time())); // スタイルオブジェクト取得([A2]セル) $objStyle = $objSheet->getStyle('A2')->getNumberFormat()->setFormatCode('yyyy"年"m"月"d"日";@'); $objStyle = $objSheet->getStyle([1, 3, 1, 3])->getNumberFormat()->setFormatCode('yyyy"年"m"月"d"日";@'); // [test6-a2.xlsx]:Excel2007形式で保存する $objWriter = new Xlsx($objSpreadsheet); try { error_reporting(E_ALL & ~E_WARNING); $objWriter->save('test6-a3.xlsx'); } catch (Exception $e) { echo "ファイル書込みエラーが発生しました。:".$e->getMessage(), "\n"; } exit(); ?>
PR -
今回はさらに小ネタですが、以下の記事では、セルに PHP のUNIT-TIMEデータを日付型データとして設定し、日付の書式で表示させる方法について説明しました。
この時のセルの書込み位置指定で従来の方法で "A2" としていましたが、最近のバージョンの PhpSpreadsheet では「列No, 行No」と配列での指定が可能になった様です。
⇒PHP PhpSpreadsheet エクセルのワークシートのセルに日付型データの設定と、日付スタイル設定について(PHPToExcel, setFormatCode)
そこで、XAMPP のPHPバージョンを「7.4.32」にアップしたのに伴って、PhpSpreadsheet のバージョンアップを行います。
まずはWindowsのコマンドプロンプトから以下のコマンドを実行します。C:\xampp\htdocs>composer config platform.php 7.4.32
「C:\xampp\htdocs\composer.json」ファイルの内容が以下の様になります。 phpspreadsheet のバージョンはそのままです。
{ "require": { "phpoffice/phpspreadsheet": "^1.8" }, "config": { "platform": { "php": "7.4.32" } } }
composer require phpoffice/phpspreadsheet
このコマンドで実行したのですが、いろいろエラーが表示されたので、「C:\xampp\htdocs\vendor」フォルダを別のものにして 「vendor」が存在しない状態で再度、上記のコマンドを実行しました。
(ちょっと強引な感じですが、更新方法が良くわからなかったので。)
「7-ZIP」という圧縮・解凍ツールがインストールされている場合は、取り敢えずアンインストールします。
(phpspreadsheet 取込の composer でファイル解凍時?にエラーになります。)
再度、コマンドを実行したら、新しく「vendor」以下が構築された様です。C:\xampp\htdocs>composer require phpoffice/phpspreadsheet Info from https://repo.packagist.org: #StandWithUkraine Using version ^1.25 for phpoffice/phpspreadsheet ./composer.json has been updated Running composer update phpoffice/phpspreadsheet Loading composer repositories with package information Updating dependencies Nothing to modify in lock file Installing dependencies from lock file (including require-dev) Package operations: 11 installs, 0 updates, 0 removals 0 [>---------------------------] 0 [->--------------------------] - Installing myclabs/php-enum (1.8.4): Extracting archive - Installing psr/simple-cache (1.0.1): Extracting archive - Installing psr/http-message (1.0.1): Extracting archive - Installing psr/http-factory (1.0.1): Extracting archive - Installing psr/http-client (1.0.1): Extracting archive - Installing markbaker/matrix (3.0.0): Extracting archive - Installing markbaker/complex (3.0.1): Extracting archive - Installing symfony/polyfill-mbstring (v1.26.0): Extracting archive - Installing maennchen/zipstream-php (2.2.1): Extracting archive - Installing ezyang/htmlpurifier (v4.16.0): Extracting archive - Installing phpoffice/phpspreadsheet (1.25.2): Extracting archive 0/11 [>---------------------------] 0% 9/11 [======================>-----] 81% 10/11 [=========================>--] 90% 11/11 [============================] 100% Generating autoload files 3 packages you are using are looking for funding. Use the `composer fund` command to find out more! No security vulnerability advisories found
■「setCellValue、getStyle」のセル指定に配列を使用
セルに座標を設定する場合、従来は "A2" の様に列のアルファベットと行Noで指定していましたが、 ここに [1, 2] の様に [列No, 行No] の指定が出来る様になりました。
なお、スタイルオブジェクトを取得する getStyle の場合は [左上列No, 左上行No, 右下列No, 右下行No] の範囲指定で行います。
以下に簡単な例を示します。 [A2] および [A3] セルに現在時刻を設定し、そのセルの書式スタイルを日付表示の設定で行っています。
<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト $objSheet = $objSpreadsheet->getActiveSheet(); // セルにシステム時刻(Unix Time)を設定 $objSheet->setCellValue('A2', Date::PHPToExcel(time())); $objSheet->setCellValue([1, 3], Date::PHPToExcel(time())); // スタイルオブジェクト取得([A2]セル) $objStyle = $objSheet->getStyle('A2')->getNumberFormat()->setFormatCode('yyyy"年"m"月"d"日";@'); $objStyle = $objSheet->getStyle([1, 3, 1, 3])->getNumberFormat()->setFormatCode('yyyy"年"m"月"d"日";@'); // [test6-a2.xlsx]:Excel2007形式で保存する $objWriter = new Xlsx($objSpreadsheet); $objWriter->save('test6-a2.xlsx'); exit(); ?>
出力されたエクセルファイルを見てみると以下の様になります。[A]列の幅は便宜上広くして表示しました。
-
今回は小ネタですが、セルに PHP のUNIT-TIMEデータを日付型データとして設定し、日付の書式で表示させる方法について説明したいと思います。■「PHPToExcel」を使ったセルへの日付型データの設定
セルに日付型データを設定するには Date クラスの PHPToExcel メソッドで PHP の日付データ(UNIX-TIME) をエクセルの date/time 値に変換したものを設定します。 PHPToExcel メソッドは static な関数なので Date::PHPToExcel と直接書くことができます。
以下に簡単な例を示します。 [A2] セルに現在時刻を設定し、そのセルの書式スタイルを日付表示の設定で行っています。
<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト $objSheet = $objSpreadsheet->getActiveSheet(); // セルにシステム時刻(Unix Time)を設定 $objSheet->setCellValue('A2', Date::PHPToExcel(time())); // スタイルオブジェクト取得([A2]セル) $objStyle = $objSheet->getStyle('A2')->getNumberFormat()->setFormatCode('yyyy"年"m"月"d"日";@'); // [test6-a2.xlsx]:Excel2007形式で保存する $objWriter = new Xlsx($objSpreadsheet); $objWriter->save('test6-a2.xlsx'); exit(); ?>
出力されたエクセルファイルを見てみると以下の様になります。[A]列の幅は便宜上広くして表示しました。
-
エクセルのワークシートのセルにスタイルを設定する説明は以下の記事で行いましたが、今回は複数セルに対するスタイル設定について説明したいと思います。
PHP PhpSpreadsheet エクセルのワークシートのセルのスタイル設定について
■「applyFromArray」を使ったセルのスタイル設定
ワークシートオブジェクト から取得した スタイルオブジェクト の applyFromArray() メソッドで設定するスタイルを array() で与えることが出来ます。
以下に簡単な例を示します。 [A1:D3] のセル範囲の周りに赤色のボーダーで囲む設定を行っています。
<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト $objSheet = $objSpreadsheet->getActiveSheet(); // スタイル設定値array $styleArray = [ 'borders' => [ 'outline' => [ 'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THICK, 'color' => ['argb' => 'FFFF0000'], ], ], ]; // [A1:D3]のスタイル設定 $objSheet->getStyle('A1:D3')->applyFromArray($styleArray); // [test6-a0.xlsx]:Excel2007形式で保存する $objWriter = new Xlsx($objSpreadsheet); $objWriter->save('test6-a0.xlsx'); exit(); ?>
出力されたエクセルファイルを見てみると以下の様になります。
■「applyFromArray」と「duplicateStyle」を使ったセルのスタイル設定
上記は スタイルオブジェクト の applyFromArray() メソッドでスタイルを設定する処理でしたが、 指定したセル範囲を一つのセルと考えてのスタイルを設定していました。
今度は、指定したセル範囲の全てのセルに対して同じスタイルを設定する方法を示します。
以下の例は スタイルクラス:Style() を生成しその スタイルオブジェクト に applyFromArray() メソッドでスタイルを設定しておきます。
この スタイルオブジェクト を ワークシートオブジェクト の duplicateStyle() メソッドで指定範囲の各セルへのスタイル設定が一括で行えます。
各セルには「文字フォント」「アライメント(水平位置)」「ボーダー(全周枠)」「書式設定(数値)」「ぬりつぶし(色)」のスタイルを設定します。
<?php // ライブラリ読込 require '../vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Style; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; // Spreadsheetオブジェクト生成 $objSpreadsheet = new Spreadsheet(); // ワークシートオブジェクト $objSheet = $objSpreadsheet->getActiveSheet(); // 配列データ $arrData = array( array(100, -1100, 12000, 130000), // マイナス値もいれる array(200, 2100, 22000, -230000), array(300, 3100, -32000, 330000), ); // [A1:D3]セルに配列データ設定 $objSheet->fromArray( $arrData, // 配列データ NULL, // 配列データの中でセルに設定しないNULL値の指定 'A1' // 左上座標(デフォルト:"A1") ); // 全部のスタイルの定義 $objStyle = new Style(); $objStyle->applyFromArray( array( 'font' => array( 'name' => 'Arial', 'bold' => TRUE, 'italic' => FALSE, 'underline' => Font::UNDERLINE_DOUBLE, 'strikethrough' => FALSE, ), 'alignment' => array( 'horizontal' => Alignment::HORIZONTAL_RIGHT ), 'borders' => array( 'top' => array( 'borderStyle' => Border::BORDER_THIN, ), 'bottom' => array( 'borderStyle' => Border::BORDER_THIN, ), 'left' => array( 'borderStyle' => Border::BORDER_THIN, ), 'right' => array( 'borderStyle' => Border::BORDER_THIN, ) ), 'numberFormat' => array( 'formatCode' => '#,##0;[Red]#,##0', // マイナスは赤色 ), 'fill' => array( 'fillType' => Fill::FILL_SOLID, 'startColor' => array( 'argb' => '00FFFF80' ) ) ) ); // セル範囲指定でのスタイル設定 $objSheet->duplicateStyle($objStyle, 'A1:D3'); // [test6-a1.xlsx]:Excel2007形式で保存する $objWriter = new Xlsx($objSpreadsheet); $objWriter->save('test6-a1.xlsx'); exit(); ?>
出力されたエクセルファイルを見てみると以下の様になります。
-
以下の記事で、クラス(class)の使い方について説明しましたが、その時は親クラスのメソッドを、 子クラスで同じメソッド名で宣言することでオーバーライドができることを記しました。
⇒PHP クラス(class)の使い方について
クラスは関数だけではなく、プロパティ(変数と言ってもいいと思います)もオーバーライドが使えます。
親クラスで public で宣言されたプロパティを、子クラスで同じ名前で public で宣言すればオーバーライドしたことになります。 取敢えず、簡単な例を以下に示します。<?php // 継承の基となる親クラス class cParent { // プロパティ public $param; // コンストラクタ public function __construct() { $this->param = "cParent_param"; } // テスト関数 public function func() { echo $this->param."
\n"; } } // [cParent]から派生した子クラス class cChild extends cParent { // オーバーライド・プロパティ public $param; // コンストラクタ public function __construct() { $this->param = "cChild_param"; } } // [cParent]クラスの生成 $insParent = new cParent(); // テスト関数の実行 $insParent->func(); // [cChild]クラスの生成 $insChild = new cChild(); // テスト関数の実行 $insChild->func(); ?>
親クラスに「param」のプロパティを宣言し、コンストラクタの中でそのプロパティを初期化しています。 また、関数「func()」の中ではそのプロパティを出力しています。
そこで、この親クラスから派生した子クラスに「param」というプロパティをオーバーライド宣言しています。
これらのクラスを実行してするため、親クラスを生成し「func()」を実行し、子クラスを生成し「func()」を実行しています。
これを実行するとブラウザに以下の様に表示されます。cParent_param cChild_param
子クラスを生成し「func()」を実行した場合は、親クラスの「func()」が実行され、 「func()」の中で参照されている「param」は子クラスのプロパティとなります。 オーバーライドの結果が出ていると思います。
このことから、子クラスで宣言されたプロパティを使って、親クラスの関数に違った動作を行わせることが出来ると思います。