2012-04-29

【電脳卸】全てのカテゴリをパースする。

電脳卸のWebAPIで「全てのカテゴリを返す」XMLファイルがあるので、それをパースしてみたよ。
PHPの組み込み関数にもXMLをパースする関数があるけど、自分で実装したくなったので自分で書いてみたよ。

APIから返されるXMLの構造が微妙に難しかったので、再帰関数とスタックでやってみた。

APIのURLは→「http://webservice.d-064.com/3.x/category.xml」です。

/**
  電脳卸用全てのカテゴリをパースして配列して、シリアライズ後保存する。
  パース結果の配列は以下のような仕様で出力される
  
  結果配列仕様:
    array( 'カテゴリID 1' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) );
    array( 'カテゴリID 2' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) );
    array( 'カテゴリID 3' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) );
    ...
    array( 'カテゴリID n' => array( 'name' => 'カテゴリ名', 'parent' => '親カテゴリID' ) );
    ※一番上のカテゴリは親カテゴリIDが「-1」となっている。
  
  商品のカテゴリIDから親カテゴリ全てを取得するには、    
    function get_category_tree( $category_id, &$category_tree )
    {
      global $global_category_array;
      
      $id = $category_id;
      $name   = $global_category_array[ $id ][ 'name' ];
      $parent = $global_category_array[ $id ][ 'parent' ];
      
      $category_tree[ $id ] = 
        array( 'name'   => $name, 'parent' => $parent );
      
      if( $parent == '-1' ) {
        return ;
      } else {
        get_category_tree( $parent, $category_tree );
      }
    }
    
    $global_category_array = unserialize( file_get_contents( './category_array.dat' ) );
    $tree = array();
    get_category_tree( '1269', $tree );
    var_dump( $tree );
  といったプログラムで返せる。
*/
define( 'CATEGORY_XML_URL',         'http://webservice.d-064.com/3.x/category.xml' );
define( 'CATEGORY_XML_FILENAME',    dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'category.xml' );
define( 'CATEGORY_ARRAY_FILENAME',  dirname( __FILE__ ) . DIRECTORY_SEPARATOR . 'category_array.dat' );
define( 'CATEGORY_SRC_ENCODING',    'UTF-8' );
define( 'CATEGORY_DIST_ENCODING',   'SJIS' );
date_default_timezone_set( 'Asia/Tokyo' );

/*
  個々のカテゴリデータを取得する
  @return array [ 'category_id', 'category_depth', 'category_name' ]
*/
function get_id_depth_name()
{
  global $xmlp;
  return array(
    preg_replace( '/<[^>]+>(.*?)<\/[^>]+>/', '$1', trim( fgets( $xmlp ) ) ),        // カテゴリID
    preg_replace( '/<[^>]+>(.*?)<\/[^>]+>/', '$1', trim( fgets( $xmlp ) ) ),        // カテゴリ深さ
    preg_replace( '/<[^>]+>(.*?)<\/[^>]+>/', '$1', trim( fgets( $xmlp ) ) ) );      // カテゴリ名
}
 /**
  カテゴリXMLをパースする。再帰関数。
  @param $parent 親カテゴリID
*/
function parse( $parent = -1 ) 
{
  global $xmlp;
  global $global_category_array;
  global $global_parent_ids;        // 親IDのスタック
  
  $line = trim( fgets( $xmlp ) );
  switch( true ) {
    // 空行
    case ($line == ''):
      parse( $parent );
      break;
    
    // XML定義
    case preg_match( '/<\?xml(?:[^>]+)>/', $line ):
      parse( $parent );
      break;
    
    // 一番外側のタグ
    case ($line == ''):
      parse( $parent );
      break;
    case ($line == ''):
      fclose( $xmlp );
      return;
    
    // 1つのカテゴリ
    case ($line == ''):
      list( $id, $depth, $name ) = get_id_depth_name();
      $name = mb_convert_encoding( $name, CATEGORY_DIST_ENCODING, CATEGORY_SRC_ENCODING );
      // 親IDを退避
      $parent = $global_parent_ids[ 0 ];
      // スタックに自分を積む
      array_unshift( $global_parent_ids, $id );
      
      $global_category_array[ $id ][ 'name' ]    = $name;
      $global_category_array[ $id ][ 'parent' ]  = $parent;
      
      parse( $parent );
      break;
    case ($line == ''):
      // 自分のIDを捨てる
      array_shift( $global_parent_ids );
      parse( $global_parent_ids[ 0 ] );
      break;
      
    // サブカテゴリ
    case ($line == ''):
      parse( $global_parent_ids[ 0 ] );
      break;
    case ($line == ''):
      parse( $global_parent_ids[ 0 ] );
      break;
    
    // ファイルが終わる前に「/Categorys」が来るはずなので使わないかも。
    case feof( $xmlp ):
      fclose( $xmlp );
      return;
      
    // 何かあったときのための保険。そのまま終了。
    default:
      die( '[' . $line . '] Exception.' );
  }
}


// XMLファイルダウンロードと保存
file_put_contents( CATEGORY_XML_FILENAME, file_get_contents( CATEGORY_XML_URL ) );

// XMLファイルのポインタ取得
$xmlp = fopen( CATEGORY_XML_FILENAME, 'r' );
if( !$xmlp ) {
  die( 'XML File Dont Open.' );
}

// 親IDスタックと結果配列準備
$global_parent_ids      = array( -1 );
$global_category_array  = array();

// パース本体
parse();

// 結果配列をシリアライズして保存
file_put_contents( CATEGORY_ARRAY_FILENAME, serialize( $global_category_array ) );

exit( 0 );

こんな感じ。

ZenBack

WebMoney ぷちカンパ