6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【短命に終わった】国民の祝日.csvをダウンロードしてパースするPHPコードの解説予定地

Posted at

「よっしゃ当日中にPHPクラス書いたぞ。」
「インターフェースとか例外とかファイルの扱いイケてないからもうちょっと寝かせるか。」
マジかよ!すべて台無しだ。お前はすべてを台無しにする。」
「この様はまるでお前の人生だ。」
「色んなことを始めるが、馬鹿みたいにリリースを遅らせる。」
「誰もお前のことを愛さない。」

とりあえずコードだけ置いておく

あんまブラッシュアップしてないので公開を躊躇してたらこれだよ。

JapanNationalHoliday.php
<?php

class JapanNationalHoliday {
	const DATA_URL	= 'http://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv';

	const TIMEZONE				= 'Asia/Tokyo';
	
	const YEAR_TEXT_FORMAT		= "/^(((平成)(\d{1,2})年)((\d{4})年)),?$/";
	
	const FROM_ENCODING			= 'SJIS-win';
	const TO_ENCODING			= 'UTF-8';

	const SKIP_ROW_KEYWORD		= '名称';

	protected static $dateTimeZone = null;

	public static function buildera ($year, $fullText = null, $eraYear = null, $era = null, $eraName = null) {
		return new class ($year, $fullText, $eraYear, $era, $eraName) {
			private $data = [];

			public function __construct ($year, $fullText = null, $eraYear = null, $era = null, $eraName = null) {
				$this->data = [
					'year'		=> $year,
					'fullText'	=> $fullText,
					'eraYear'	=> $eraYear,
					'era'		=> $era,
					'eraName'	=> $eraName,
				];
			}

			public function __call ($method, $args) {
				return $this->data[$method] ?? null;
			}
		};
	}

	public static function buildHoliday (string $time, \DateTimeZone $timezone = null) {
		$timezone = $timezone ?? (static::$dateTimeZone ?? static::$dateTimeZone = new \DateTimeZone(static::TIMEZONE));
		return new class ($time, $timezone) extends \DateTime {
			protected $eraInfo		= null;
			protected $holidayName	= '';

			public function setEraInfo ($era_info) {
				$this->eraInfo = $era_info;
				return $this;
			}

			public function getEraInfo () {
				return $this->eraInfo;
			}
			
			public function setHolidayName ($holiday_name) {
				$this->holidayName = $holiday_name;
				return $this;
			}
			
			public function getHolidayName () {
				return $holidayName;
			}
		};
	}

	public static function update ($master_file_path, $options = []) {
		$is_win = substr(strtoupper(PHP_OS), 0 ,3) === 'WIN';
		$temp_dir = $options['temp_dir'] ?? ($is_win ? getenv('TEMP') : '/var/tmp');

		$allow_url_fopen = ini_get('allow_url_fopen');
		switch (true) {
			case $allow_url_fopen === '1':
			case $allow_url_fopen === true:
				break;
			default:
				throw new \Exception('allow_url_fopenが有効になっていません。php.iniまたはhttpd.confでallow_url_fopenを有効にしてください。');
		}

		$proxy_host = $options['proxy_host'] ?: null;
		$proxy_port = $options['proxy_port'] ?: null;
		if ($proxy_host !== null) {
			if (parse_url($proxy_host, \PHP_URL_SCHEME) === 'https') {
				if (extension_loaded('openssl') === false) {
					throw new \Exception('proxy hostにhttpsが使用されていますが、openssl拡張が有効になっていません。');
				}
			}
		}

		$context_config = [
			'http'	=> [
				'ignore_errors' => true,
			],
			'ssl'	=> [
				'verify_peer'		=> false,
				'verify_peer_name'	=> false,
			],
		];

		if ($proxy_host !== null) {
			$context_config['http']['proxy'] = vsprintf(
				$proxy_host !== null ? 'tcp://%s:%s' : 'tcp://%s',
				array_filter([$proxy_host, $proxy_port])
			);
			$context_config['http']['request_fulluri'] = true;
		}

		$stream_context = stream_context_create($context_config);

		file_put_contents($master_file_path, file_get_contents(static::DATA_URL, false, $stream_context));
	}

	public static function factory ($master_file_path, $options = []) {
		$class_path = get_called_class();
		$instance = new $class_path();
		$instance->init($master_file_path, $options);
		return $instance;
	}

	public function init ($master_file_path, $options = []) {
		if (!file_exists($master_file_path)) {
			static::update($master_file_path, $options);
		}

		$holiday_csv = file_get_contents($master_file_path);

		if (static::TO_ENCODING !== static::FROM_ENCODING) {
			$holiday_csv = mb_convert_encoding($holiday_csv, static::TO_ENCODING, static::FROM_ENCODING);
		}

		$fp = fopen('php://memory', 'r+');
		fwrite($fp, $holiday_csv);
		rewind($fp);
		$current_locale = setlocale(LC_ALL, '0');
		setlocale(LC_ALL, 'ja_JP.UTF-8');

		$header = [];
		foreach (fgetcsv($fp, 0) as $col) {
			preg_match(static::YEAR_TEXT_FORMAT, $col, $mat);
			$header[]	= static::buildera($mat[5], $mat[1], $mat[4], $mat[2], $mat[3]);
			$year_map[]	= $mat[5];
		}

		$list = [];
		while (($data = fgetcsv($fp, 0)) !== FALSE) {
			if ($data[0] === '' && empty(array_filter($data))) {
				break;
			}

			if ($data[0] === static::SKIP_ROW_KEYWORD) {
				continue;
			}

			foreach (array_chunk($data, 2) as $idx => $chunk) {
				$holiday = static::buildHoliday($chunk[1])->setHolidayName($chunk[0])->setEraInfo($header[$idx]);
				$list[$year_map[$idx]][$holiday->format('Y-m-d')] = $holiday;
			}
		}

		setlocale(LC_ALL, $current_locale);
		fclose($fp);

		$this->list = $list;
	}

	public function list ($year = null) {
		return $this->list[$year ?? date('Y')] ?? null;
	}

	public function isHoliday ($datetime) {
		$timestamp = is_int($datetime) ? $datetime : strtotime($datetime);
		return isset($this->list[date('Y', $timestamp)][date('Y-m-d', $timestamp)]);
	}

	public function getHoliday ($datetime) {
		$timestamp = is_int($datetime) ? $datetime : strtotime($datetime);
		return $this->list[date('Y', $timestamp)][date('Y-m-d', $timestamp)] ?? null;
	}
}
index.php
<?php

$master_file_path = './syukujitsu.csv';

$jnh = JapanNationalHoliday::factory($master_file_path);

var_dump($jnh->isHoliday('2017-08-11'));
var_dump($jnh->getHoliday('2017-08-11'));
6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?