更新日: 2024年12月5日


IV. DBを使って人事評価から特許活用まで(2)

5. 特許活用システム構築の背景

知財戦略担当は全社の技術部門傘下に配置されたと書きましたが、駄待ち狐がいた半導体分社では開発センターや事業部開発部門毎に1~2名が任命されました。全体で数十名規模となり、分社社長肝いりの戦略知財タスクフォースを推進します。その最終目標は知財収支の黒字化ですが、取敢えず何をやるかというと特許使用料支出を減らすための他社特許調査です。各部門で数千件の特許を調べる(1件ずつ特許を読んで自社製品の抵触性を判断する)必要があり、知財戦略担当だけでできる仕事ではありません。「特許調査なんて面倒くさい雑用だ」と思っている技術者を脅したりすかしたりして巻込みます。

その活動を否定するものではありませんが、駄待ち狐としては「支出を減らすだけじゃなくて収入を増やさないと黒字にならないでしょ」という思いがありました。ライセンス収入を増やすためには、自社が出願した特許を活用するための仕組みが必要です。以下に示す図は駄待ち狐が2002年に提案したもの(一部修正)です。技術者が発明提案をすると社内で出願要否を判断し、明細書を作成して出願します。特許庁が審査して特許性を認められると登録となり、出願後20年間有効です。1件の特許だけを見ても、発明提案から権利満了までには20年という歳月が流れ、この間の特許管理がライフサイクルマネジメントです。

画像を新しいタブで開くと高解像度で見ることができます。

ライフサイクルマネジメントの対象となる特許は半導体分社で数千件、全社では10万件以上あります。この特許全体を管理するためには当然DBが必要ですが、連綿と続く知財部門は全社の全出願特許についてのDB(図中のOpenPACS)を運用しており、これは堅牢さで定評のあるOracleでした。また、発明者が出願依頼をしてから登録になるまでの間、発明者と知財部門がやり取りをするためのwebシステム(Nexus)もあります。さらに、日米の全ての特許を網羅した調査系のDB(GlobalPATLIOT)も構築されています。

特許活用というのは自社で使用することも含みますが、ライセンス収入を得るためには他社に対して特許権を主張する必要があります。これはライフサイクルで言うと登録から権利満了までの期間の活動ですが、これに対応するwebシステム(特許活用システム)が必要なのではないかというのが上図の主張です。OpenPACSにも他社使用のフラッグはあって、その内容も記入できるのですが、これは発明者の自主申告に依拠しています。知財戦略担当が他社に権利行使できる自社特許を探しやすくするシステムが特許活用システムであり、そういう視点で自社特許を管理することがポートフォリオマネジメントです。

6. システム構築に至る経緯

特許活用システムの構築に向けて、駄待ち狐はまず知財部門でDBを運用している部署に相談に行きました。OpenPACS、Nexus、GlobalPATLIOTは上位組織も異なる別々の部署が担当しており、相互の連携はありません。特許活用システムの話をしても、「必要性は認めるが自部署の業務ではない」という対応です。こうなれば自分で作るしかありません。その当時はOpenPACSとNexusの間ですらDBとしての連携がなかったので、まずはここからと思いました。知財戦略担当として両方のDBに対するアクセス権を持っていたので、取敢えずは在籍していた研究所が保有する2,000件のデータをCSVでダウンロードし、Excelで加工しました。

しかし、この結果はイマイチでした。加工に手間がかかる上、それぞれのデータは日々更新されます。最低でも月に一度のデータ更新が必要ですが、その度に面倒な加工をしていたのでは割が合いません。しかもExcelなので、研究所所員が簡単に欲しい情報を入手できるという訳でもありません。結局のところ、特許活用システムの構想は駄待ち狐の頭の中で塩漬けとなりました。その後、駄待ち狐は知財・技術助成課の課長になり、これが発展的拡大(なんて社内用語はありませんが)した知財・技術推進部の部長になり、他社が自社特許を侵害していると提訴する日米訴訟を戦い・・・8年の歳月が流れました。

しかし、駄待ち狐は初心を忘れていた訳ではありません。以下の年表に示す通り、2009年の研究所運営方針発表会で特許活用システムの構築を表明したのです。「それじゃあ、『本職』としてのプログラミングじゃないの!」と突っ込まれそうですが、『本職』であればシステム構築はSIerに丸投げします。これを自分一人で作り上げるところが『余技』である所以です。WAMPへの移植を経て、特許活用システムのver. 3であるPPMが完成します。余談ですが、「III. 複素関数による垂直共振器モード解析」の「1. 研究開発に従事した20年」で紹介した光並列メモリもPPM(photonic parallel memory)と言います。

因みにOpenPACSとNexusの間ですらDBとしての連携がないということに知財部門としても問題意識は持っていたようで、両者を統合したi∞pro(アイプロ)が2010年にリリースされました。このi∞proにも統合検索機能はありますが、データを見やすく加工するダッシュボード機能はなく、それを提供するのがPPMだという見方もできます。PPMはi∞proから定型のクエリでデータをダウンロードし、PPM側の機能でアップロードしますが、一連の操作は2万件の特許に対して数分で完了します。2,000件の特許に四苦八苦していた2002年から飛躍的な進化です。年表の最後にあるPatentSQUAREというのはGlobalPATLIOTの進化版です。

7. 特許活用システムPPMの概要

PPMは今でもこのように動きます。ただ、2013年当時のままという訳ではなく、「8. PHPプログラム」で触れる2点の改修を施しています。特許情報は全て公開なので、この中に社内機密はありませんが、強いて言えば社内のどの組織(業務処理コード)でその特許を出願したのかが社内機密です。これは社外の人にとってはどうでもいい話で「社内だけの機密」ですが、PPM再現版では組織名を伏字にしてあります。そもそも2013年当時の情報なので、その名前の組織はとっくに無くなっています。また、出願から1年半後に公開されるまでの間の明細書も社内機密ですが、元のシステムでは見られたこの情報も今は見られません。

PPMの使い方はトップ画面からリンクしているマニュアルにある通りです。「生存family数」のfamilyというのは、1件の特許を日本で出願した後、外国に出願したり、分割出願したりするので、親を同じくする特許群のことです。ポートフォリオマネジメントはfamily単位で行うので、family数はその対象数ということになります。何ヶ国で登録されているかで分類したfamily数を大括りの組織やその下位の組織ごとに一覧表示し、それぞれの一覧表からExcelの特許リストを出力し、個別の特許公報を見るという一連の操作がシームレスに実行できます。「森を見て、ズームインして木を見る」ことができるのです。

マニュアル3頁目の「⑤ 本部/BUが保有する特許全件のリストをExcelファイルで出力」は、出力件数が2,000件以上になると1分近くかかります。出力先はお使いのブラウザで設定しているデフォルトのダウンロード場所です。これは冒頭に述べた改修点の2つ目に関係していて、元のシステムではファイルを保存する必要はなく数秒で画面上にExcelが開きました。また、開いたシートの特許番号から公報や公開前の明細書にリンクが張られていましたが、PatentSQUAREあるいはi∞proへのリンクなので、今の再現版では機能しません。

PPMを作り始めた時からの懸案が、特許の技術分類です。組織ごとではなく技術分類ごとに全社の特許を管理するのが、究極のポートフォリオマネジメントです。ただ、ある時点で考えた社内技術分類はその時の社会/社内の技術動向に左右されてしまいます。社内で重点開発されている技術の分類は妙に細かくなってしまい、10年後には意味のない分類となります。逆に10年前にはどうでもよかった技術がクローズアップされることもあります。出願特許に技術分類を付与することは知財部門にとっても永遠の課題でしたが、発明者に技術分類を記入して貰うというのはいつの時代も中途半端になっていました。

PPMの完成間近になって駄待ち狐が思いついたのは、技術分類を出願前に発明者に記入してもらう必要はないということです。特許を活用するのは登録後なので、審査時に特許庁が付与してくれる技術分類を使えばいいのです。特に日本の特許庁が付与するテーマコード(Fターム)は汎用の技術分類としてよくできていて、しかも審査官が明細書を読んだ上で付与してくれます。こんな有難い話はありません。このテーマコードはPatentSQUAREから取得できます。こうして技術分類からもポートフォリオ表示が可能なPPMが完成しました。

8. PHPプログラム

再現システムは以下の5本のプログラムで動いています。Portfolio.class.phpとExport.class.phpはそれぞれPortfolioとExportというクラスを記述し、慣例に従ってファイル名の頭字は大文字、拡張子は.class.phpです。Exportクラスはindex.php、makeTable.php、showClass.phpで使われます。紛らわしいですが、showClass.phpのClassは分類という意味です。最初の4本がシステムの根幹なので本項でコードを紹介します。

  index.php				# トップ画面の表示、元コードはDB更新メニューを含む
   ├ makeTable.php			# サブテーブルの表示
   │   │  ├ Portfolio.class.php	# 抽出特許の登録国数確認
   ├   ├ Export.class.php		# サブテーブルの特許抽出+Excelファイル出力
   └ showClass.php			# 業務処理コード体系とテーマコード体系の表示

