/** * Read and process file, read dependencies if exist. * * @param[in] $filename Name of the file to parse and interpet. * @param[out] $extendedFiles List of files additionaly loaded via `extends_file` directive. */ public static function parseFile($filename, &$extendedFiles = null) { $schema = static::annotateSchema(\Smalldb\StateMachine\Utils::parse_json_file($filename), false); $extends_file = isset($schema['extends_file']) ? dirname($filename) . '/' . $schema['extends_file'] : null; while ($extends_file !== null) { if ($extendedFiles !== null) { $extendedFiles[] = $extends_file; } $part = static::annotateSchema(\Smalldb\StateMachine\Utils::parse_json_file($extends_file), str_replace('.schema.json', '', basename($extends_file))); $extends_file = isset($part['extends_file']) ? dirname($extends_file) . '/' . $part['extends_file'] : null; $schema = static::extendSchema($part, $schema); } if (isset($schema['extends_file'])) { unset($schema['extends_file']); } return $schema; }
/** * Constructor compatible with cascade resource factory. */ public function __construct($options, $context = null, $alias) { parent::__construct($options, $context, $alias); // Get base dir (constants are available) $this->base_dir = rtrim(Utils::filename_format($options['base_dir'], array()), '/') . '/'; // Load machine definitions from APC cache if (!empty($options['cache_disabled'])) { $cache_loaded = false; $cache_mtime = 0; $cache_disabled = true; } else { $cache_disabled = false; $cache_key = __CLASS__ . ':' . $alias . ':' . $this->base_dir; $cache_data = apc_fetch($cache_key, $cache_loaded); if ($cache_loaded) { list($this->machine_type_table, $cache_mtime) = $cache_data; //debug_dump($this->machine_type_table, 'Cached @ '.strftime('%F %T', $cache_mtime)); } } // Scan base dir for machines and find youngest file $dh = opendir($this->base_dir); if (!$dh) { throw new RuntimeException('Cannot open base dir: ' . $this->base_dir); } $youngest_mtime = filemtime(__FILE__); // Make sure cache gets regenerated when this file is changed $file_list = array(); while (($file = readdir($dh)) !== false) { if ($file[0] != '.') { $file_list[] = $file; } $mtime = filemtime($this->base_dir . $file); if ($youngest_mtime < $mtime) { $youngest_mtime = $mtime; } } closedir($dh); // Load data if cache is obsolete if (!$cache_loaded || $youngest_mtime >= $cache_mtime) { //debug_msg('Machine type table cache miss. Reloading...'); $this->machine_type_table = array(); // Find all machine types foreach ($file_list as $file) { @(list($machine_type, $ext) = explode('.', $file, 2)); switch ($ext) { case 'json': case 'json.php': $filename = $this->base_dir . $file; $this->machine_type_table[$machine_type] = JsonReader::loadString(file_get_contents($filename), [], $filename); break; } } ksort($this->machine_type_table); // Load all included files foreach ($this->machine_type_table as $machine_type => $json_config) { $graphml_reader_used = false; $bpmn_reader_used = false; $machine_def =& $this->machine_type_table[$machine_type]; foreach ((array) @$json_config['include'] as $include) { // String is filename if (!is_array($include)) { $include_file = $include; $include_opts = array(); } else { $include_file = $include['file']; $include_opts = $include; } // Relative path is relative to base directory if ($include_file[0] != '/') { $include_file = $this->base_dir . $include_file; } // Detect file type and use proper loader // FIXME: Replace this with extendible loading infrastructure. if (preg_match('/\\.json\\(\\.php\\)\\?$/i', $include_file)) { // Include JSON file (simple merge) $machine_def = array_replace_recursive(JsonReader::loadString(file_get_contents($include_file), $include_opts, $include_file), $machine_def); } else { if (preg_match('/\\.graphml$/i', $include_file)) { // Include GraphML file (find states and transitions, then simply add them to the definition) $machine_def = array_replace_recursive(GraphMLReader::loadString(file_get_contents($include_file), $include_opts, $include_file), $machine_def); $graphml_reader_used = true; } else { if (preg_match('/\\.bpmn$/i', $include_file)) { // Load relevant fragment of BPMN diagram, it will be merged with other fragments and added to the definition later. $machine_def = array_replace_recursive(BpmnReader::loadString(file_get_contents($include_file), $include_opts, $include_file), $machine_def); $bpmn_reader_used = true; } else { throw new RuntimeException('Unknown file format: ' . $include_file); } } } } if ($graphml_reader_used) { GraphMLReader::postprocessDefinition($machine_def); } if ($bpmn_reader_used) { BpmnReader::postprocessDefinition($machine_def); } JsonReader::postprocessDefinition($machine_def); unset($machine_def); } if (!$cache_disabled) { apc_store($cache_key, array($this->machine_type_table, time())); } } }