fix view 835 html report in edihistory
[openemr.git] / library / edihistory / edih_x12file_class.php
blob4ea7fcd34a817970d0d66f5a21168b5a812370b4
1 <?php
2 /*
3 * edih_x12file_class.php
5 * Copyright 2014 Kevin McCormick Longview, Texas
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; version 3 or later. You should have
16 * received a copy of the GNU General Public License along with this program;
17 * if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * <http://opensource.org/licenses/gpl-license.php>
22 * @link: http://www.open-emr.org
23 * @author: Kevin McCormick
24 * @package: OpenEMR
25 * @subpackage: ediHistory
28 /* ********* project notes =================
29 * determine GET and POST array elements
30 * process new files -- type and csv data values
31 * display tables -- links with GET and POST
32 * find files -- find transactions
33 * format display
35 * ==========================================
38 /*********** php code here ****************************************************************/
40 /**
41 * Class to read EDI X12 files in healthcare setting
43 * It is assumed that EDI X12 files will have mime-type text/plain; charset=us-ascii
45 * initialize with file path or as empty object, e.g.
46 * $x12_file = new edih_x12_file(filepath); segment array and envelope array, no file text
47 * $x12_file = new edih_x12_file(filepath, false); no segment or envelope array, no file text
48 * $x12_file = new edih_x12_file(filepath, false, true); no segment or envelope array, yes file text
49 * or
50 * $x12_file = new edih_x12_file(); empty object, ' _x12_ ' methods available if file text supplied as method argument
52 * The properties filename, type, version, valid, isx12, hasGS, hasST, and delimiters should be available
53 * if the valid filepath is provided when creating the object.
55 * @param string $filepath default = ''
56 * @param bool $mk_segs default = true
57 * @param bool $text default = false
58 * @return bool|string true for empty object "ovgis" for validated x12
60 class edih_x12_file {
61 // properties
62 private $filepath = '';
63 private $filename = '';
64 private $type = '';
65 private $version = '';
66 private $text = '';
67 private $length = 0;
68 private $valid = false;
69 private $isx12 = false;
70 private $hasGS = false;
71 private $hasST = false;
72 private $message = array();
73 private $delimiters = array();
74 private $segments = array();
75 private $envelopes = array();
77 private $constructing = false;
79 private $gstype_ar = array('HB'=>'271', 'HS'=>'270', 'HR'=>'276', 'HN'=>'277',
80 'HI'=>'278', 'HP'=>'835', 'FA'=>'999', 'HC'=>'837');
82 function __construct($file_path='', $mk_segs=true, $text=false ) {
84 if ($file_path === '') { return true; }
86 if ( is_file($file_path) && is_readable($file_path) ) {
87 $this->filepath = trim($file_path);
88 $this->filename = basename($this->filepath);
89 $f_text = file_get_contents($this->filepath);
91 $testval = ($f_text) ? $this->edih_x12_scan($f_text) : '';
92 $this->valid = ( strpos($testval, 'v') ) ? true : false;
93 $this->isx12 = ( strpos($testval, 'i') ) ? true : false;
94 $this->hasGS = ( strpos($testval, 'g') ) ? true : false;
95 $this->hasST = ( strpos($testval, 's') ) ? true : false;
97 if ($this->valid) {
98 $this->constructing = true;
99 $this->text = ($text) ? $f_text : '';
100 $this->length = ($f_text) ? strlen($f_text) : 0;
101 if ($this->isx12) {
102 $this->delimiters = $this->edih_x12_delimiters( substr($f_text, 0, 126) );
103 $this->version = substr($f_text, 84, 5);
104 if ($mk_segs) {
105 $this->segments = $this->edih_x12_segments($f_text);
106 if ( is_array($this->segments) && count($this->segments) ) {
107 $this->envelopes = $this->edih_x12_envelopes();
108 $this->type = $this->edih_x12_type();
109 } else {
110 $this->message[] = 'edih_x12_file: error in creating segment array ' . $this->filename . PHP_EOL;
112 } else {
113 // read file contents to try and determine x12 type
114 $this->type = $this->edih_x12_type($f_text);
118 } else {
119 // invalid file path
120 $this->message[] = 'edih_x12_file: invalid file path ' . $file_path;
122 $this->constructing = false;
123 return $this->valid;
127 * function to support empty object and '_x12_' functions called with supplied file text
129 * @param string $file_text
130 * @param bool return x12 type
131 * @param bool return delimiters
132 * @param bool return segments
133 * @return array array['filetext'] and maybe ['type'] ['$delimiters'] ['segments']
135 private function edih_file_text($file_text, $type=false, $delimiters=false, $segments=false) {
137 $ret_ar = array();
138 if ( !$file_text || is_string($file_text) == false ) {
139 $this->message[] = 'edih_file_text(): invalid argument';
140 return $ret_ar;
142 // do verifications
143 $v = $this->edih_x12_scan($file_text);
144 if ( !strpos($v, 's') ) {
145 $this->message[] = 'edih_file_text(): failed scan of file text ('.$v.')';
146 return $ret_ar;
149 $this->constructing = true;
151 if ($type) { $ret_ar['type'] = $this->edih_x12_type($file_text); }
152 if ($delimiters) { $ret_ar['delimiters'] = $this->edih_x12_delimiters( substr($file_text, 0, 126) ); }
153 if ($segments) { $ret_ar['segments'] = $this->edih_x12_segments($file_text); }
155 $this->constructing = false;
157 return $ret_ar;
161 * functions to return properties
163 public function classname() { return get_class($this); }
164 public function edih_filepath() { return $this->filepath; }
165 public function edih_filename() { return $this->filename; }
166 public function edih_type() { return $this->type; }
167 public function edih_version() { return $this->version; }
168 public function edih_text() { return $this->text; }
169 public function edih_length() { return $this->length; }
170 public function edih_valid() { return $this->valid; }
171 public function edih_isx12() { return $this->isx12; }
172 public function edih_hasGS() { return $this->hasGS; }
173 public function edih_hasST() { return $this->hasST; }
174 public function edih_delimiters() { return $this->delimiters; }
175 public function edih_segments() { return $this->segments; }
176 public function edih_envelopes() { return $this->envelopes; }
179 * message statements regarding object or from functions
180 * formatted as html
182 * @return string
184 public function edih_message() {
185 $str_html = '<p>' . PHP_EOL;
186 if ( count($this->message) ) {
187 foreach($this->message as $msg) {
188 $str_html .= $msg . '<br />' . PHP_EOL;
190 $str_html .= PHP_EOL . '</p>' . PHP_EOL;
191 } else {
192 $str_html = '';
194 return $str_html;
199 * Numeric type of x12 HC file associated with GS01 code
201 * @param string $gs01
202 * @return string|bool
204 public function edih_gs_type($gs01) {
205 $tpky = strtoupper($gs01);
206 return ( isset($this->gstype_ar[$tpky]) ) ? $this->gstype_ar[$tpky] : false;
210 * Use PHP FileInfo to check mime type and then scan for unwanted characters
211 * check for Non-basic ASCII character and <%, <asp:, <?, ${, #!, <scr (any other evil script indicators?)
212 * basically allows A-Z a-z 0-9 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and newline carriage_return
213 * This function accepts the following mime-type: text/plain; charset=us-ascii
215 * The return string can be 'ovigs' ov - valid, igs - ISA GS ST
217 * @param string $filetext the file contents
218 * @return string zero length on failure
220 public function edih_x12_scan($filetext) {
221 $hasval = '';
222 $ftxt = ( $filetext && is_string($filetext) ) ? trim($filetext) : $filetext;
223 // possibly $ftxt = trim($filetext, "\x00..\x1F") to remove ASCII control characters
224 // remove newlines
225 if (strpos($ftxt, PHP_EOL)) { $ftxt = str_replace(PHP_EOL, '', $ftxt); }
226 $flen = ( $ftxt && is_string($ftxt) ) ? strlen($ftxt) : 0;
227 if ( !$flen ) {
228 $this->message[] = 'edih_x12_scan: zero length or invalid file text';
229 return $hasval;
231 $de = '';
232 $dt = '';
233 // use finfo php class
234 if ( class_exists('finfo') ) {
235 $finfo = new finfo(FILEINFO_MIME);
236 $mimeinfo = $finfo->buffer($ftxt);
237 if ( strncmp($mimeinfo, 'text/plain; charset=us-ascii', 28) !== 0 ) {
238 $this->message[] = 'edih_x12_scan: '.$this->filename.' : invalid mime info: <br />'.$mimeinfo;
240 return $hasval;
244 if (preg_match('/[^\x20-\x7E\x0A\x0D]|(<\?)|(<%)|(<asp)|(#!)|(\$\{)|(<scr)|(script:)/is', $ftxt, $matches, PREG_OFFSET_CAPTURE)) {
246 $this->message[] = 'edih_x12_scan: suspect characters in file '.$this->filename.'<br />'.
247 ' character: '.$matches[0][0].' position: '. $matches[0][1];
249 return $hasval;
251 $hasval = 'ov'; // valid
252 // check for required segments ISA GS ST; assume segment terminator is last character
253 if (substr($ftxt, 0, 3) === 'ISA') {
254 $hasval = 'ovi';
255 $de = substr($ftxt, 3, 1);
256 $dt = substr($ftxt, -1);
257 if ( strpos($ftxt, $dt.'GS'.$de, 0) ) { $hasval = 'ovig'; }
258 if ( strpos($ftxt, $dt.'ST'.$de, 0) ) { $hasval = 'ovigs'; }
260 return $hasval;
264 * read the GS segments in file contents to determine x12 type, or, if the
265 * object was created with a file path and envelopes, from the GS envelope array
267 * @param string $file_text optional contents of an x12 file
268 * @return string the x12 type, e.g. 837, 835, 277, 999, etc.
270 public function edih_x12_type($file_text='') {
271 $tpstr = '';
272 $tp_tmp = array();
273 $f_text = '';
274 $delims = array();
275 $delimarg = '';
276 $dt = ( isset($this->delimiters['t']) ) ? $this->delimiters['t'] : '';
277 $de = ( isset($this->delimiters['e']) ) ? $this->delimiters['e'] : '';
279 if ($file_text) {
280 // For when '_x12_' function is called with file contents as argument
281 if (!$this->constructing) {
282 $vars = $this->edih_file_text($file_text, false, true, false);
283 $f_text = $file_text;
284 $dt = ( isset($vars['delimiters']['t']) ) ? $vars['delimiters']['t'] : '';
285 $de = ( isset($vars['delimiters']['e']) ) ? $vars['delimiters']['e'] : '';
286 } elseif ( $this->text ) {
287 // called in initial construction, delimiters already created if x12 file
288 $f_text =& $this->text;
289 if ( !$dt ) {
290 $this->message[] = 'edih_x12_type: not x12 file';
291 return $tpstr;
293 } else {
294 // called after file scan, but no segment array exists
295 $f_text =& $file_text;
296 if ( !$dt ) {
297 $delims = $this->edih_x12_delimiters( substr($f_text, 0, 126) );
298 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
299 $de = ( isset($delims['e']) ) ? $delims['e'] : '';
302 if (!$f_text) {
303 $this->message[] = 'edih_x12_type: failed scan of file content';
304 return $tpstr;
306 } elseif ( isset($this->envelopes['GS']) ) {
307 // No argument, so if envelopes exist, take values from there
308 foreach($this->envelopes['GS'] as $gs) {
309 $tp_tmp[] = $gs['type'];
311 } elseif ( count($this->segments) ) {
312 // No argument and no envelopes, so scan segments
313 if (!$de) { $de = substr(reset($this->segments), 3, 1); }
314 foreach( $this->segments as $seg ) {
315 if ( strncmp($seg, 'GS'.$de, 3) == 0) {
316 $gs_ar = explode($de, $seg);
317 if ( array_key_exists($gs_ar[1], $this->gstype_ar) ) {
318 //$tp_tmp[] = $this->gstype_ar[$gs_ar[1]];
319 $tp_tmp[] = $gs_ar[1];
320 } else {
321 $tp_tmp[] = $gs_ar[1];
322 $this->message[] = 'edih_x12_type: unknown x12 type '.$gs_ar[1];
326 } else {
327 $this->message[] = 'edih_x12_type: no content to determine x12 type';
328 return $tpstr;
330 // $f_text has content only if file contents supplied or in text property
331 if ( $f_text ) {
332 // use regular expression instead of strpos($f_text, $dt.'GS'.$de)
333 $pcrepattern = '/GS\\'.$de.'(?:HB|HS|HR|HI|HN|HP|FA|HC)\\'.$de.'/';
334 $pr = preg_match_all($pcrepattern, $f_text, $matches, PREG_OFFSET_CAPTURE);
336 if ($pr && count($matches)) {
337 foreach($matches as $m) {
338 //$gspos1 = $m[0][1];
339 $gs_ar1 = explode($de, $m[0][0]);
340 if ( array_key_exists($gs_ar1[1], $this->gstype_ar) ) {
341 //$tp_tmp[] = $this->gstype_ar[$gs_ar1[1]];
342 $tp_tmp[] = $gs_ar1[1];
343 } else {
344 $tp_tmp[] = $gs_ar1[1];
345 $this->message[] = 'edih_x12_type: unknown x12 type '.$gs_ar1[1];
348 } else {
349 $this->message[] = 'edih_x12_type: did not find GS segment ';
351 /* **** this replaced by preg_match_all() above ******
353 // scan GS segments
354 $gs_str = $dt.'GS'.$de;
355 $gs_pos = 1;
356 $gse_pos = 2;
357 while ($gs_pos) {
358 $gs_pos = strpos($f_text, $gs_str, $gs_pos);
359 if ($gs_pos) {
360 $gsterm = strpos($f_text, $dt, $gs_pos+1);
361 $gsseg = trim(substr($f_text, $gs_pos+1, $gsterm-$gs_pos-1));
362 //$gs_ar = explode($de, substr($f_text, $gs_pos+1, $gsterm-$gs_pos-1) );
363 $this->message[] = 'edih_x12_type: '.$gsseg.PHP_EOL;
364 $gs_ar = explode($de, $gsseg);
365 if ( array_key_exists($gs_ar[1], $this->gstype_ar) ) {
366 $tp_tmp[] = $this->gstype_ar[$gs_ar[1]];
367 } else {
368 $tp_tmp[] = $gs_ar[1];
369 $this->message[] = 'edih_x12_type: unknown x12 type '.$gs_ar[1];
371 $gs_pos = $gsterm + 1;
374 ******************* */
376 // x12 type information collected
377 if ( count($tp_tmp) ) {
378 $tp3 = array_values( array_unique($tp_tmp) );
379 // mixed should not happen -- concatenated ISA envelopes of different types?
380 $tpstr = ( count($tp3) > 1 ) ? 'mixed|' . implode("|", $tp3) : $tp3[0];
381 //$this->message[] = 'edih_x12_type: ' . $tpstr;
382 } else {
383 $this->message[] = 'edih_x12_type: error in identifying type ';
384 return false;
386 return $tpstr;
391 * Extract x12 delimiters from the ISA segment
393 * There are obviously easier/faster ways of doing this, but we go character by character.
394 * The value returned is empty on error, otherwise:
395 * <pre>
396 * array('t'=>segment terminator, 'e'=>element delimiter,
397 * 's'=>sub-element delimiter, 'r'=>repetition delimiter)
398 * </pre>
400 * @param string $isa_str110 first n>=106 characters of x12 file
401 * @return array array or empty on error
403 public function edih_x12_delimiters($isa_str110='') {
405 $delim_ar = array();
406 if (!$isa_str110 && $this->text) {
407 $isa_str = substr($this->text, 0, 106);
408 } else {
409 $isa_str = trim($isa_str110);
411 $isalen = strlen($isa_str);
412 if ($isalen >= 106) {
413 if (substr($isa_str, 0, 3) != 'ISA') {
414 // not the starting characters
415 $this->message[] = 'edih_x12_delimiters: text does not begin with ISA';
416 return $delim_ar;
418 /* Extract delimiters using the prescribed positions.
419 * -- problem is possibly mangled files
420 * $t_ar['e'] = substr($isa_str, 3, 1);
421 * $t_ar['r'] = substr($isa_str, 82, 1);
422 * $t_ar['s'] = substr($isa_str, 104, 1);
423 * $t_ar['t'] = substr($isa_str, 105, 1);
426 } else {
427 $this->message[] = 'edih_x12_delimiters: ISA string too short'.PHP_EOL;
428 return $delim_ar;
430 $s = '';
431 $delim_ct = 0;
432 $de = substr($isa_str, 3, 1); // ISA*
433 for ($i = 0; $i < $isalen; $i++) {
434 if ($isa_str[$i] == $de) {
435 // element count incremented at end of loop
436 // repetition separator in version 5010
437 if ($delim_ct == 11) { $dr = substr($s, 1, 1); }
438 if ($delim_ct == 12) {
439 if ( strpos($s, '501') === false ) { $dr = ''; }
442 if ($delim_ct == 15) {
443 $ds = substr($isa_str, $i+1, 1);
444 $dt = substr($isa_str, $i+2, 1);
446 if ($delim_ct == 16) { break; }
447 $s = $isa_str[$i]; // $elem_delim;
448 $delim_ct++;
449 } else {
450 $s .= $isa_str[$i];
453 // there are 16 elements in ISA segment
454 if ($delim_ct < 16) {
455 // too few elements -- probably did not get delimiters
456 $this->message[] = "edih_x12_delimiters: too few elements in ISA string";
457 return $delim_ar;
460 $delim_ar = array('t'=>$dt, 'e'=>$de, 's'=>$ds, 'r'=>$dr);
462 return $delim_ar;
466 * Create a multidimensional array of edi envelope info from object segments.
467 * Useful for slicing and dicing. The ['ST'][$stky]['trace'] value is used only for 835
468 * or 999 type files and the ['ST'][$stky]['acct'][i] array will have multiple values
469 * likely only for 835, 271, and 277 types, because response from a payer will have
470 * multiple transactions in the ST-SE envelope while OpenEMR probably will place each
471 * transaction in its own ST-SE envelope for 270 and 837 types.
473 * The ['start'] and ['count'] values are for use in php function array_slice()
474 * The numeric keys of the segments array begin at 1 and the ['start'] value is one less
475 * than the actual key because array_slice() offset is zero-based.
477 * <pre>
478 * ['ISA'][$icn]=>['start']['count']['sender']['receiver']['icn']['gscount']['date']
479 * ['GS'][$gs_ct]=>['start']['count']['gsn']['icn']['sender']['date']['stcount']['type']
480 * ['ST'][$stky]=>['start']['count']['stn']['gsn']['icn']['type']['trace']['acct']
481 * ['ST'][$stky]['acct'][i]=>CLM01
482 * ['ST'][$stky]['bht03'][i]=>BHT03
483 * </pre>
485 * @return array array as shown above or empty on error
487 public function edih_x12_envelopes($file_text='') {
488 // produce an array of envelopes and positions
489 $env_ar = array();
490 $de = '';
491 if ($file_text) {
492 // presume need for file scan and delimiters
493 $vars = $this->edih_file_text($file_text, false, true, true);
494 $segment_ar = (isset($vars['segments']) ) ? $vars['segments'] : array();
495 $de = (isset($vars['delimiters']) ) ? $vars['delimiters']['e'] : '';
496 //$segment_ar = $this->edih_x12_segments($file_text);
497 if ( empty($segment_ar) || !$de ) {
498 $this->message[] = 'edih_x12_envelopes: invalid file text';
499 return $env_ar;
501 } elseif ( count($this->segments) ) {
502 $segment_ar = $this->segments;
503 if (isset($this->delimiters['e'])) {
504 $de = $this->delimiters['e'];
505 } else {
506 $de = (substr(reset($segment_ar), 0, 3) == 'ISA') ? substr(reset($segment_ar), 3, 1) : '';
508 } else {
509 $this->message[] = 'edih_x12_envelopes: no text or segments';
510 return $env_ar;
512 if (!$de) {
513 $this->message[] = 'edih_x12_envelopes: invalid delimiters';
514 return $env_ar;
517 // get the segment array bounds
518 $seg_first = (reset($segment_ar) !== false) ? key($segment_ar) : '1';
519 $seg_last = (end($segment_ar) !== false) ? key($segment_ar) : count($segment_ar) + $seg_first;
520 if (reset($segment_ar) === false) {
521 $this->message[] = 'edi_x12_envelopes: reset() error in segment array';
522 return $env_ar;
523 } else {
524 $seg_ct = $seg_last + 1;
526 // variables
527 $seg_txt = '';
528 $sn = '';
529 $st_type = '';
530 $st_ct = 0;
531 $isa_ct = 0;
532 $iea_ct = 0;
533 $gs_st_ct = 0;
534 $trnset_seg_ct = 0;
535 $st_segs_ct = 0;
536 $isa_segs_ct = 0;
537 $chk_trn = false;
538 $trncd = '2';
539 //$id278 = false;
540 $ta1_icn = '';
541 $seg_ar = array();
542 // the segment IDs we look for
543 $chk_segs = array('ISA', 'GS'.$de, 'TA1', 'ST'.$de, 'BHT', 'HL'.$de, 'TRN', 'CLP', 'CLM', 'SE'.$de, 'GE'.$de, 'IEA');
545 for ($i=$seg_first; $i<$seg_ct; $i++) {
546 // counters
547 $isa_segs_ct++;
548 $st_segs_ct++;
550 $seg_text = $segment_ar[$i];
551 $sn = substr($seg_text, 0, 4);
552 // skip over segments that are not envelope boundaries or identifiers
553 if ( !in_array(substr($sn, 0, 3), $chk_segs) ) { continue; }
554 // create the structure array
555 if ( strncmp($sn, 'ISA'.$de, 4) == 0 ) {
556 $seg_ar = explode($de, $seg_text);
557 $icn = trim($seg_ar[13]);
559 $env_ar['ISA'][$icn]['start'] = strval($i-1);
560 $env_ar['ISA'][$icn]['sender'] = trim($seg_ar[6]);
561 $env_ar['ISA'][$icn]['receiver'] = trim($seg_ar[8]);
562 $env_ar['ISA'][$icn]['icn'] = $icn;
563 $env_ar['ISA'][$icn]['date'] = trim($seg_ar[9]); // YYMMDD
564 $env_ar['ISA'][$icn]['version'] = trim($seg_ar[12]);
566 $isa_segs_ct = 1;
567 $isa_ct++;
568 continue;
571 if ( strncmp($sn, 'GS'.$de, 3) == 0) {
572 $seg_ar = explode($de, $seg_text);
573 $gs_start = strval($i-1);
574 $gsn = $seg_ar[6];
575 // GS06 could be used to id 997/999 response, if truly unique
576 // cannot index on $gsn due to concatenated ISA envelopes and non-unique
577 $gs_ct = isset($env_ar['GS']) ? count($env_ar['GS']) : 0;
579 $env_ar['GS'][$gs_ct]['start'] = $gs_start;
580 $env_ar['GS'][$gs_ct]['gsn'] = $gsn;
581 $env_ar['GS'][$gs_ct]['icn'] = $icn;
582 $env_ar['GS'][$gs_ct]['sender'] = trim($seg_ar[2]);
583 $env_ar['GS'][$gs_ct]['date'] = trim($seg_ar[4]);
584 $env_ar['GS'][$gs_ct]['srcid'] = '';
585 // to verify type of edi transaction
586 if ( array_key_exists($seg_ar[1], $this->gstype_ar) ) {
587 $gs_fid = $this->gstype_ar[$seg_ar[1]];
588 $env_ar['GS'][$gs_ct]['type'] = $seg_ar[1];
589 } else {
590 $gs_fid = 'NA';
591 $env_ar['GS'][$gs_ct]['type'] = 'NA';
592 $this->message[] = 'edih_x12_envelopes: Unknown GS type '.$seg_ar[1];
594 continue;
596 // expect 999 TA1 before ST
597 if ( strncmp($sn, 'TA1'.$de, 4) == 0) {
598 $seg_ar = explode($de, $seg_text);
599 if ( isset($seg_ar[1]) && $seg_ar[1] ) {
600 $ta1_icn = $seg_ar[1];
601 } else {
602 $this->message[] = 'edih_x12_envelopes: Error in TA1 segment response ICN';
604 //TA1*ISA13ICN*ISA09DATE*ISA10TIME*ACKCode*NoteCode~
605 continue;
608 if ( strncmp($sn, 'ST'.$de, 3) == 0 ) {
609 $seg_ar = explode($de, $seg_text);
610 $stn = $seg_ar[2];
611 $st_type = $seg_ar[1];
612 $st_start = strval($i);
613 $st_segs_ct = 1;
614 $st_ct = isset($env_ar['ST']) ? count($env_ar['ST']) : 0;
616 $env_ar['ST'][$st_ct]['start'] = strval($i-1);
617 $env_ar['ST'][$st_ct]['count'] = '';
618 $env_ar['ST'][$st_ct]['stn'] = $seg_ar[2];
619 $env_ar['ST'][$st_ct]['gsn'] = $gsn;
620 $env_ar['ST'][$st_ct]['icn'] = $icn;
621 $env_ar['ST'][$st_ct]['type'] = $seg_ar[1];
622 $env_ar['ST'][$st_ct]['trace'] = '0';
623 $env_ar['ST'][$st_ct]['acct'] = array();
624 $env_ar['ST'][$st_ct]['bht03'] = array();
625 // GS file id FA can be 999 or 997
626 if ( $gs_fid != $st_type && strpos($st_type, '99') === false ) {
627 $this->message[] = "edih_x12_envelopes: ISA $icn, GS $gsn $gs_fid ST $stn $st_type type mismatch".PHP_EOL;
630 continue;
633 if ( strpos('|270|271|276|277|278', $st_type) ) {
635 if ( strncmp($sn, 'BHT'.$de, 4) == 0) {
636 $seg_ar = explode($de, $seg_text);
637 if ( isset($seg_ar[2]) ) {
638 $trncd = ($seg_ar[2] == '13') ? '1' : '2';
639 // 13 = request, otherwise assume response
640 } else {
641 $this->message[] = 'edih_x12_envelopes: missing BHT02 type element';
643 if ( isset($seg_ar[3]) && $seg_ar[3]) {
644 $env_ar['ST'][$st_ct]['bht03'][] = $seg_ar[3];
645 } else {
646 $this->message[] = 'edih_x12_envelopes: missing BHT03 identifier';
649 if (strncmp($sn, 'HL'.$de, 3) == 0 ) {
650 $seg_ar = explode($de, $seg_text);
651 if (isset($seg_ar[3]) && $seg_ar[3]) {
652 $chk_trn = ( strpos('|22|23|PT', $seg_ar[3]) ) ? true : false;
653 } else {
654 $this->message[] = 'edih_x12_envelopes: missing HL03 level element';
656 continue;
658 if ($chk_trn && strncmp($sn, 'TRN'.$de, 4) == 0) {
659 $seg_ar = explode($de, $seg_text);
660 if (isset($seg_ar[1]) && $seg_ar[1] == $trncd) {
661 $env_ar['ST'][$st_ct]['acct'][] = (isset($seg_ar[2])) ? $seg_ar[2] : '';
662 $chk_trn = false;
663 } else {
664 $this->message[] = 'edih_x12_envelopes: missing TRN02 type identifier element';
666 continue;
671 if ($st_type == '835') {
672 if ( strncmp($sn, 'TRN'.$de, 4) == 0) {
673 $seg_ar = explode($de, $seg_text);
674 if (!isset($seg_ar[2]) || !isset($seg_ar[3]) ) {
675 $this->message[] = 'error in 835 TRN segment '.$seg_text;
677 $env_ar['ST'][$st_ct]['trace'] = (isset($seg_ar[2])) ? $seg_ar[2] : "";
678 // to match OpenEMR billing parse file name
679 if (isset($seg_ar[4])) {
680 $env_ar['GS'][$gs_ct]['srcid'] = $seg_ar[4];
681 } else {
682 $env_ar['GS'][$gs_ct]['srcid'] = (isset($seg_ar[3])) ? $seg_ar[3] : "";
685 continue;
687 if ( strncmp($sn, 'CLP'.$de, 4) == 0) {
688 $seg_ar = explode($de, $seg_text);
689 if ( isset($seg_ar[1]) ) {
690 $env_ar['ST'][$st_ct]['acct'][] = $seg_ar[1];
691 } else {
692 $this->message[] = 'error in 835 CLP segment '.$seg_text;
694 continue;
698 if ($st_type == '837') {
699 if ( strncmp($sn, 'BHT'.$de, 4) == 0) {
700 $seg_ar = explode($de, $seg_text);
701 if ( isset($seg_ar[3]) && $seg_ar[3]) {
702 $env_ar['ST'][$st_ct]['bht'][] = $seg_ar[3];
703 } else {
704 $this->message[] = 'edih_x12_envelopes: missing BHT03 identifier';
708 if ( strncmp($sn, 'CLM'.$de, 4) == 0) {
709 $seg_ar = explode($de, $seg_text);
710 if ( isset($seg_ar[1]) ) {
711 $env_ar['ST'][$st_ct]['acct'][] = $seg_ar[1];
712 } else {
713 $this->message[] = 'error in 837 CLM segment '.$seg_text;
715 continue;
719 if ( strncmp($sn, 'SE'.$de, 3) == 0 ) {
720 // make sure no lingering toggle
721 $id278 = false;
722 $chk_trn = false;
724 $seg_ar = explode($de, $seg_text);
725 $se_num = $seg_ar[2];
726 $env_ar['ST'][$st_ct]['count'] = strval($seg_ar[1]);
727 // 999 case: expect TA1 before ST, so capture batch icn here
728 if ($st_type == '999' || $st_type == '997') {
729 if ( isset($ta1_icn) && strlen($ta1_icn) ) {
730 $env_ar['ST'][$st_ct]['trace'] = $ta1_icn;
731 $ta1_icn = '';
734 // errors
735 if ($se_num != $stn) {
736 $this->message[] = 'edih_x12_envelopes: ST-SE number mismatch '.$stn.' '. $se_num.' in ISA '.$icn.PHP_EOL;
738 if (intval($seg_ar[1]) != $st_segs_ct) {
739 $this->message[] = 'edih_x12_envelopes: ST-SE segment count mismatch '.$st_segs_ct.' '.$seg_ar[1].' in ISA '.$icn.PHP_EOL;
741 continue;
744 if ( strncmp($sn, 'GE'.$de, 3) == 0 ) {
745 $seg_ar = explode($de, $seg_text);
746 $env_ar['GS'][$gs_ct]['count'] = $i - $gs_start - 1;
747 $env_ar['GS'][$gs_ct]['stcount'] = trim($seg_ar[1]); // ST count
748 $gs_st_ct += $seg_ar[1];
750 if ($seg_ar[2] != $env_ar['GS'][$gs_ct]['gsn']) {
751 $this->message[] = 'edih_x12_envelopes: GS-GE identifier mismatch'.PHP_EOL;
753 if ( $gs_ct === 0 && ($seg_ar[1] != count($env_ar['ST'])) ) {
754 $this->message[] = 'edih_x12_envelopes: GS count of ST mismatch'.PHP_EOL;
755 } elseif ($gs_st_ct != count($env_ar['ST']) ) {
756 $this->message[] = 'edih_x12_envelopes: GS count of ST mismatch'.PHP_EOL;
758 continue;
761 if ( strncmp($sn, 'IEA'.$de, 4) == 0 ) {
762 $seg_ar = explode($de, $seg_text);
763 $env_ar['ISA'][$icn]['count'] = $isa_segs_ct;
764 $env_ar['ISA'][$icn]['gscount'] = $seg_ar[1];
765 $iea_ct++;
767 if ( count( $env_ar['GS'] ) != $seg_ar[1] ) {
768 $this->message[] = 'edih_x12_envelopes: GS count mismatch in ISA '.$icn.PHP_EOL;
769 $gsct = count( $env_ar['GS'] );
770 $this->message[] = 'GS group count: '.$gsct.' IEA01: '.$seg_ar[1].' segment: '.$seg_text;
772 if ($env_ar['ISA'][$icn]['icn'] !== $seg_ar[2]) {
773 $this->message[] = 'edih_x12_envelopes: ISA-IEA identifier mismatch ISA '.$icn.' IEA '.$seg_ar[2];
775 if ($iea_ct == $isa_ct) {
776 $trnset_seg_ct += $isa_segs_ct;
777 //if ( $i+1 != $trnset_seg_ct ) {
778 if ( $i != $trnset_seg_ct ) {
779 $this->message[] = 'edih_x12_envelopes: IEA segment count error '.($i).' : '.$trnset_seg_ct;
781 } else {
782 $this->message[] = 'edih_x12_envelopes: ISA-IEA count mismatch ISA '.$isa_ct.' IEA '.$iea_ct;
784 continue;
788 return $env_ar;
792 * Parse x12 file contents into array of segments.
794 * @uses edih_x12_delimiters()
795 * @uses edih_x12_scan()
797 * @param string $file_text
798 * @return array array['i'] = segment, or empty on error
800 public function edih_x12_segments($file_text='') {
801 $ar_seg = array();
802 // do verifications
803 if ( $file_text ) {
804 if (!$this->constructing) {
805 // need to validate file
806 $vars = $this->edih_file_text($file_text, false, true, false);
807 $f_str = $file_text;
808 $dt = ( isset($vars['delimiters']['t']) ) ? $vars['delimiters']['t'] : '';
809 } else {
810 $f_str = $file_text;
811 if ( isset($this->delimiters['t']) ) {
812 $dt = $this->delimiters['t'];
813 } else {
814 $delims = $this->edih_x12_delimiters( substr($f_str, 0, 126) );
815 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
818 } elseif ( $this->text ) {
819 $f_str = $this->text;
820 if ( isset($this->delimiters['t']) ) {
821 $dt = $this->delimiters['t'];
822 } else {
823 $delims = $this->edih_x12_delimiters( substr($f_str, 0, 126) );
824 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
826 } else {
827 $this->message[] = 'edih_x12_segments: no file text';
828 return $ar_seg;
830 // did we get the segment terminator?
831 if ( !$dt ) {
832 $this->message[] = 'edih_x12_segments: invalid delimiters';
833 return $ar_seg;
835 // OK, now initialize variables
836 $seg_pos = 0; // position where segment begins
837 $seg_end = 0;
838 $seg_ct = 0;
839 $moresegs = true;
840 // could test this against simple $segments = explode($dt, $f_str)
841 while ($moresegs) {
842 // extract each segment from the file text
843 $seg_end = strpos($f_str, $dt, $seg_pos);
844 $seg_text = substr($f_str, $seg_pos, $seg_end-$seg_pos);
845 $seg_pos = $seg_end + 1;
846 $moresegs = strpos($f_str, $dt, $seg_pos);
847 $seg_ct++;
848 // we trim in case there are line or carriage returns
849 $ar_seg[$seg_ct] = trim($seg_text);
852 return $ar_seg;
857 * extract the segments representing a transaction for CLM01 pt-encounter number
858 * note: there may be more than one in a file, all matching are returned
859 * 27x transactions will have unique BHT03 that could be used as the claimid argument
861 * return_array[i] => transaction segments array
862 * return_array[i][j] => particular segment string
864 * @param string $clm01 837 CLM01 or BHT03 from 277
865 * @param string $stn ST number -- optional, limit search to that ST-SE envelope
866 * @param string $filetext optional file contents
867 * @return array multidimensional array of segments or empty on failure
869 public function edih_x12_transaction($clm01, $stn='', $filetext='') {
871 $ret_ar = array();
873 if (!$clm01) {
874 $this->message[] = 'edih_x12_transaction: invalid argument';
875 return $ret_ar;
878 $de = '';
879 $tp = '';
880 $seg_ar = array();
881 $env_ar = array();
882 // select the data to search
883 if ( $filetext && !$this->constructing) {
884 $vars = $this->edih_file_text($filetext, true, true, true);
885 $tp = ( isset($vars['type']) ) ? $vars['type'] : $tp;
886 $de = ( isset($vars['delimiters']['e']) ) ? $vars['delimiters']['e'] : $de;
887 $seg_ar = ( isset($vars['segments']) ) ? $vars['segments'] : $seg_ar;
888 //$env_ar = $vars['envelopes']; // probably faster without envelopes in this case
889 } elseif ( count($this->segments) ) {
890 // default created object
891 $seg_ar = $this->segments;
892 if (count($this->delimiters)) {
893 $de = $this->delimiters['e'];
894 } else {
895 $de = (substr(reset($segment_ar), 0, 3) == 'ISA') ? substr(reset($segment_ar), 3, 1) : '';
897 $tp = ($this->type) ? $this->type : $this->edih_x12_type();
898 $env_ar = ( isset($this->envelopes['ST']) ) ? $this->envelopes : $env_ar;
899 } elseif ($this->text) {
900 // object with file text, but no processing
901 $tp = $this->edih_x12_type();
902 $seg_ar = ( $tp ) ? $this->edih_x12_segments() : $seg_ar;
903 if ( count($seg_ar) ) {
904 $de = substr(reset($seg_ar), 3, 1);
906 } else {
907 $this->message[] = 'edih_x12_transaction: invalid search data';
908 return $ret_ar;
911 if ( !count($seg_ar) ) {
912 $this->message[] = 'edih_x12_transaction: invalid segments';
913 return $ret_ar;
915 if (!$de) {
916 $this->message[] = 'edih_x12_transaction: invalid delimiters';
917 return $ret_ar;
919 //array('HB'=>'271', 'HS'=>'270', 'HR'=>'276', 'HI'=>'278',
920 // 'HN'=>'277', 'HP'=>'835', 'FA'=>'999', 'HC'=>'837');
921 if ( substr($tp, 0, 5) == 'mixed' ) { $tp = substr($tp, -2); }
922 if ( !strpos('|HB|271|HS|270|HR|276|HI|278|HN|277|HP|835|FA|999|HC|837', $tp) ) {
923 $this->message[] = 'edih_x12_transaction: wrong edi type for transaction search '.$tp;
924 return $ret_ar;
926 $idx = 0;
927 $is_found = false;
928 $slice = array();
929 $srch_ar = array();
930 $sl_idx = 0;
931 // there may be several in same ST envelope with the same $clm01, esp. 835
932 // we will get each set of relevant transaction segments in foreach() below
933 if ( count($env_ar) ) {
934 foreach($env_ar['ST'] as $st) {
935 if ( strlen($stn) && $st['stn'] != $stn ) { continue; }
936 if ( isset($st['acct']) && count($st['acct']) ) {
937 $ky = array_search($clm01, $st['acct']);
938 if ($ky !== false) {
939 $srch_ar[$idx]['array'] = array_slice($seg_ar, $st['start'], $st['count'], true);
940 $srch_ar[$idx]['start'] = $st['start'];
941 $srch_ar[$idx]['type'] = $st['type'];
942 $idx++;
947 // if not identified in envelope search, use segments
948 if ( !count($srch_ar) ) {
949 $srch_ar[0]['array'] = $seg_ar;
950 $srch_ar[0]['start'] = 0; // with array_slice() the index is absolute zero base
951 $srch_ar[0]['type'] = $tp;
953 // verify we have type
954 if ($srch_ar[0]['type'] == 'NA' || !$srch_ar[0]['type']) {
955 $this->edih_message('edih_x12_transaction(): invalid file type '.$srch_ar[0]['type']);
956 return $ret_ar;
958 // segments we check
959 $test_id = array('TRN','CLM','CLP','ST'.$de,'BHT','REF','LX'.$de,'PLB','SE'.$de);
961 foreach($srch_ar as $srch) {
962 $idx = $srch['start'] - 1; // align index to segments array offset
963 $type = (string)$srch['type'];
964 $is_found = false;
965 $idval = '';
966 $idlen = 1;
968 foreach($srch['array'] as $key=>$seg) {
969 $idx++;
971 $test_str = substr($seg, 0, 3);
972 if ( !in_array($test_str, $test_id, true) ) { continue; }
974 // the opening ST segment should be in each search array,
975 // so type and search values can be determined here.
976 if ( strncmp($seg, 'ST'.$de, 3) == 0) {
977 $stseg = explode($de, $seg);
978 $type = ($type) ? $type : (string)$stseg[1];
980 $idval = ( strpos('|HN|277|HB|271', $type) ) ? 'TRN'.$de.'2'.$de.$clm01 : '';
981 $idval = ( strpos('|HR|276|HS|270', $type) ) ? 'TRN'.$de.'1'.$de.$clm01 : $idval;
982 $idval = ( strpos('|HI|278', $type) ) ? 'REF'.$de.'EJ'.$de.$clm01 : $idval;
983 $idval = ( strpos('|HC|837', $type) ) ? 'CLM'.$de.$clm01.$de : $idval;
984 $idval = ( strpos('|HP|835', $type) ) ? 'CLP'.$de.$clm01.$de : $idval;
985 $idlen = strlen($idval);
987 continue;
989 //array('HB'=>'271', 'HS'=>'270', 'HR'=>'276', 'HI'=>'278',
990 // 'HN'=>'277', 'HP'=>'835', 'FA'=>'999', 'HC'=>'837');
991 // these types use the BHT segment to begin transactions
992 if ( strpos('|HI|278|HN|277|HR|276|HB|271|HS|270|HC|837', $type) ) {
994 if ( strncmp($seg, 'BHT'.$de, 4) === 0 ) {
995 $bht_seg = explode($de, $seg);
996 $bht_pos = $idx;
997 //$bht_pos = $key;
998 if ( $is_found && isset($slice[$sl_idx]['start']) ) {
999 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1000 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1001 $is_found = false;
1002 $sl_idx++;
1003 } elseif ( strcmp($clm01, $bht_seg[3]) === 0 ) {
1004 // matched by BHT03 identifier
1005 $is_found = true;
1006 $slice[$sl_idx]['start'] = $bht_pos;
1008 continue;
1011 if (strncmp($seg, $idval, $idlen) === 0 ) {
1012 // matched by clm01 identifier (idval)
1013 $is_found = true;
1014 $slice[$sl_idx]['start'] = $bht_pos;
1015 continue;
1019 if ( $type == 'HP' || $type == '835') {
1020 if ( strncmp($seg, 'CLP'.$de, 4) === 0 ) {
1021 if ( strncmp($seg, $idval, $idlen) === 0 ) {
1022 if ( $is_found && isset($slice[$sl_idx]['start']) ) {
1023 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1024 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1025 $sl_idx++;
1027 $is_found = true;
1028 $slice[$sl_idx]['start'] = $idx;
1029 //$slice[$sl_idx]['start'] = $key;
1030 } else {
1031 if ( $is_found && isset($slice[$sl_idx]['start']) ) {
1032 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1033 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1034 $is_found = false;
1035 $sl_idx++;
1038 continue;
1040 // LX segment is often used to group claim payment information
1041 // we do not capture TS3 or TS2 segments in the transaction
1042 if ( strncmp($seg, 'LX'.$de, 3) === 0 ) {
1043 if ( $is_found && isset($slice[$sl_idx]['start']) ) {
1044 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1045 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1046 $is_found = false;
1047 $sl_idx++;
1049 continue;
1051 // PLB segment is part of summary/trailer in 835
1052 // not part of the preceeding transaction
1053 if ( strncmp($seg, 'PLB'.$de, 4) === 0 ) {
1054 if ( $is_found && isset($slice[$sl_idx]['start']) ) {
1055 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1056 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1057 $is_found = false;
1058 $sl_idx++;
1060 continue;
1063 // SE will always mark end of transaction segments
1064 if ( strncmp($seg, 'SE'.$de, 3) === 0 ) {
1065 if ( $is_found && isset($slice[$sl_idx]['start']) ) {
1066 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1067 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1068 $is_found = false;
1069 $sl_idx++;
1072 } // end foreach($srch['array'] as $seg)
1073 } // end foreach($srch_ar as $srch)
1075 if ( count($slice) ) {
1076 foreach($slice as $sl) {
1077 $ret_ar[] = array_slice($seg_ar, $sl['start'], $sl['count'], true);
1081 return $ret_ar;
1086 * get the segment(s) with a particular ID, such as CLP, NM1, etc.
1087 * return is array
1088 * array[i] => matching segment string
1090 * @param string $segmentID such as NM1, CLP, STC, etc.
1091 * @param string $srchStr optional string contained in segment
1092 * @param array $seg_array optional supplied array of segments to search
1093 * @return array
1095 public function edih_get_segment($segmentID, $srchStr='', $seg_array='') {
1097 $ret_ar = array();
1098 $seg_ar = array();
1099 $segid = ( strlen($segmentID) ) ? trim($segmentID) : '';
1101 $srch = ( strlen($srchStr) ) ? $srchStr : '';
1104 if ( !$segid ) {
1105 $this->message[] = 'edih_get_segment(): missing segment ID';
1106 return $ret_ar;
1109 $de = ( isset($this->delimiters['e']) ) ? $this->delimiters['e'] : '';
1110 $dt = ( isset($this->delimiters['t']) ) ? $this->delimiters['t'] : '';
1112 // segment array from edih_x12_transaction() is two dimension
1113 if ( is_array($seg_array) && count($seg_array) ) {
1114 if ( isset($seg_array[0]) && is_array($seg_array[0]) ) {
1115 foreach($seg_array as $ar) { $seg_ar = array_merge($seg_ar, $ar); }
1116 } else {
1117 $seg_ar = $seg_array;
1119 } elseif ( count($this->segments) ) {
1120 $seg_ar = $this->segments;
1121 } elseif ( $this->text ) {
1122 if ( !$de ) {
1123 $delims = $this->edih_x12_delimiters( substr($this->text, 0, 126) );
1124 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
1125 $de = ( isset($delims['e']) ) ? $delims['e'] : '';
1127 if ( !$de || !$dt ) {
1128 $this->message[] = 'edih_get_segment() : unable to get delimiters';
1129 return $ret_ar;
1132 $segsrch = ($segid == 'ISA') ? $segid.$de : $dt.$segid.$de;
1133 $seg_pos = 1;
1134 $see_pos = 2;
1135 while ($seg_pos) {
1136 $seg_pos = strpos($this->text, $segsrch, $seg_pos);
1137 $see_pos = strpos($this->text, $dt, $seg_pos+1);
1138 if ($seg_pos) {
1139 $segstr = trim(substr($this->text, $seg_pos, $see_pos-$seg_pos), $dt);
1140 if ($srch) {
1141 if ( strpos($segstr, $srch) !== false ) {
1142 $ret_ar[] = $segstr;
1144 } else {
1145 $ret_ar[] = $segstr;
1147 $seg_pos = $see_pos+1;
1152 if ( count($seg_ar) ) {
1153 $cmplen = strlen($segid.$de);
1154 foreach($seg_ar as $key=>$seg) {
1155 if (strncmp($seg, $segid.$de, $cmplen) === 0 ) {
1156 if ($srch) {
1157 if ( strpos($seg, $srch) !== false ) {
1158 $ret_ar[$key] = $seg;
1160 } else {
1161 $ret_ar[$key] = $seg;
1165 } else {
1166 $this->message[] = 'edih_get_segment() : no segments or text content available';
1169 return $ret_ar;
1174 * Get a slice of the segments array
1175 * Supply an array with one or more of the following keys and values:
1177 * ['trace'] => trace value from 835(TRN02) or 999(TA101) x12 type
1178 * ['ISA13'] => ISA13
1179 * ['GS06'] => GS06 (sconsider also 'ISA13')
1180 * ['ST02'] => ST02 (condider also 'ISA13' and 'GS06')
1181 * ['keys'] => true to preserve segment numbering from original file
1183 * The return value will be an array of one or more segments.
1184 * The 'search' parameter results in one or more segments containing
1185 * the search string. The
1186 * @param array note: all element values except 'keys' are strings
1187 * @return array
1189 function edih_x12_slice($arg_array, $file_text='') {
1191 $ret_ar = array();
1192 $f_str = '';
1193 // see what we have
1194 if ( !is_array($arg_array) || !count($arg_array) ) {
1195 // debug
1196 $this->message[] = 'edih_x12_slice() invalid array argument';
1197 return $ret_ar;
1200 if ( $file_text ) {
1201 // need to validate file edih_file_text($file_text, $type=false, $delimiters=false, $segments=false)
1202 $vars = $this->edih_file_text($file_text, true, true, false);
1203 if ( is_array($vars) && count($vars) ) {
1204 $f_str = $file_text;
1205 $dt = ( isset($vars['delimiters']['t']) ) ? $vars['delimiters']['t'] : '';
1206 $de = ( isset($vars['delimiters']['e']) ) ? $vars['delimiters']['e'] : '';
1207 $ft = ( isset($vars['type']) ) ? $vars['type'] : '';
1208 //$seg_ar = ( isset($vars['segments']) ) ? $vars['segments'] : '';
1209 //$env_ar = $this->edih_x12_envelopes($f_str);
1210 } else {
1211 $this->message[] = 'edih_x12_slice() error processing file text';
1212 // debug
1213 //echo $this->edih_message().PHP_EOL;
1214 return $ret_ar;
1216 } elseif ( count($this->segments) && count($this->envelopes) && count($this->delimiters) ) {
1217 $seg_ar = $this->segments;
1218 $env_ar = $this->envelopes;
1219 $dt = $this->delimiters['t'];
1220 $de = $this->delimiters['e'];
1221 $ft = $this->type;
1222 } else {
1223 $this->message[] = 'edih_x12_slice() object missing needed properties';
1224 // debug
1225 //echo $this->edih_message().PHP_EOL;
1226 return $ret_ar;
1228 // initialize search variables
1229 $trace = '';
1230 $stn = '';
1231 $gsn = '';
1232 $icn = '';
1233 $prskeys = false;
1235 foreach($arg_array as $key=>$val) {
1236 switch((string)$key) {
1237 case 'trace': $trace = (string)$val; break;
1238 case 'ST02': $stn = (string)$val; break;
1239 case 'GS06': $gsn = (string)$val; break;
1240 case 'ISA13': $icn = (string)$val; break;
1241 case 'keys': $prskeys = (bool)$val; break;
1245 if ($trace && strpos('|HP|FA', $ft) === false) {
1246 $this->message[] = 'edih_x12_slice() incorrect type ['.$ft.'] for trace';
1247 return $ret_ar;
1250 if ($f_str) {
1251 $srchstr = '';
1252 if ($icn) {
1253 $icnpos = strpos($f_str, $de.$icn.$de);
1254 if ($icnpos === false) {
1255 // $icn not found
1256 $this->message[] = 'edih_x12_slice() did not find ISA13 '.$icn;
1257 // debug
1258 //echo $this->edih_message().PHP_EOL;
1259 return $ret_ar;
1260 } elseif ($icnpos < 106) {
1261 $isapos = 0;
1262 } else {
1263 $isapos = strrpos($f_str, $dt.'ISA'.$de, ($icnpos - strlen($f_str)) ) + 1;
1265 $ieapos = strpos($f_str, $de.$icn.$dt, $isapos);
1266 $ieapos = strpos($f_str, $dt, $ieapos) + 1;
1267 $segidx = ($prskeys) ? substr_count($f_str, $dt, 0, $isapos+2) + 1 : 0;
1269 $srchstr = substr($f_str, $isapos, $ieapos-$isapos);
1271 if ($gsn) {
1272 $srchstr = ($srchstr) ? $srchstr : $f_str;
1273 $gspos = strpos($srchstr, $de.$gsn.$de);
1274 if ($gspos === false) {
1275 // $gsn not found
1276 $this->message[] = 'edih_x12_slice() did not find GS06 '.$gsn;
1277 return $ret_ar;
1278 } else {
1279 $gspos = strrpos(substr($srchstr, 0, $gspos), $dt) + 1;
1281 $gepos = strpos($srchstr, $dt.'GE'.$dt, $gspos);
1282 $gepos = strpos($srchstr, $dt, $gepos+1) + 1;
1283 $segidx = ($prskeys) ? substr_count ($f_str, $dt, 0, $gspos+2) + 1 : 0;
1285 $srchstr = substr($srchstr, $gspos, $gepos-$gspos);
1287 if ($stn) {
1288 $srchstr = ($srchstr) ? $srchstr : $f_str;
1289 $sttp = $this->gstype_ar[$ft];
1290 $seg_st = $dt.'ST'.$de.$sttp.$de.$stn ;
1291 $seg_se = $dt.'SE'.$de;
1292 // $segpos = 1;
1293 $stpos = strpos($srchstr, $seg_st);
1294 if ($stpos === false) {
1295 // $stn not found
1296 $this->message[] = 'edih_x12_slice() did not find ST02 '.$stn;
1297 return $ret_ar;
1298 } else {
1299 $stpos = $stpos + 1;
1301 $sepos = strpos($srchstr, $seg_se, $stpos);
1302 $sepos = strpos($srchstr, $dt, $sepos+1);
1303 $segidx = ($prskeys) ? substr_count ($f_str, $dt, 0, $stpos+2) + 1 : 0;
1305 $srchstr = substr($srchstr, $stpos, $sepos-$stpos);
1307 if ($trace) {
1309 $trpos = strpos($f_str, $de.$trace);
1310 if ($trpos === false) {
1311 // $icn not found
1312 $this->message[] = 'edih_x12_slice() did not find trace '.$trace;
1313 return $ret_ar;
1315 $sttp = $this->gstype_ar[$ft];
1316 $seg_st = $dt.'ST'.$de.$sttp.$de;
1317 $stpos = strrpos($f_str, $seg_st, ($trpos - strlen($f_str)) );
1318 $sepos = strpos($f_str, $dt.'SE'.$de, $stpos);
1319 $sepos = strpos($f_str, $dt, $sepos+1);
1321 $segidx = ($prskeys) ? substr_count($f_str, $dt, 0, $st_pos+2) + 1 : 0;
1322 $srchstr = substr($f_str, $stpos+1, $sepos-$stpos);
1324 // if we have a match, the $srchstr should have the desired segments
1325 if ($trace || $icn || $gsn || $stn) {
1326 if ($srchstr) {
1327 $seg_ar = explode($dt, $srchstr);
1328 // to keep segment numbers same as original file
1329 foreach($seg_ar as $seg) {
1330 $ret_ar[$segidx] = $seg;
1331 $segidx++;
1333 return $ret_ar;
1334 } else {
1335 $this->message[] = 'edih_x12_slice() error creating substring';
1336 return $ret_ar;
1339 // file_text not supplied, check for object values
1340 } elseif (!($seg_ar && $env_ar && $dt && $de && $ft) ) {
1341 // debug
1342 $this->message[] = 'edih_x12_slice() error is processing file';
1343 return $ret_ar;
1345 // file_text not supplied, use object values
1346 if ($trace) {
1347 foreach($env_ar['ST'] as $st) {
1348 if ($st['trace'] == $trace) {
1349 // have to add one to the count to capture the SE segment so html_str has data
1350 // when called from edih_835_payment_html function in edih_835_html.php 4-25-17 SMW
1351 $ret_ar = array_slice($seg_ar, $st['start'], $st['count']+1, $prskeys);
1352 break;
1355 } elseif ($icn && !($stn || $gsn) ) {
1356 if ( isset($env_ar['ISA'][$icn]) ) {
1357 $ret_ar = array_slice($seg_ar, $env_ar['ISA'][$icn]['start'], $env_ar['ISA'][$icn]['count'], $prskeys);
1359 } elseif ($gsn && !$stn) {
1360 foreach($env_ar['GS'] as $gs) {
1361 if ($icn) {
1362 if ( ($gs['icn'] == $icn) && ($gs['gsn'] == $gsn) ) {
1363 $ret_ar = array_slice($seg_ar, $gs['start'], $gs['count'], $prskeys);
1364 break;
1366 } else {
1367 if ( $gs['gsn'] == $gsn ) {
1368 $ret_ar = array_slice($seg_ar, $gs['start'], $gs['count'], $prskeys);
1369 break;
1373 } elseif ($stn) {
1374 // ;
1375 foreach($env_ar['ST'] as $st) {
1377 if ($icn) {
1378 if ($gsn) {
1379 if ($st['icn'] == $icn && $st['gsn'] == $gsn && $st['stn'] == $stn) {
1380 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1381 break;
1383 } else {
1384 if ($st['icn'] == $icn && $st['stn'] == $stn) {
1385 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1386 break;
1389 } elseif ($gsn) {
1390 if ($st['gsn'] == $gsn && $st['stn'] == $stn) {
1391 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1392 break;
1394 } elseif ($st['stn'] == $stn) {
1396 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1397 break;
1400 } else {
1401 $this->message[] = 'edih_x12_slice() no file text or invalid array argument keys or values';
1404 if ( !count($ret_ar) ) {
1405 $this->message[] = 'edih_x12_slice() no match';
1407 return $ret_ar;
1410 // end class edih_x12_file