元のシステムでは、さらに以下の5本のプログラムでDBの更新機能を実装していました。管理者がアクセスした時のみ、トップ画面に更新メニューを表示する仕組みでした。

  uploadFile.php				# データ更新に使うファイルをサーバにアップロード
  importPrelist.php			# アップロードしたi∞proのデータをMySQLに書込む
  export4PS.php 			# 技術分類が付与されていない特許をMySQLから抽出
  importPSdata.php			# PatentSQUAREで取得した技術分類をMySQLに書込む
  exportMngcode.php			# 書換えの元データとして業務処理コードをダウンロード
  importMngcode.php			# 書換えた業務処理コードをアップロード

8.1 index.php

再現版のindex.phpを以下に示します。7~13行目のfunction post_open()は元のプログラムにはなかったもので、改修点の1つ目です。この改修は「I. 人生の余技として作成したプログラム」の「6. JavaScript」に書いた、ウィンドウサイズを最大で表示するための4行のコードに関係しています。2013年当時のブラウザ、少なくともIEでは<a href="..." target="_blank">で新しいウィンドウが開きました。<form action="..." method="post" target="_blank">でも同様です。この場合、新しいウィンドウで開かれる側の文書に4行のJavaScriptを書いておけば、ウィンドウサイズが最大になります。

しかし、現在のブラウザでは新しいウィンドウではなく、新しいタブが開きます。ウィンドウは元のままなのでサイズは変りません。ウィンドウを最大化するためには、開く側の文書に新しいウィンドウを開くためのJavaScriptを埋込む必要があります。それが8行目のwindow.open()です。ここでウィンドウの幅と高さを最大値に設定してもいいのですが、元のプログラムと同様に開かれる側で最大化するようにしたかったので、初期サイズは適当なサイズにしています。トップ画面のテーブルのセル内にある多数の「GO」ボタンはそれぞれ異なるフォームを送信するので、異なるidを設定してdocument要素として区別しています。

コードの表示にはPrism.jsを使用しています。
prism.cssの設定は団塊爺ちゃんの備忘録を参考にしました。

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Patent Portfolio Manager</title>
<script>
<!--
function post_open(count){
  window.open("", "new_window", "width=600, height=450");
  document.getElementById("form"+count).action = "makeTable.php";
  document.getElementById("form"+count).method = "POST";
  document.getElementById("form"+count).target = "new_window";
  document.getElementById("form"+count).submit();
}
//-->
</script>
</head>

<body bgcolor="#ccecff">
<center>
<h1>
<font color="#00c400">Patent Portfolio Manager (ver.2.1)</font>
  <a href="manual.pdf" target="_blank"><font size="+0">マニュアル表示</font></a>
<br>

<?php
$mysqli = new mysqli("ホスト名",  "ユーザ名",  "パスワード", "データベース名");
$stmt = $mysqli->prepare("SELECT date FROM update_date WHERE id = 1");
$stmt->execute();
$stmt->bind_result($date);
$stmt->fetch();
$stmt->close();
$mysqli->close();
$date_array = explode('-', $date);
$date_array[1] = ltrim($date_array[1], '0');
$date_array[2] = ltrim($date_array[2], '0');
echo "<font size='+1'>(データ更新日: $date_array[0]年$date_array[1]月$date_array[2]日)</font>";
?>

</h1>
</center>
<h2>
<font color="0000ff">1.業務処理コード毎のポートフォリオを表示</font>
</h2>
<center>
<table border="1">
<tr bgcolor="99ccff">
<td align= "center" width="200" height="26">本部/BU</td>
<td align= "center" width="150">ポートフォリオ<br>(件数)</td>
<td align= "center" width="150">ポートフォリオ<br>(番号表示)</td>
<td align= "center" width="150">対応特許リスト</td>
</tr>

<?php
require_once("Export.class.php");
$ep = new Export();
$ep->makeArray(1);
$count = 0;
foreach($ep->num2BU as $key => $value){
    echo "<tr><td bgcolor='99ff99'>$value</td>";
    $count++;
    $form_id = "form".$count;
    echo "<form id='$form_id')><input type='hidden' name='Proj' value='$key'>";
    echo "<input type='hidden' name='This_class_1' value='$value'><td align='center'>";
    echo "<input type='button' value='GO' onClick='post_open($count)'></td></form>";
    $count++;
    $form_id = "form".$count;
    $key2 = $key + 40;
    echo "<form id='$form_id'><input type='hidden' name='Proj' value='$key2'>";
    echo "<input type='hidden' name='This_class_1' value='$value'><td align='center'>";
    echo "<input type='button' value='GO' onClick='post_open($count)'></td></form>";
    echo "<form action='makeTable.php' method='post'><input type='hidden' name='Proj' value='$key2'>";
    echo "<input type='hidden' name='This_class_1' value='$value'>";
    echo "<input type='hidden' name='Sub_fnum_str' value='all'><td align='center'>";
    echo "<input type='submit' value='GO'></td></form></tr>";
}
$count++;
$form_id = "form".$count;
echo "<tr><td bgcolor='ffff66' align='center' colspan='2'>件数合計の表示</td>";
echo "<form id='$form_id'><input type='hidden' name='Proj' value='10'>";
echo "<input type='hidden' NAME='This_class_1' value=NULL><td align='center' colspan='3'>";
echo "<input type='button' value='GO' onClick='post_open($count)'></td></form></tr>";
?>

<tr>
<td bgcolor="ffcccc" align="center" colspan="2">業務処理コード体系の表示</td>
<form action="showClass.php" method="post" target="_blank"><input type="hidden" name="Table" value="1">
<td align="center" colspan="3"><input type="submit" value="GO"></td>
</form>
</tr>
</table>
</center>
<p> <p>

<h2>
<font color='0000ff'>2.技術分類(テーマコード)毎のポートフォリオを表示</font>
</h2>
<center>
<table border='1'>
<tr bgcolor="99ccff">
<td align= "center" width='100' height='26'>テーマコード<BR>上位2桁</td>
<td align= "center" width='150'>ポートフォリオ<br>(件数)</td>
<td align= "center" width='150'>ポートフォリオ<br>(番号表示)</td>
<td align= "center" width='150' colspan='2'>対応特許リスト</TD>
</tr>

<?php
$ep->makeArray(3);
foreach($ep->num2class as $key => $value){
    echo "<tr><td bgcolor='99ff99' align='center'>$value</td>";
    $count++;
    $form_id = "form".$count;
    echo "<form id='$form_id')><input type='hidden' name='Proj' value='$key'>";
    echo "<input type='hidden' name='This_class_1' value='$value'><td align='center'>";
    echo "<input type='button' value='GO' onClick='post_open($count)'></td></form>";
    $count++;
    $form_id = "form".$count;
    $key2 = $key + 40;
    echo "<form id='$form_id'><input type='hidden' name='Proj' value='$key2'>";
    echo "<input type='hidden' name='This_class_1' value='$value'><td align='center'>";
    echo "<input type='button' value='GO' onClick='post_open($count)'></td></form>";
    echo "<form action='makeTable.php' method='post'><input type='hidden' name='Proj' value='$key2'>";
    echo "<input type='hidden' name='This_class_1' value='$value'>";
    echo "<input type='hidden' name='Sub_fnum_str' value='all'><td align='center'>";
    echo "<input type='submit' value='GO'></td></form></tr>";
}
$count++;
$form_id = "form".$count;
echo "<tr><td bgcolor='ffff66' align='center' colspan='2'>件数合計の表示</td>";
echo "<form id='$form_id'><input type='hidden' name='Proj' value='110'>";
echo "<input type='hidden' NAME='This_class_1' value=NULL><td align='center' colspan='3'>";
echo "<input type='button' value='GO' onClick='post_open($count)'></td></form></tr>";
?>

<tr>
<td bgcolor="ffcccc" align="center" colspan="2">テーマコード体系の表示</td>
<form action="showClass.php" method="post" target="_blank"><input type="hidden" name="Table" value="2">
<td align="center" colspan="3"><input type="submit" value="GO"></td>
</form>
</tr>
</table>
</center>
<p> <p>
</body>
</html>

25~37行目のPHPコードはMySQLからデータを読出していますが、ここで読出すのはDBの更新日だけです。「1.業務処理コード毎のポートフォリオを表示」のテーブル1列目に並ぶ「本部/BU」名もMySQLから読出しますが、これは55~56行目でExportクラスのインスタンス$epを生成し、makeArray()メソッドを実行することで取得します。取得結果である$ep->num2BUプロパティを58行目でforeachの反復配列として使い、59行目でテーブルの1列目としてそのまま出力します。62~64行目ではテーブルの2列目、68~70行目では3列目、71~74行目では4列目の「GO」ボタンに対応するフォームを生成します。

テーブルの2列目と3列目の「GO」は上記のfunction post_open()を介してmakeTable.phpにフォームを送信し、makeTable.phpがサブテーブルを生成します。2列目と3列目の違いはhiddenで渡されるProjの値(前者は11~50、後者は51~90)で、これによってサブテーブルのセルに入るのが件数なのか、特許リストの遠し番号列挙なのかが変ります。4列目の「GO」はサブテーブルを表示するのではなくExcelファイルを出力するので、ウィンドウサイズを最大にするためのfunction post_open()は介しません。フォームの送信先はmakeTable.phpですが、hiddenでSub_fnum_strを渡すことでその挙動が変ります。

foreachループはここで終って、79~81行目は件数合計を表示する「GO」ボタンのフォームを生成します。これはサブテーブルを表示するので、function post_open()を介します。PHPコードを抜けた85~87行目は業務処理コード体系を表示する「GO」ボタンのフォームで、この時は直接showClass.phpにデータを送信します。92行目の<p>□<p>(□は全角スペース)は2行分の改行で、当時は<p style="margin-bottom: 40px;">を知りませんでした。94~142行目では、以上の処理と同様の処理を「テーマコード」に対して行い、「2.技術分類(テーマコード)毎のポートフォリオを表示」のテーブルを生成しています。

8.2 makeTable.php

makeTable.phpは非常に複雑な構成になっていますが、まずはウィンドウサイズを最大にするという簡単な話からします。2013年頃に主流だったディスプレイは解像度が1366×768(16:9)、1024×768(4:3)、1280×800(16:10)で、最大にすると横幅の広いサブテーブルがうまく収りました。しかし、今の1920×1080や2560×1600(150%に拡大すると1707×1067)では間延びします。そこで42~43行目にあるJavaScript後半2行で最大サイズの80%にしています。ウィンドウサイズはユーザが自由に変れば済む話ですが、サブテーブルを開く作業は何回も繰返されるので、その度に変更するのは煩わしいことです。


<?php
$proj = $_POST['Proj'];
$this_class_1 = $_POST['This_class_1'];
$sub_fnum_str = $_POST['Sub_fnum_str'];
$class_name_str = $_POST['Class_name_str'];
if($class_name_str != NULL) $class_name = explode("+", $class_name_str);
if($sub_fnum_str == NULL){ ?>

<HTML>
<HEAD>
<TITLE>件数/特許一覧表示</TITLE>
<META http-equiv="Content-Type" content="text/css; charset=UTF-8">
<SCRIPT>
<!-- 
var scrnx = screen.availWidth;
var scrny = screen.availHeight;
//--> 
</SCRIPT>
<STYLE type="text/css"> 
<!--
TABLE{
	width: 1200px;
	border: solid 1px;
	border-collapse: separate;
	border-color: #c0c0c0 #c0c0c0 #808080 #808080;
	border-spacing: 2px;
}
TABLE.table_1{
	table-layout: fixed;
}
TD{
	border: solid 1px;
    border-color: #808080 #808080 #c0c0c0 #c0c0c0;
}
-->
</STYLE>
</HEAD>

<BODY BGCOLOR="#ccecff">
<SCRIPT>
<!--
window.moveTo(scrnx * 0.1, scrny * 0.1);
window.resizeTo(scrnx * 0.8, scrny * 0.8);
//-->
</SCRIPT>
<CENTER>
<H4>
<FONT COLOR='0000d0'>
※※※ 自動でウィンドウサイズを大きくして表示しています。(×ボタンでクローズ) ※※※
</FONT>
</H4>
</CENTER>

<?php }
/*
テーブルclass_listからサブリストに入る中分類/小分類の配列を作成
*/
if($proj == 100){
		$class_name = array('/');
}elseif($sub_fnum_str == NULL || $sub_fnum_str == 'all'){
    $mysqli = new mysqli("ホスト名",  "ユーザ名",  "パスワード", "データベース名");
    $mysqli->set_charset("utf8");
	if($proj == 10){
		$stmt1 = $mysqli->prepare("SELECT class_1 FROM mng_code_list");
		$stmt1->execute();
		$stmt1->bind_result($class_1);
		while($stmt1->fetch()){
			$class_name_duplicate[] = $class_1;
		}
		$stmt1->close();
		$class_name = array_unique($class_name_duplicate);
	}elseif($proj < 100){
		$stmt2 = $mysqli->prepare("SELECT class_2, class_3 FROM mng_code_list WHERE class_1 = '$this_class_1'");
		$stmt2->execute();
		$stmt2->bind_result($class_2, $class_3);
		while($stmt2->fetch()){
			$class_name[] = $class_2;
		}
		$stmt2->close();
	}elseif($proj == 110){
		$stmt3 = $mysqli->prepare("SELECT code_2l FROM class_list");
		$stmt3->execute();
		$stmt3->bind_result($class_1);
		while($stmt3->fetch()){
			$class_name_duplicate[] = $class_1;
		}
		$stmt3->close();
		$class_name = array_unique($class_name_duplicate);
	}elseif($proj <= 200){
		$stmt4 = $mysqli->prepare("SELECT code_2l, theme_name FROM class_list WHERE code_2l = '$this_class_1'");
		$stmt4->execute();
		$stmt4->bind_result($class_1, $class_2);
		while($stmt4->fetch()){
			$class_name[] = $class_1 . "/" . $class_2;
		}
		$stmt4->close();
	}
	$mysqli->close();
}

/*
テーブルPPF_prelistから分類別のサブリストとポートフォリオを生成
*/
// ポートフォリオを取得するPortfolioクラスのインスタンス$pfを生成
require_once("Portfolio.class.php");
$pf = new Portfolio();
// Exportクラスのインスタンス$epのメソッド内で$pfのメソッドを実行
require_once("Export.class.php");
$ep = new Export();
$ep->exportSublist($proj, $class_name, $pf, $sub_fnum_str);

/*
ポートフォリオをブラウザに表示
*/
if($sub_fnum_str == NULL && ($proj <= 50 || $proj > 100 && $proj <= 150)){
?>

  <FONT COLOR='0000ff'><B>・グローバル出願率:</B></FONT>
  [日本出願後1年を経過していない外国未出願案件は除外して計算] 外国出願件数÷(外国出願件数+出願後1年以上経過の外国未出願件数)
<BR>
  <FONT COLOR='0000ff'><B>・グローバル登録率:</B></FONT>
  [日本のみ登録の特許から外国審査係属中の案件を除外して計算]  外国登録件数÷(外国登録件数+外国審査係属中を除く日本のみ登録件数)
<BR>
   <FONT COLOR='ff0000'><B>※ 件数はすべてファミリー単位</B></FONT>
<P>
<CENTER>
<TABLE  class="table_1">
<TR BGCOLOR='99ccff'>
<?php if($proj == 10){ ?>
<TD ROWSPAN='2' WIDTH='180' ALIGN='CENTER'>本部/BU</TD>
<?php }elseif($proj <= 50){ ?>
<TD ROWSPAN='2' WIDTH='180' ALIGN='CENTER'>業務処理コード名称</TD>
<?php }elseif($proj == 110){ ?>
<TD ROWSPAN='2' WIDTH='100' ALIGN='CENTER'>テーマコード<BR>上位2桁</TD>
<?php }else{ ?>
<TD ROWSPAN='2' WIDTH='180' ALIGN='CENTER'>テーマ名</TD>
<?php } ?>
<TD ROWSPAN='2' WIDTH='90' ALIGN='CENTER'>生存<BR>family数</TD>
<TD ROWSPAN='2' WIDTH='90' ALIGN='CENTER'>内、外国<BR>出願あり</TD>
<TD COLSPAN='4' ALIGN='CENTER'>1ヶ国のみ登録</TD>
<TD COLSPAN='2' ALIGN='CENTER'>2ヶ国登録</TD>
<TD COLSPAN='2' ALIGN='CENTER'>3ヶ国以上登録</TD>
<TD ROWSPAN='2' WIDTH='100' ALIGN='CENTER'>グローバル<BR>出願率 (%)</TD>
<TD ROWSPAN='2' WIDTH='100' ALIGN='CENTER'>グローバル<BR>登録率 (%)</TD>
</TR>
<TR BGCOLOR='99ccff'>
<TD WIDTH='80' ALIGN='CENTER'>日本</TD>
<TD WIDTH='80' ALIGN='CENTER'>米国</TD>
<TD WIDTH='80' ALIGN='CENTER'>中国</TD>
<TD WIDTH='80' ALIGN='CENTER'>他</TD>
<TD WIDTH='80' ALIGN='CENTER'>米あり</TD>
<TD WIDTH='80' ALIGN='CENTER'>米なし</TD>
<TD WIDTH='80' ALIGN='CENTER'>米あり</TD>
<TD WIDTH='80' ALIGN='CENTER'>米なし</TD>
</TR>

<?php }elseif($sub_fnum_str == NULL && ($proj > 50 && $proj <= 100)){ ?>
<H3>
  ・<FONT COLOR='ff0000'>赤字</FONT>の番号は本年使用「有」の特許を含むファミリー
</H3>
<CENTER>
<TABLE  class="table_1">
<TR BGCOLOR='99ccff'>
<TD WIDTH='60' ALIGN='CENTER'>処理<BR>コード</TD>
<TD WIDTH='180' ALIGN='CENTER'>日本のみ</TD>
<TD WIDTH='180' ALIGN='CENTER'>米国のみ</TD>
<TD WIDTH='120' ALIGN='CENTER'>中国のみ</TD>
<TD WIDTH='60' ALIGN='CENTER'>他1国<BR>のみ</TD>
<TD WIDTH='120' ALIGN='CENTER'>米あり<BR>2国</TD>
<TD WIDTH='60' ALIGN='CENTER'>米なし<BR>2国</TD>
<TD WIDTH='120' ALIGN='CENTER'>米あり<BR>≧3国</TD>
<TD WIDTH='60' ALIGN='CENTER'>米なし<BR>≧3国</TD>
<TD WIDTH='120' ALIGN='CENTER'>プラチナ候補</TD>
<TD WIDTH='120' ALIGN='CENTER'>プラチナ予備軍</TD>
</TR>

<?php }elseif($sub_fnum_str == NULL && ($proj > 150 && $proj <= 200)){ ?>
<H3>
  ・<FONT COLOR='ff0000'>赤字</FONT>の番号は本年使用「有」の特許を含むファミリー
</H3>
<CENTER>
<TABLE  class="table_1">
<TR BGCOLOR='99ccff'>
<TD WIDTH='180' ALIGN='CENTER'>テーマ名</TD>
<TD WIDTH='140' ALIGN='CENTER'>日本のみ</TD>
<TD WIDTH='140' ALIGN='CENTER'>米国のみ</TD>
<TD WIDTH='80' ALIGN='CENTER'>中国のみ</TD>
<TD WIDTH='60' ALIGN='CENTER'>他1国<BR>のみ</TD>
<TD WIDTH='120' ALIGN='CENTER'>米あり<BR>2国</TD>
<TD WIDTH='60' ALIGN='CENTER'>米なし<BR>2国</TD>
<TD WIDTH='120' ALIGN='CENTER'>米あり<BR>≧3国</TD>
<TD WIDTH='60' ALIGN='CENTER'>米なし<BR>≧3国</TD>
<TD WIDTH='120' ALIGN='CENTER'>プラチナ候補</TD>
<TD WIDTH='120' ALIGN='CENTER'>プラチナ予備軍</TD>
</TR>

<?php }
if($sub_fnum_str == NULL){
// 配列$class_nameを<INPUT TYPE='hidden'>で引渡すために文字列に変換
	foreach($class_name as $value){
		$class_name_str_tmp .= $value;
		$class_name_str_tmp .= '+';
	}
	$class_name_str = rtrim($class_name_str_tmp, "+");
	foreach($class_name as $value){
		if($pf->app_nai["$value"] == NULL) $pf->app_nai["$value"] = 0;
		if($pf->app_gai["$value"] == NULL) $pf->app_gai["$value"] = 0;		
		if($pf->iss_one_JP["$value"] == NULL) $pf->iss_one_JP[$value] = 0;
		if($pf->iss_one_US["$value"] == NULL) $pf->iss_one_US[$value] = 0;
		if($pf->iss_one_CN["$value"] == NULL) $pf->iss_one_CN[$value] = 0;
		if($pf->iss_one_other["$value"] == NULL) $pf->iss_one_other[$value] = 0;
		if($pf->iss_two_US["$value"] == NULL) $pf->iss_two_US[$value] = 0;
		if($pf->iss_two_other["$value"] == NULL) $pf->iss_two_other[$value] = 0;
		if($pf->iss_three_US["$value"] == NULL) $pf->iss_three_US[$value] = 0;
		if($pf->iss_three_other["$value"] == NULL) $pf->iss_three_other[$value] = 0;
		if($pf->star_pat_cnt["$value"] == NULL) $pf->star_pat_cnt[$value] = 0;
		if($pf->star_can_cnt["$value"] == NULL) $pf->star_can_cnt[$value] = 0;
		if($pf->app_nai4gai["$value"] == NULL){
			$gai_percent["$value"] = '-';
		}else{
			$gai_percent["$value"] = round($pf->app_gai["$value"] / $pf->app_nai4gai["$value"] * 100);
		}
        $iss_total["$value"] = $pf->iss_JP_only["$value"] + $pf->iss_one_US["$value"]
                               + $pf->iss_one_CN["$value"] + $pf->iss_one_other["$value"]
                               + $pf->iss_two_US["$value"] + $pf->iss_two_other["$value"]
                               + $pf->iss_three_US["$value"] + $pf->iss_three_other["$value"];
		if($iss_total["$value"] == NULL){
			$gai_iss_percent["$value"] = '-';
		}else{
            $gai_iss_percent["$value"] = round(($iss_total["$value"]
                                         - $pf->iss_JP_only["$value"]) / $iss_total["$value"] * 100);
		}
		if($pf->iss_one_j["$value"] == NULL){
			$iss_one_j["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_one_j["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_one_j["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_one_j["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_one_j["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$iss_one_j["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_one_j["$value"] .=  $pf->iss_one_j["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
    	if($pf->iss_one_u["$value"] == NULL){
			$iss_one_u["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_one_u["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_one_u["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_one_u["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_one_u["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$iss_one_u["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_one_u["$value"] .= $pf->iss_one_u["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->iss_one_c["$value"] == NULL){
			$iss_one_c["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_one_c["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_one_c["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_one_c["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_one_c["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
    $iss_one_c["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_one_c["$value"] .= $pf->iss_one_c["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->iss_one_o["$value"] == NULL){
			$iss_one_o["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_one_o["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_one_o["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_one_o["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_one_o["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$iss_one_o["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_one_o["$value"] .= $pf->iss_one_o["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->iss_two_u["$value"] == NULL){
			$iss_two_u["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_two_u["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_two_u["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_two_u["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_two_u["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$iss_two_u["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_two_u["$value"] .= $pf->iss_two_u["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->iss_two_o["$value"] == NULL){
			$iss_two_o["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_two_o["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_two_o["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_two_o["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_two_o["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$iss_two_o["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_two_o["$value"] .= $pf->iss_two_o["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->iss_three_u["$value"] == NULL){
			$iss_three_u["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_three_u["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_three_u["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_three_u["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_three_u["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$iss_three_u["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_three_u["$value"] .= $pf->iss_three_u["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->iss_three_o["$value"] == NULL){
			$iss_three_o["$value"] = " ";
		}else{
			$sub_fnum_tmp = str_replace("<FONT COLOR='ff0000'>", NULL, $pf->iss_three_o["$value"]);
			$sub_fnum_str = str_replace("</FONT>", NULL, $sub_fnum_tmp);
			$iss_three_o["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$iss_three_o["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$iss_three_o["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$iss_three_o["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$iss_three_o["$value"] .= $pf->iss_three_o["$value"] . " <INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->star_pat["$value"] == NULL){
			$star_pat["$value"] = " ";
		}else{
			$sub_fnum_tmp1 = str_replace("日", NULL, $pf->star_pat["$value"]);
			$sub_fnum_tmp2 = str_replace("米", NULL, $sub_fnum_tmp1);
			$sub_fnum_tmp3 = str_replace("中", NULL, $sub_fnum_tmp2);
			$sub_fnum_tmp4 = str_replace("(", NULL, $sub_fnum_tmp3);
			$sub_fnum_tmp5 = str_replace(")", NULL, $sub_fnum_tmp4);
			$sub_fnum_str = str_replace("<BR>", ", ", $sub_fnum_tmp5);
			$star_pat["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$star_pat["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$star_pat["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$star_pat["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$star_pat["$value"] .= $pf->star_pat["$value"] . "<BR><INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($pf->star_can["$value"] == NULL){
			$star_can["$value"] = " ";
		}else{
			$sub_fnum_tmp1 = str_replace("日", NULL, $pf->star_can["$value"]);
			$sub_fnum_tmp2 = str_replace("米", NULL, $sub_fnum_tmp1);
			$sub_fnum_tmp3 = str_replace("中", NULL, $sub_fnum_tmp2);
			$sub_fnum_tmp4 = str_replace("(", NULL, $sub_fnum_tmp3);
			$sub_fnum_tmp5 = str_replace(")", NULL, $sub_fnum_tmp4);
			$sub_fnum_str = str_replace("<BR>", ", ", $sub_fnum_tmp5);
			$star_can["$value"] = "<FORM ACTION='makeTable.php' METHOD='POST'>";
			$star_can["$value"] .= "<INPUT TYPE='hidden' NAME='Proj' VALUE='$proj'>";
			$star_can["$value"] .= "<INPUT TYPE='hidden' NAME='Class_name_str' VALUE='$class_name_str'>";
			$star_can["$value"] .= "<INPUT TYPE='hidden' NAME='Sub_fnum_str' VALUE='$sub_fnum_str'>";
			$star_can["$value"] .= $pf->star_can["$value"] . "<BR><INPUT TYPE='SUBMIT' VALUE='list'></FORM>";
		}
		if($proj > 110){
			$value_div = explode("/", $value);
			$value_disp = $value_div[1];
		}else{
			$value_disp = $value;
		}
		if($proj == 110){
			printf("<TR><TD BGCOLOR='99ff99' ALIGN='CENTER'>%s</TD>", $value_disp);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->app_nai["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->app_gai["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_JP["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_US["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_CN["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_other["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_two_US["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_two_other["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_three_US["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_three_other["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $gai_percent["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD></TR>", $gai_iss_percent["$value"]);
		}elseif($proj <= 50 || $proj > 100 && $proj <= 150){
			printf("<TR><TD BGCOLOR='99ff99'>%s</TD>", $value_disp);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->app_nai["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->app_gai["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_JP["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_US["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_CN["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_one_other["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_two_US["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_two_other["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_three_US["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $pf->iss_three_other["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD>", $gai_percent["$value"]);
			printf("<TD ALIGN='CENTER'>%s</TD></TR>", $gai_iss_percent["$value"]);
		}else{
			printf("<TR><TD BGCOLOR='99ff99'>%s</TD>", $value_disp);
			printf("<TD>%s</TD><TD>%s</TD>", $iss_one_j["$value"], $iss_one_u["$value"]);
			printf("<TD>%s</TD><TD>%s</TD>", $iss_one_c["$value"], $iss_one_o["$value"]);
			printf("<TD>%s</TD><TD>%s</TD>", $iss_two_u["$value"], $iss_two_o["$value"]);
			printf("<TD>%s</TD><TD>%s</TD>", $iss_three_u["$value"], $iss_three_o["$value"]);
			printf("<TD>%s</TD><TD>%s</TD></TR>", $star_pat["$value"], $star_can["$value"]);
		}
	}
	$app_nai_sum = array_sum($pf->app_nai);
	$app_gai_sum = array_sum($pf->app_gai);
	$iss_one_JP_sum = array_sum($pf->iss_one_JP);
	$iss_one_US_sum = array_sum($pf->iss_one_US);
	$iss_one_CN_sum = array_sum($pf->iss_one_CN);
	$iss_one_other_sum = array_sum($pf->iss_one_other);
	$iss_two_US_sum = array_sum($pf->iss_two_US);
	$iss_two_other_sum = array_sum($pf->iss_two_other);
	$iss_three_US_sum = array_sum($pf->iss_three_US);
	$iss_three_other_sum = array_sum($pf->iss_three_other);
	$star_pat_cnt_sum = array_sum($pf->star_pat_cnt);
	$star_can_cnt_sum = array_sum($pf->star_can_cnt);
	$app_nai4gai_sum = array_sum($pf->app_nai4gai);
	if($app_nai4gai_sum == NULL){
		$gai_sum_percent = '-';
	}else{
		$gai_sum_percent = round($app_gai_sum / $app_nai4gai_sum * 100);
	}
	$iss_total_sum = array_sum($iss_total);
	$iss_JP_only_sum = array_sum($pf->iss_JP_only);
	if($iss_total_sum == NULL){
		$gai_sum_iss_percent = '-';
	}else{
		$gai_sum_iss_percent = round(($iss_total_sum - $iss_JP_only_sum) / $iss_total_sum *100);
	}
	if($proj <= 50 || $proj > 100 && $proj <= 150){
		printf("<TR BGCOLOR='ffff66'><TD ALIGN='CENTER'>合   計</TD><TD ALIGN='CENTER'>%s</TD>", $app_nai_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $app_gai_sum, $iss_one_JP_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_one_US_sum, $iss_one_CN_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_one_other_sum, $iss_two_US_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_two_other_sum, $iss_three_US_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_three_other_sum, $gai_sum_percent);
		printf("<TD ALIGN='CENTER'>%s</TD></TR>", $gai_sum_iss_percent);
	}else{
		printf("<TR BGCOLOR='ffff66'><TD ALIGN='CENTER'>合   計</TD><TD ALIGN='CENTER'>%s</TD>", $iss_one_JP_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_one_US_sum, $iss_one_CN_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_one_other_sum, $iss_two_US_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_two_other_sum, $iss_three_US_sum);
		printf("<TD ALIGN='CENTER'>%s</TD><TD ALIGN='CENTER'>%s</TD>", $iss_three_other_sum, $star_pat_cnt_sum);
		printf("<TD ALIGN='CENTER'>%s</TD></TR>", $star_can_cnt_sum);
	}
	printf("</TABLE></CENTER><P> <P></BODY></HTML>");
}

トップ画面にある「コード体系の表示」以外の「GO」ボタンは全てmakeTable.phpにフォームを送信しますが、サブテーブルを表示する場合とExcelファイルを出力する場合があることは既に述べた通りです。7~54行目と115行~最終行は前者の場合のみに実行され、"$sub_fnum_str == NULL"を条件にしています。if(){の直後に?>でPHPを終了し、HTMLをそのまま書いた後に再度<?phpでPHPに入って}を閉じるというコーディングは、以前に紹介した<?php echo $foo; ?>以上にPHPらしい感じがします。4行目にある通り、$sub_fnum_strはフォームからhiddenで渡されたSub_fnum_strですが、その本質的な意味は後で説明します。

105~110行目はExcelファイルを出力する場合にも実行されます。Portfolioクラスは特許1件ごとのデータを受取って、family単位で何ヶ国に登録されているかというポートフォリオを作成しますが、そのインスタンスが$pfです。一方、Exportクラスの役割は$pfにMySQLから読出したデータを渡すことと、Excelファイルを出力することで、そのインスタンスが$epです。110行目で$epのexportSublistメソッドを実行する際に$pfを引数として渡しますが、当該メソッドの中で$pfのメソッドが実行され、$pfのプロパティとしてポートフォリオがセットされます。サブテーブルを表示する場合は、115行~最終行でそのポートフォリオを使います。

$sub_fnum_strの意味について説明します。トップ画面のテーブルの3列目の「GO」で表示されるサブテーブルのセルは、当初は4列目の「GO」で出力されるExcel特許リストの通し番号を列挙するだけでした。しかし、完成間近の段階で関係者に試用版を使ってもらっていると、列挙した番号の特許だけをExcelファイルに出力したいと言われたのです。ユーザ目線で見ればもっともな要望ですが、システム的には「最初から作り直し!?」というレベルです。そこで考え抜いた答がmakeTable.phpを再帰的に呼出す「list」ボタンで、列挙した番号の文字列をSub_fnum_strとして送信してExcelファイルを出力するという手法です。

フォームから受取る変数にSub_fnum_strを加えたので、サブリストを表示するかExcelファイルを出力するかのフラッグはなくし、$sub_fnum_strがNULLの場合はサブリストを出力することにしました。トップ画面のテーブル4列目の「GO」ボタンはExcel出力なので、Sub_fnum_strを'all'としています。59~99行目は$sub_fnum_strがNULLか'all'の場合、すなわちトップ画面から呼出された場合に実行されます。ここではサブテーブルの1列目に表示する中分類をMySQLから取得しています。Excelファイルを出力する場合でも、個々の特許のレコードには中分類しか記載されていないので、この処理が必要です。

8.3 Portfolio.class.php

Portfoli.class.phpは元のシステムのままです。前述の通り、Portfolioクラスのみを記述しています。"function __construct() "はインスタンスを生成する際に呼出されるメソッドですが、{}の中に実行文がなくても書いておくようにとPHPマニュアルにありました。public宣言したプロパティとメソッドはクラスの外から参照することができますが、例えば$app_naiプロパティを参照する場合は$pf->app_naiとし、app_naiには$を付けません。コメントにある「サブF(amily)番号文字列」は$sub_fnum_strのことです。Portfolioクラスの役割はmakeTable.phpで述べた通りですが、動作について説明すると長くなるので割愛します。


<?php
class Portfolio{
	public function __construct(){ /* コンストラクタ */ }

	public $app_nai = array();
	public $app_nai4gai = array();
	public $app_gai = array();
	public $iss_one_j = array();
	public $iss_one_u = array();
	public $iss_one_c = array();
	public $iss_one_o = array();
	public $iss_two_u = array();
	public $iss_two_o = array();
	public $iss_three_u = array();
	public $iss_three_o = array();
	public $iss_one_JP = array();
	public $iss_JP_only = array();
	public $iss_one_US = array();
	public $iss_one_CN = array();
	public $iss_one_other = array();
	public $iss_two_US = array();
	public $iss_two_other = array();
	public $iss_three_US = array();
	public $iss_three_other = array();
	public $star_can = array();
	public $star_pat = array();
	public $star_can_cnt = array();
	public $star_pat_cnt = array();
	private $iss_cnt = 0;
	private $iss_state = array();
	private $family_siyo_umu = FALSE;
	private $star_can_state = NULL;
	private $star_pat_state = NULL;
	private $mng_code_old = NULL;
	private $class_name_old = NULL;
	private $iss_JP_only_flag = FALSE;

/*
ポートフォリオの登録国数カウント
*/
	public function countIssState($iss_num, $state, $siyo_umu, $star){
		if($iss_num == NULL){
			if($state != '日本') $this->iss_JP_only_flag = TRUE;
		}elseif(!$this->iss_state["$state"]){
				$this->iss_cnt++;
				$this->iss_state["$state"] = TRUE;
		}
		if($siyo_umu == "有") $this->family_siyo_umu = TRUE;
		if($star == 'スター候補'){
			if($state == '日本'){
				$this->star_can_state .= '日';
			}
			elseif($state == 'アメリカ'){
				$this->star_can_state .= '米';
			}
			elseif($state == '中国'){
				$this->star_can_state .= '中';
			}
		}
		elseif($star == 'スター特許'){
			if($state == '日本'){
				$this->star_pat_state .= "日";
			}
			elseif($state == 'アメリカ'){
				$this->star_pat_state .= "米";
			}
			elseif($state == '中国'){
				$this->star_pat_state .= "中";
			}
		}
	}

/*
ポートフォリオの出願件数、登録国数対応サブF番号文字列を生成
*/
	public function makeFnumString($sub_fnum, $gai_umu, $gai_umu_flag, $state, $mng_code, $class_name){
// 出願件数カウントアップ
		if($class_name != NULL){
			$this->app_nai[$class_name]++;
			if($gai_umu_flag || $gai_umu == 'あり'){
				$this->app_nai4gai[$class_name]++;
			}
		}
		if($gai_umu == 'あり') $this->app_gai[$class_name]++;
// サブF番号にフォント色を指定
		if($this->family_siyo_umu){
			$sub_fnum_color = "<FONT COLOR='ff0000'>" . $sub_fnum . "</FONT>";
		}else{
			$sub_fnum_color = $sub_fnum;
		}
// 登録国数対応サブF番号文字列を生成し、件数を集計
		if($this->iss_cnt == 1){
			$this->iss_one_cnt[$this->class_name_old]++;
			if($this->iss_state['日本']){
				$this->iss_one_JP[$this->class_name_old]++;
				if($this->iss_one_j[$this->class_name_old] == NULL){
					$this->iss_one_j[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_one_j[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
				if(!$this->iss_JP_only_flag) $this->iss_JP_only[$this->class_name_old]++;
			}elseif($this->iss_state['アメリカ']){
				$this->iss_one_US[$this->class_name_old]++;
				if($this->iss_one_u[$this->class_name_old] == NULL){
					$this->iss_one_u[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_one_u[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
			}elseif($this->iss_state['中国']){
				$this->iss_one_CN[$this->class_name_old]++;
				if($this->iss_one_c[$this->class_name_old] == NULL){
					$this->iss_one_c[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_one_c[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
			}else{
				$this->iss_one_other[$this->class_name_old]++;
				if($this->iss_one_o[$this->class_name_old] == NULL){
					$this->iss_one_o[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_one_o[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
			}
		}elseif($this->iss_cnt == 2){
			if($this->iss_state['アメリカ']){
				$this->iss_two_US[$this->class_name_old]++;
				if($this->iss_two_u[$this->class_name_old] == NULL){
					$this->iss_two_u[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_two_u[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
			}else{
				$this->iss_two_other[$this->class_name_old]++;
				if($this->iss_two_o[$this->class_name_old] == NULL){
					$this->iss_two_o[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_two_o[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
			}			
		}elseif($this->iss_cnt >= 3){
			if($this->iss_state['アメリカ']){
				$this->iss_three_US[$this->class_name_old]++;
				if($this->iss_three_u[$this->class_name_old] == NULL){
					$this->iss_three_u[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_three_u[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
			}else{
				$this->iss_three_other[$this->class_name_old]++;
				if($this->iss_three_o[$this->class_name_old] == NULL){
					$this->iss_three_o[$this->class_name_old] = $sub_fnum_color;
				}else{
					$this->iss_three_o[$this->class_name_old] .= ", " . $sub_fnum_color;
				}
			}
		}
// スター特許サブF番号文字列を生成し、件数を集計
		if($this->star_can_state != NULL){
			if($this->star_can[$this->class_name_old] == NULL){
				$this->star_can[$this->class_name_old] = $sub_fnum . "(" . $this->star_can_state . ")";
			}else{
				$this->star_can[$this->class_name_old] .= "<BR>" . $sub_fnum . "(" . $this->star_can_state . ")";
			}
			$this->star_can_cnt[$this->class_name_old] += strlen($this->star_can_state) / 2;
		}
		if($this->star_pat_state != NULL){
			if($this->star_pat[$this->class_name_old] == NULL){
				$this->star_pat[$this->class_name_old] = $sub_fnum . "(" . $this->star_pat_state . ")";
			}else{
				$this->star_pat[$this->class_name_old] .= "<BR>" . $sub_fnum . "(" . $this->star_pat_state . ")";
			}
			$this->star_pat_cnt[$this->class_name_old] += strlen($this->star_pat_state) / 2;
		}
// 次回の初期値設定
		$this->mng_code_old = $mng_code;
		$this->class_name_old = $class_name;
		$this->iss_cnt = 0;
		$this->iss_state = array();
		$this->family_siyo_umu = FALSE;
		$this->star_can_state = NULL;
		$this->star_pat_state = NULL;
		$this->iss_JP_only_flag = FALSE;
	}
}
?>

8.4 Export.class.php

Exportクラスについても動作の詳細は説明しませんが、再現版の改修点の2つ目であるExcel出力について述べます。元のシステムではExcelファイルを出力するのにPHPのPEARパッケージを利用していました。PEARをインストールしてSpreadsheet/Excel/Writer.phpをrequire(インポート)すると、"$workbook = new Spreadsheet_Excel_Writer();"を宣言するだけで$workbookを開くのか、保存するのかをブラウザ(IE)が訊いてくれました。そのPEARはPHP7.4で非推奨となり、PHP8では動作しません。また、ファイルを保存せず、「Excelアプリを立上げて」開いてくれるブラウザは、Edgeを含めて今や存在しません。

グーグると、PHPでExcelファイルを出力するにはPhpSpreadsheet(以下、PhpSsと略します)を使えとあります。PhpSsはPHPのライブラリで、Composer(PHPのライブラリ管理ツール)でインストールできます。PhpSsのサンプルコードは問題なく動くのですが、Export.class.phpに埋込むと動かなくなりました。これで相当悩んだのですが、以下のコードで16~18行目にあるuse文を最初はclass Export{...}の中に入れていたので動かなかったのです。コメントにある通り、プログラムの冒頭に置く必要がありました。useは名前空間をインポートするもので、名前空間の階層区切りにはバックスラッシュを使います。


<?php
/*
$proj => 動作
    10 => BU毎の件数(Excel生成なし)
  11- 50 => 業務処理コード毎の件数(Excel生成なし)
  51- 90 => 業務処理コード毎の番号表示+Excel生成
   100 => 日本基礎出願不明のExcel生成のみ
   110 => 技術大分類毎の件数(Excel生成なし)
 111-150 => 技術中分類毎の件数(Excel生成なし)
 151-190 => 技術小分類毎の番号表示+Excel生成
*/

// useキーワードの宣言はファイル内の一番外側のスコープで行う。
// (インポートが実行時ではなくコンパイル時に行われるため)
require "(サーバ上の絶対パス)/vendor/autoload.php";
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

class Export{
	public function __construct(){ /* コンストラクタ */ }
	public $num2BU = array();
	public $code2BU = array();
	public $code2unit = array();
	public $num2class = array();

/*
テーブルPPF_prelistから条件に応じたサブリストをエクセルに出力
*/
	public function exportSublist($proj, $class_name, $pf, $sub_fnum_str){
		if($sub_fnum_str != NULL && $sub_fnum_str != 'all'){
			$sub_fnum_array = explode(", ", $sub_fnum_str);
		}else{
			$sub_fnum_array = array();
		}
// Excelファイルへの書込み準備と書式設定
		if($sub_fnum_str != NULL){
            $spreadsheet = new Spreadsheet();
            $sheet = $spreadsheet->getActiveSheet();
            $sheet->setTitle("PPF_sublist");
            $font = $spreadsheet->getDefaultStyle()->getFont();
            $font->setName('MS Pゴシック');
            $font->setSize(10);
            $sheet->getColumnDimension('A')->setWidth(5);
            $sheet->getColumnDimension('B')->setWidth(10);
            $sheet->getColumnDimension('C')->setWidth(8);
            $sheet->getColumnDimension('D')->setWidth(12);
            $sheet->getColumnDimension('E')->setWidth(12);
            $sheet->getColumnDimension('F')->setWidth(12);
            $sheet->getColumnDimension('G')->setWidth(12);
            $sheet->getColumnDimension('H')->setWidth(12);
            $sheet->getColumnDimension('I')->setWidth(12);
            $sheet->getColumnDimension('J')->setWidth(15);
            $sheet->getColumnDimension('K')->setWidth(30);
            $style = $sheet->getStyle('A');
            $style->getAlignment()->setHorizontal('center');
            $style = $sheet->getStyle('B');
            $style->getAlignment()->setHorizontal('center');
            $style = $sheet->getStyle('C');
            $style->getAlignment()->setHorizontal('center');
            $style = $sheet->getStyle('F');
            $style->getAlignment()->setHorizontal('center');
            $style = $sheet->getStyle('H');
            $style->getAlignment()->setHorizontal('center');
// 見出しの書込み
            $header_1 = ['番号', 'GP番号', '管理籍', '基礎出願', 'IPC', 'テーマコード', 'テーマ名', '', '本年使用', '発明の名称', '要約(50文字)'];
            $sheet->fromArray($header_1, null, 'A1');
            $style = $sheet->getStyle('A1:K1');
            $style->getAlignment()->setHorizontal('center');
            $style->getFont()->getColor()->setARGB('FF0000FF');
            $style->getFont()->setBold(true);
            $style->getFill()->setFillType('solid')->getStartColor()->setARGB('FFCCFFFF');
            $header_2 = ['管理番号', '国名', '出願の種類', '状態', '出願番号', '出願日', '登録番号', '権利満了日', 'プラチナ登録', 'プラチナ登録詳細情報'];
            $sheet->fromArray($header_2, null, 'B2');
            $style = $sheet->getStyle('B2:K2');
            $style->getAlignment()->setHorizontal('center');
            $style->getFont()->getColor()->setARGB('FF0000FF');
            $style->getFont()->setBold(true);
        }
// 業務処理コードからBU名あるいは業務処理コード名称を得る配列を生成
        $this->makeArray(2);
// 対象分類のレコードをテーブルprelistから読出し
        $mysqli = new mysqli("ホスト名",  "ユーザ名",  "パスワード", "データベース名");
        $mysqli->set_charset("utf8");
        $stmt1 = $mysqli->prepare("SELECT * FROM prelist ORDER BY g_fnum, app_date");
        $stmt1->execute();
        $stmt1->bind_result($id, $g_fnum, $p_num, $app_type, $status, $app_num, $app_date, $state, $iss_num, $exp_date,
        $title, $abst, $mng_code, $gai_umu, $siyo_umu, $star, $class_1, $class_2, $class_3, $class_4, $kiso_num);
        $row = 2;
        $sub_fnum = 0;
        while($stmt1->fetch()){
// Excelに書出す時のセル番号との対応付け
            $data_1[1] = $g_fnum;
            $data_1[2] = $mng_code;
            $data_1[3] = $class_1;
            $data_1[4] = $class_2;
            $data_1[5] = $class_3;
            $data_1[6] = $class_4;
            $data_1[7] = " ";
            $data_1[8] = NULL;
            $data_1[9] = $title;
            $data_1[10] = $abst;
            $data_2[1] = $p_num;
            $data_2[2] = $state;
            $data_2[3] = $app_type;
            $data_2[4] = $status;
            $data_2[5] = $app_num;
            $data_2[6] = date('Y/n/j', strtotime($app_date));
            $data_2[7] = $iss_num;
            if($exp_date != NULL){
                $data_2[8] = date('Y/n/j', strtotime($exp_date));
            }else{
                $data_2[8] = NULL;
            }
            $data_2[9] = $star;
            $data_2[10] = NULL;
# 要約がブランクの場合、全角空白に置換え
            if($data_1[10] == NULL){
                $data_1[10] = " ";
            }
            if($proj == 100){
                $this_class_name = $class_1 . '/' . $class_2;
            }elseif($proj == 10){
                $this_class_name = $this->code2BU["$mng_code"];
            }elseif($proj < 100){
# 同一ファミリーで管理籍が違う場合、最初のレコードの管理籍を採用
                if($g_fnum == $g_fnum_old_2){
                    $this_class_name = $class_2_old;
                }else{
                    $this_class_name = $this->code2unit["$mng_code"];
                    $class_2_old = $this->code2unit["$mng_code"];
                }
            }elseif($proj <= 200){
                if($proj == 110){
                    $this_class_name = substr($class_3, 0, 2);
                }else{
                    $this_class_name = substr($class_3, 0, 2) . "/" . $class_4;
                }
            }else{
                $this_class_name = NULL;
            }
            $g_fnum_old_2 = $g_fnum;

            if(array_search($this_class_name, $class_name) !== FALSE){
// 読出したレコードのF番号が変った場合、ファミリー情報を書出し
                if($g_fnum != $g_fnum_old){
# 出願1年未満と「社内秘匿登録」は$gai_umu_flagをFALSEに
                    if(strtotime($app_date) < strtotime("-1 year") && $app_type != '社内秘匿登録'){
                        $gai_umu_flag = TRUE;
                    }else{
                        $gai_umu_flag = FALSE;
                    }
# ポートフォリオの出願件数、登録国数対応サブF番号文字列を生成
                    if($sub_fnum_str == NULL){
                        $pf->makeFnumString($sub_fnum, $gai_umu, $gai_umu_flag, $state, $mng_code, $this_class_name);
                    }
                    $sub_fnum++;
                    if($sub_fnum_str != NULL){
// 日本基礎出願不明の出力対応
                        if($sub_fnum_str == 'publish' && $status != '出願未公開' && $app_type != '社内秘匿登録'){
                            $jpflg["$g_fnum"] = TRUE;
                        }else{
                            $jpflg["$g_fnum"] = FALSE;
                        }
// ファミリーヘッダの書出しと、サブF番号/F番号の更新
                        if($jpflg["$g_fnum"] || $sub_fnum_str == 'all' || array_search($sub_fnum, $sub_fnum_array) !== FALSE){
                            $row++;
                            $row_header = $row;
                            $sheet->setCellValue("A$row", $sub_fnum);
                            $style = $sheet->getStyle("A$row:K$row");
                            $style->getFill()->setFillType('solid')->getStartColor()->setARGB('FFCCFFFF');
                            foreach($data_1 as $key => $value){
                                if($key == 3 && $value != NULL){
# 基礎公報リンクをurl書式で書込む
                                    $ganban_tmp = explode("-", $value);
                                    $ganban = $ganban_tmp[0] . $ganban_tmp[1];
                                    $sheet->setCellValue([$key + 1, $row], $value);
                                    $style = $sheet->getStyle([$key + 1, $row]);
                                    $style->getFont()->getColor()->setARGB('FF0000FF');
                                    $style->getFont()->setUnderline(true);
                                    $style->getFill()->setFillType('solid')->getStartColor()->setARGB('FFCCFFFF');
                                }else{
                                    $sheet->setCellValue([$key + 1, $row], $value);
                                }
                            }
# 要約の右隣のセルに全角空白を入れる
                            $sheet->setCellValue([12, $row], ' ');
                        }
                    }
                    $g_fnum_old = $g_fnum;
                }
// 全てのレコードに対して、個々の特許情報を書出し
# ポートフォリオの登録国数カウント
                if($sub_fnum_str == NULL && $app_type != '社内秘匿登録'){
                    $pf->countIssState($iss_num, $state, $siyo_umu, $star);
                }elseif($jpflg["$g_fnum"] || $sub_fnum_str == 'all' || array_search($sub_fnum, $sub_fnum_array) !== FALSE){
                    $row++;
# 本年使用「有」の場合、$row_header行の8列目に記入
                    if($siyo_umu == '有' && !$siyo_umu_flg["$row_header"]){
                       $sheet->setCellValue([8, $row_header], $siyo_umu);
                       $siyo_umu_flg["$row_header"] = TRUE;
                    }
                    foreach($data_2 as $key => $value){
                        if(($data_2[2] == '日本' && $data_2[4] == '出願未公開' || $data_2[2] == 'PCT') && $key == 5 && $value != NULL){
# 日本包袋リンクをurl書式で書込む
                            if($data_2[3] == 'PCT優先出願' && $data_2[4] == '出願未公開' || $data_2[2] == 'PCT'){
                                $ganban = substr($kiso_num, 15, 10);
                            }else{
                                $ganban_tmp = explode('-', $value);
                                if(substr($ganban_tmp[0], 1) == 'H'){
                                    $ganban_tmp[0] = substr_replace($ganban_tmp[0], NULL, 0, 1);
                                    $ganban_tmp[0] += 1988;
                                }
                                $ganban = $ganban_tmp[0] . $ganban_tmp[1];
                            }
                            $sheet->setCellValue([$key + 1, $row], $value);
                            $style = $sheet->getStyle([$key + 1, $row]);
                            $style->getFont()->getColor()->setARGB('FF0000FF');
                            $style->getFont()->setUnderline(true);
                        }elseif($data_2[2] == '日本' && $key == 7 && $value !=NULL){
# 日本登録公報リンクをurl書式で書込む
                            $value = ltrim($value, '0');
                            $sheet->setCellValue([$key + 1, $row], $value);
                            $style = $sheet->getStyle([$key + 1, $row]);
                            $style->getAlignment()->setHorizontal('center');
                            $style->getFont()->getColor()->setARGB('FF0000FF');
                            $style->getFont()->setUnderline(true);
                        }elseif($data_2[2] == 'アメリカ' && $key == 7 && $value !=NULL){
# 米国特許公報リンクをurl書式で書込む
                            $sheet->setCellValue([$key + 1, $row], $value);
                            $style = $sheet->getStyle([$key + 1, $row]);
                            $style->getAlignment()->setHorizontal('center');
                            $style->getFont()->getColor()->setARGB('FF0000FF');
                            $style->getFont()->setUnderline(true);
                        }elseif($key == 5 || $key == 7){
# 出願/登録番号の先頭の0を削除し、中央揃えで書込む
                                $value = ltrim($value, '0');
                                $sheet->setCellValue([$key + 1, $row], $value);
                        }else{
# 左揃えで書込む
                            $sheet->setCellValue([$key + 1, $row], $value);
                        }
                    }
                }
            }
        }
        $stmt1->close();
        $mysqli->close();
# 最後のレコードを出願件数/登録国数対応サブF番号文字列生成に反映
        if($sub_fnum_str == NULL){
            $pf->makeFnumString($sub_fnum, NULL, FALSE, NULL, NULL, NULL);
        }
// Excelファイルのダウンロード
        if($sub_fnum_str != NULL){
            header("Content-Description: File Transfer");
            header('Content-Disposition: attachment; filename="PPF_sublist.xlsx"');
            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
            header('Content-Transfer-Encoding: binary');
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header('Expires: 0');
            ob_end_clean(); //バッファ消去
            $writer = new Xlsx($spreadsheet);
            $writer->save('php://output');
        }
	}

/*
テーブルmng_code_listをブラウザに表示
*/
	public function showCode(){
        echo "<TR><TD BGCOLOR='99ccff' WIDTH='150' ALIGN='CENTER'>本部/BU</TD>";
        echo "<TD BGCOLOR='99ccff' WIDTH='250' ALIGN='CENTER'>業務処理コード名称</TD>";
        echo "<TD BGCOLOR='99ccff' WIDTH='150' ALIGN='CENTER'>業務処理コード</TD></TR>";
        $mysqli = new mysqli("ホスト名",  "ユーザ名",  "パスワード", "データベース名");
        $mysqli->query("SET CHARACTER SET utf8");
		$stmt3 = $mysqli->prepare("SELECT * FROM mng_code_list");
		$stmt3->execute();
		$stmt3->bind_result($id, $bu_name, $unit_name, $mng_code);
		$count = 11;
		while($stmt3->fetch()){
			if($bu_name == $bu_name_old){
				$bu_name_disp = " ";
			}else{
				$bu_name_disp = $bu_name;
			}
			printf("<TR><TD>%s</TD><TD>%s</TD><TD ALIGN='CENTER'>%s</TD></TR>", $bu_name_disp, $unit_name, $mng_code);
			$bu_name_old = $bu_name;
		}
		$stmt3->close();
		$mysqli->close();
	}

/*
テーブルclass_listをブラウザに表示
*/
	public function showClass(){
        echo "<TR><TD BGCOLOR='99ccff' WIDTH='100' ALIGN='CENTER'>テーマコード<BR>上位2桁</TD>";
        echo "<TD BGCOLOR='99ccff' WIDTH='340' ALIGN='CENTER'>テーマ名</TD>";
        echo "<TD BGCOLOR='99ccff' WIDTH='150' ALIGN='CENTER'>テーマコード</TD></TR>";
        $mysqli = new mysqli("ホスト名",  "ユーザ名",  "パスワード", "データベース名");
        $mysqli->query("SET CHARACTER SET utf8");
		$stmt4 = $mysqli->prepare("SELECT * FROM class_list");
		$stmt4->execute();
		$stmt4->bind_result($id, $code_2L, $theme_name, $theme_code);
		$count = 11;
		while($stmt4->fetch()){
			if($code_2L == $code_2L_old){
				$code_2L_disp = " ";
			}else{
				$code_2L_disp = $code_2L;
			}
			if(strlen($theme_name) > 42){
				$theme_name = wordwrap($theme_name, 42, "<BR>", true);
			}
			printf("<TR><TD ALIGN='CENTER'>%s</TD><TD>%s</TD><TD ALIGN='CENTER'>%s</TD></TR>", $code_2L_disp, $theme_name, $theme_code);
			$code_2L_old = $code_2L;
		}
		$stmt4->close();
		$mysqli->close();
	}

/*
テーブルmng_code_list/class_listを配列に出力
*/
	public function makeArray($flag){
        $mysqli = new mysqli("ホスト名",  "ユーザ名",  "パスワード", "データベース名");
        $mysqli->query("SET CHARACTER SET utf8");
		if($flag == 1 || $flag ==2){
			$stmt5 = $mysqli->prepare("SELECT * FROM mng_code_list");
		}elseif($flag == 3){
			$stmt5 = $mysqli->prepare("SELECT * FROM class_list");
		}
		$stmt5->execute();
		$stmt5->bind_result($id, $class_1, $class_2, $class_3);
		if($flag == 1){
			$count = 11;
		}elseif($flag == 3){
			$count = 111;
		}
		while($stmt5->fetch()){
			if($flag != 2){
				if($class_1 != $class_1_old){
					if($flag == 1) $this->num2BU[$count++] = $class_1;
					if($flag == 3) $this->num2class[$count++] = $class_1;
				}
				$class_1_old = $class_1;
			}else{
				$this->code2BU[$class_3] = $class_1;
				$this->code2unit[$class_3] = $class_2;
			}
		}
		$stmt5->close();
		$mysqli->close();
	}
}
?>

38~40行目で$spreadsheet(Excelファイル)を生成してアクティブシートである$sheetに名前を付けた後、41~64行目でシート全体のフォントや列幅・文字揃えを一括して指定しています。これはPhpSsでは書式設定に恐ろしく時間がかかるためです。PEARを使う元のプログラムでは、各行を書出すたびにセルごとに書式を設定しても何のストレスもなかったのですが、PhpSsでこれをやると1分間で200行くらい(さくらのレンタルサーバ上、Ubuntu上では数倍速い)しか出力できません。書式設定を一括して行うことで、「7. 特許活用システムPPMの概要」で述べた通り1分間で2,000行以上が出力できるようになりました。

66~78行目は$sheet上に2行ある見出しの書込みですが、ここでも'A1:K1'と'B2:K2'の書式設定をそれぞれ一括して行っています。この後、$sheetのセルへの値の書込みとセルごとにしかできないの書式設定(文字色等)が241行目まで続き、255~263行目でExcelファイルをダウンロードします。PEARに比べて格段の使いにくさで、巷では「PhpSsなんか使うくらいなら、CSVでええやん」という声も聞かれます。しかし、ダウンロードしたExcelファイルを見ていただければ分る通り、これがCSVではとても読めたものではありません。


PPMが完成した直後に駄待ち狐は部署を異動になり、知財戦略担当から技術戦略の担当になりした。「I. 人生の余技として作成したプログラム」の「7-1. VBA」に書いた技術本部で開発中のものを見込み顧客に売込む仕事です。ですから、自分でPPMを活用するということはありませんでした。ただ、人事評価システムと違って自身で発案したものであり、着想以来11年を経て完成したという点と、初めて使ったWAMPと初めて使いこなしたオブジェクト指向言語で所望のシステムを作上げたという点で大いに満足しています。ですから、さくらのレンタルサーバ上で非公開の動態保存を続け、動かなくなったところを改修して再現版とした訳です。

PPMにも「雌伏」のオチがあります。PPMの完成から5年も経たないうちに、なんとi∞proの方が廃棄されてしまったのです。i∞pro構築の予算がいくらだったのかは知りませんが、維持費だけで年間1億円以上かかるということで「経営判断」により廃棄となったらしいです。さらに2017年にはロボットを事業化するSBUでSBU長から「特許を見て下さい」と言われ(「I. 人生の余技として作成したプログラム」の「9. C++」参照)、知財戦略担当になった時の特許調査をまた一からやり直しです。システム構築も知財活動も、この会社ではシーシュポスの岩だと嘆息せざるを得ません。

DBを使って人事評価から特許活用まで(1)に戻る