Various changes and fixes (#7424)
[openemr.git] / library / edihistory / edih_x12file_class.php
blob5ecb17231c4629ab956e125d15086bbb619b960d
1 <?php
3 /*
4 * edih_x12file_class.php
6 * Copyright 2014 Kevin McCormick Longview, Texas
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; version 3 or later. You should have
17 * received a copy of the GNU General Public License along with this program;
18 * if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * <http://opensource.org/licenses/gpl-license.php>
23 * @link: https://www.open-emr.org
24 * @author: Kevin McCormick
25 * @package: OpenEMR
26 * @subpackage: ediHistory
29 /* ********* project notes =================
30 * determine GET and POST array elements
31 * process new files -- type and csv data values
32 * display tables -- links with GET and POST
33 * find files -- find transactions
34 * format display
36 * ==========================================
39 /*********** php code here ****************************************************************/
41 /**
42 * Class to read EDI X12 files in healthcare setting
44 * It is assumed that EDI X12 files will have mime-type text/plain; charset=us-ascii
46 * initialize with file path or as empty object, e.g.
47 * $x12_file = new edih_x12_file(filepath); segment array and envelope array, no file text
48 * $x12_file = new edih_x12_file(filepath, false); no segment or envelope array, no file text
49 * $x12_file = new edih_x12_file(filepath, false, true); no segment or envelope array, yes file text
50 * or
51 * $x12_file = new edih_x12_file(); empty object, ' _x12_ ' methods available if file text supplied as method argument
53 * The properties filename, type, version, valid, isx12, hasGS, hasST, and delimiters should be available
54 * if the valid filepath is provided when creating the object.
56 * @param string $filepath default = ''
57 * @param bool $mk_segs default = true
58 * @param bool $text default = false
59 * @return bool|string true for empty object "ovgis" for validated x12
61 class edih_x12_file
63 // properties
64 private $filepath = '';
65 private $filename = '';
66 private $type = '';
67 private $version = '';
68 private $text = '';
69 private $length = 0;
70 private $valid = false;
71 private $isx12 = false;
72 private $hasGS = false;
73 private $hasST = false;
74 private $message = array();
75 private $delimiters = array();
76 private $segments = array();
77 private $envelopes = array();
79 private $constructing = false;
81 private $gstype_ar = array('HB' => '271', 'HS' => '270', 'HR' => '276', 'HN' => '277',
82 'HI' => '278', 'HP' => '835', 'FA' => '999', 'HC' => '837');
84 function __construct($file_path = '', $mk_segs = true, $text = false)
87 if ($file_path === '') {
88 return true;
92 if (is_file($file_path) && is_readable($file_path)) {
93 $this->filepath = trim($file_path);
94 $this->filename = basename($this->filepath);
95 $f_text = file_get_contents($this->filepath);
97 $testval = ($f_text) ? $this->edih_x12_scan($f_text) : '';
98 $this->valid = ( strpos($testval, 'v') ) ? true : false;
99 $this->isx12 = ( strpos($testval, 'i') ) ? true : false;
100 $this->hasGS = ( strpos($testval, 'g') ) ? true : false;
101 $this->hasST = ( strpos($testval, 's') ) ? true : false;
103 if ($this->valid) {
104 $this->constructing = true;
105 $this->text = ($text) ? $f_text : '';
106 $this->length = ($f_text) ? strlen($f_text) : 0;
107 if ($this->isx12) {
108 $this->delimiters = $this->edih_x12_delimiters(substr($f_text, 0, 126));
109 $this->version = substr($f_text, 84, 5);
110 if ($mk_segs) {
111 $this->segments = $this->edih_x12_segments($f_text);
112 if (is_array($this->segments) && count($this->segments)) {
113 $this->envelopes = $this->edih_x12_envelopes();
114 $this->type = $this->edih_x12_type();
115 } else {
116 $this->message[] = 'edih_x12_file: error in creating segment array ' . text($this->filename) . PHP_EOL;
118 } else {
119 // read file contents to try and determine x12 type
120 $this->type = $this->edih_x12_type($f_text);
124 } else {
125 // invalid file path
126 $this->message[] = 'edih_x12_file: invalid file path ' . text($file_path);
129 $this->constructing = false;
130 return $this->valid;
134 * function to support empty object and '_x12_' functions called with supplied file text
136 * @param string $file_text
137 * @param bool return x12 type
138 * @param bool return delimiters
139 * @param bool return segments
140 * @return array array['filetext'] and maybe ['type'] ['$delimiters'] ['segments']
142 private function edih_file_text($file_text, $type = false, $delimiters = false, $segments = false)
145 $ret_ar = array();
146 if (!$file_text || is_string($file_text) == false) {
147 $this->message[] = 'edih_file_text(): invalid argument';
148 return $ret_ar;
151 // do verifications
152 $v = $this->edih_x12_scan($file_text);
153 if (!strpos($v, 's')) {
154 $this->message[] = 'edih_file_text(): failed scan of file text (' . text($v) . ')';
155 return $ret_ar;
159 $this->constructing = true;
161 if ($type) {
162 $ret_ar['type'] = $this->edih_x12_type($file_text);
165 if ($delimiters) {
166 $ret_ar['delimiters'] = $this->edih_x12_delimiters(substr($file_text, 0, 126));
169 if ($segments) {
170 $ret_ar['segments'] = $this->edih_x12_segments($file_text);
174 $this->constructing = false;
176 return $ret_ar;
180 * functions to return properties
182 public function classname()
184 return get_class($this);
186 public function edih_filepath()
188 return $this->filepath;
190 public function edih_filename()
192 return $this->filename;
194 public function edih_type()
196 return $this->type;
198 public function edih_version()
200 return $this->version;
202 public function edih_text()
204 return $this->text;
206 public function edih_length()
208 return $this->length;
210 public function edih_valid()
212 return $this->valid;
214 public function edih_isx12()
216 return $this->isx12;
218 public function edih_hasGS()
220 return $this->hasGS;
222 public function edih_hasST()
224 return $this->hasST;
226 public function edih_delimiters()
228 return $this->delimiters;
230 public function edih_segments()
232 return $this->segments;
234 public function edih_envelopes()
236 return $this->envelopes;
240 * message statements regarding object or from functions
241 * formatted as html
243 * @return string
245 public function edih_message()
247 $str_html = '<p>' . PHP_EOL;
248 if (count($this->message)) {
249 foreach ($this->message as $msg) {
250 $str_html .= text($msg) . '<br />' . PHP_EOL;
253 $str_html .= PHP_EOL . '</p>' . PHP_EOL;
254 } else {
255 $str_html = '';
258 return $str_html;
263 * Numeric type of x12 HC file associated with GS01 code
265 * @param string $gs01
266 * @return string|bool
268 public function edih_gs_type($gs01)
270 $tpky = strtoupper($gs01);
271 return ( isset($this->gstype_ar[$tpky]) ) ? $this->gstype_ar[$tpky] : false;
275 * Use PHP FileInfo to check mime type and then scan for unwanted characters
276 * check for Non-basic ASCII character and <%, <asp:, <?, ${, #!, <scr (any other evil script indicators?)
277 * basically allows A-Z a-z 0-9 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ and newline carriage_return
278 * This function accepts the following mime-type: text/plain; charset=us-ascii
280 * The return string can be 'ovigs' ov - valid, igs - ISA GS ST
282 * @param string $filetext the file contents
283 * @return string zero length on failure
285 public function edih_x12_scan($filetext)
287 $hasval = '';
288 $ftxt = ( $filetext && is_string($filetext) ) ? trim($filetext) : $filetext;
289 // possibly $ftxt = trim($filetext, "\x00..\x1F") to remove ASCII control characters
290 // remove newlines
291 if (strpos($ftxt, PHP_EOL)) {
292 $ftxt = str_replace(PHP_EOL, '', $ftxt);
295 $flen = ( $ftxt && is_string($ftxt) ) ? strlen($ftxt) : 0;
296 if (!$flen) {
297 $this->message[] = 'edih_x12_scan: zero length or invalid file text';
298 return $hasval;
301 $de = '';
302 $dt = '';
303 // use finfo php class
304 if (class_exists('finfo')) {
305 $finfo = new finfo(FILEINFO_MIME);
306 $mimeinfo = $finfo->buffer($ftxt);
307 if (strncmp($mimeinfo, 'text/plain; charset=us-ascii', 28) !== 0) {
308 $this->message[] = 'edih_x12_scan: ' . text($this->filename) . ' : invalid mime info: <br />' . text($mimeinfo);
310 return $hasval;
315 if (preg_match('/[^\x20-\x7E\x0A\x0D]|(<\?)|(<%)|(<asp)|(#!)|(\$\{)|(<scr)|(script:)/is', $ftxt, $matches, PREG_OFFSET_CAPTURE)) {
317 $this->message[] = 'edih_x12_scan: suspect characters in file ' . text($this->filename) . '<br />' .
318 ' character: ' . text($matches[0][0]) . ' position: ' . text($matches[0][1]);
320 return $hasval;
323 $hasval = 'ov'; // valid
324 // check for required segments ISA GS ST; assume segment terminator is last character
325 if (substr($ftxt, 0, 3) === 'ISA') {
326 $hasval = 'ovi';
327 $de = substr($ftxt, 3, 1);
328 $dt = substr($ftxt, -1);
329 if (strpos($ftxt, $dt . 'GS' . $de, 0)) {
330 $hasval = 'ovig';
333 if (strpos($ftxt, $dt . 'ST' . $de, 0)) {
334 $hasval = 'ovigs';
338 return $hasval;
342 * read the GS segments in file contents to determine x12 type, or, if the
343 * object was created with a file path and envelopes, from the GS envelope array
345 * @param string $file_text optional contents of an x12 file
346 * @return string the x12 type, e.g. 837, 835, 277, 999, etc.
348 public function edih_x12_type($file_text = '')
350 $tpstr = '';
351 $tp_tmp = array();
352 $f_text = '';
353 $delims = array();
354 $delimarg = '';
355 $dt = ( isset($this->delimiters['t']) ) ? $this->delimiters['t'] : '';
356 $de = ( isset($this->delimiters['e']) ) ? $this->delimiters['e'] : '';
358 if ($file_text) {
359 // For when '_x12_' function is called with file contents as argument
360 if (!$this->constructing) {
361 $vars = $this->edih_file_text($file_text, false, true, false);
362 $f_text = $file_text;
363 $dt = ( isset($vars['delimiters']['t']) ) ? $vars['delimiters']['t'] : '';
364 $de = ( isset($vars['delimiters']['e']) ) ? $vars['delimiters']['e'] : '';
365 } elseif ($this->text) {
366 // called in initial construction, delimiters already created if x12 file
367 $f_text =& $this->text;
368 if (!$dt) {
369 $this->message[] = 'edih_x12_type: not x12 file';
370 return $tpstr;
372 } else {
373 // called after file scan, but no segment array exists
374 $f_text =& $file_text;
375 if (!$dt) {
376 $delims = $this->edih_x12_delimiters(substr($f_text, 0, 126));
377 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
378 $de = ( isset($delims['e']) ) ? $delims['e'] : '';
382 if (!$f_text) {
383 $this->message[] = 'edih_x12_type: failed scan of file content';
384 return $tpstr;
386 } elseif (isset($this->envelopes['GS'])) {
387 // No argument, so if envelopes exist, take values from there
388 foreach ($this->envelopes['GS'] as $gs) {
389 $tp_tmp[] = $gs['type'];
391 } elseif (count($this->segments)) {
392 // No argument and no envelopes, so scan segments
393 if (!$de) {
394 $de = substr(reset($this->segments), 3, 1);
397 foreach ($this->segments as $seg) {
398 if (strncmp($seg, 'GS' . $de, 3) == 0) {
399 $gs_ar = explode($de, $seg);
400 if (array_key_exists($gs_ar[1], $this->gstype_ar)) {
401 //$tp_tmp[] = $this->gstype_ar[$gs_ar[1]];
402 $tp_tmp[] = $gs_ar[1];
403 } else {
404 $tp_tmp[] = $gs_ar[1];
405 $this->message[] = 'edih_x12_type: unknown x12 type ' . text($gs_ar[1]);
409 } else {
410 $this->message[] = 'edih_x12_type: no content to determine x12 type';
411 return $tpstr;
414 // $f_text has content only if file contents supplied or in text property
415 if ($f_text) {
416 // use regular expression instead of strpos($f_text, $dt.'GS'.$de)
417 $pcrepattern = '/GS\\' . $de . '(?:HB|HS|HR|HI|HN|HP|FA|HC)\\' . $de . '/';
418 $pr = preg_match_all($pcrepattern, $f_text, $matches, PREG_OFFSET_CAPTURE);
420 if ($pr && count($matches)) {
421 foreach ($matches as $m) {
422 //$gspos1 = $m[0][1];
423 $gs_ar1 = explode($de, $m[0][0]);
424 if (array_key_exists($gs_ar1[1], $this->gstype_ar)) {
425 //$tp_tmp[] = $this->gstype_ar[$gs_ar1[1]];
426 $tp_tmp[] = $gs_ar1[1];
427 } else {
428 $tp_tmp[] = $gs_ar1[1];
429 $this->message[] = 'edih_x12_type: unknown x12 type ' . text($gs_ar1[1]);
432 } else {
433 $this->message[] = 'edih_x12_type: did not find GS segment ';
436 /* **** this replaced by preg_match_all() above ******
438 // scan GS segments
439 $gs_str = $dt.'GS'.$de;
440 $gs_pos = 1;
441 $gse_pos = 2;
442 while ($gs_pos) {
443 $gs_pos = strpos($f_text, $gs_str, $gs_pos);
444 if ($gs_pos) {
445 $gsterm = strpos($f_text, $dt, $gs_pos+1);
446 $gsseg = trim(substr($f_text, $gs_pos+1, $gsterm-$gs_pos-1));
447 //$gs_ar = explode($de, substr($f_text, $gs_pos+1, $gsterm-$gs_pos-1) );
448 $this->message[] = 'edih_x12_type: '.$gsseg.PHP_EOL;
449 $gs_ar = explode($de, $gsseg);
450 if ( array_key_exists($gs_ar[1], $this->gstype_ar) ) {
451 $tp_tmp[] = $this->gstype_ar[$gs_ar[1]];
452 } else {
453 $tp_tmp[] = $gs_ar[1];
454 $this->message[] = 'edih_x12_type: unknown x12 type '.$gs_ar[1];
456 $gs_pos = $gsterm + 1;
459 ******************* */
462 // x12 type information collected
463 if (count($tp_tmp)) {
464 $tp3 = array_values(array_unique($tp_tmp));
465 // mixed should not happen -- concatenated ISA envelopes of different types?
466 $tpstr = ( count($tp3) > 1 ) ? 'mixed|' . implode("|", $tp3) : $tp3[0];
467 //$this->message[] = 'edih_x12_type: ' . $tpstr;
468 } else {
469 $this->message[] = 'edih_x12_type: error in identifying type ';
470 return false;
473 return $tpstr;
478 * Extract x12 delimiters from the ISA segment
480 * There are obviously easier/faster ways of doing this, but we go character by character.
481 * The value returned is empty on error, otherwise:
482 * <pre>
483 * array('t'=>segment terminator, 'e'=>element delimiter,
484 * 's'=>sub-element delimiter, 'r'=>repetition delimiter)
485 * </pre>
487 * @param string $isa_str110 first n>=106 characters of x12 file
488 * @return array array or empty on error
490 public function edih_x12_delimiters($isa_str110 = '')
493 $delim_ar = array();
494 if (!$isa_str110 && $this->text) {
495 $isa_str = substr($this->text, 0, 106);
496 } else {
497 $isa_str = trim($isa_str110);
500 $isalen = strlen($isa_str);
501 if ($isalen >= 106) {
502 if (substr($isa_str, 0, 3) != 'ISA') {
503 // not the starting characters
504 $this->message[] = 'edih_x12_delimiters: text does not begin with ISA';
505 return $delim_ar;
508 /* Extract delimiters using the prescribed positions.
509 * -- problem is possibly mangled files
510 * $t_ar['e'] = substr($isa_str, 3, 1);
511 * $t_ar['r'] = substr($isa_str, 82, 1);
512 * $t_ar['s'] = substr($isa_str, 104, 1);
513 * $t_ar['t'] = substr($isa_str, 105, 1);
515 } else {
516 $this->message[] = 'edih_x12_delimiters: ISA string too short' . PHP_EOL;
517 return $delim_ar;
520 $s = '';
521 $delim_ct = 0;
522 $de = substr($isa_str, 3, 1); // ISA*
523 for ($i = 0; $i < $isalen; $i++) {
524 if ($isa_str[$i] == $de) {
525 // element count incremented at end of loop
526 // repetition separator in version 5010
527 if ($delim_ct == 11) {
528 $dr = substr($s, 1, 1);
531 if ($delim_ct == 12) {
532 if (strpos($s, '501') === false) {
533 $dr = '';
538 if ($delim_ct == 15) {
539 $ds = substr($isa_str, $i + 1, 1);
540 $dt = substr($isa_str, $i + 2, 1);
543 if ($delim_ct == 16) {
544 break;
547 $s = $isa_str[$i]; // $elem_delim;
548 $delim_ct++;
549 } else {
550 $s .= $isa_str[$i];
554 // there are 16 elements in ISA segment
555 if ($delim_ct < 16) {
556 // too few elements -- probably did not get delimiters
557 $this->message[] = "edih_x12_delimiters: too few elements in ISA string";
558 return $delim_ar;
562 $delim_ar = array('t' => $dt, 'e' => $de, 's' => $ds, 'r' => $dr);
564 return $delim_ar;
568 * Create a multidimensional array of edi envelope info from object segments.
569 * Useful for slicing and dicing. The ['ST'][$stky]['trace'] value is used only for 835
570 * or 999 type files and the ['ST'][$stky]['acct'][i] array will have multiple values
571 * likely only for 835, 271, and 277 types, because response from a payer will have
572 * multiple transactions in the ST-SE envelope while OpenEMR probably will place each
573 * transaction in its own ST-SE envelope for 270 and 837 types.
575 * The ['start'] and ['count'] values are for use in php function array_slice()
576 * The numeric keys of the segments array begin at 1 and the ['start'] value is one less
577 * than the actual key because array_slice() offset is zero-based.
579 * <pre>
580 * ['ISA'][$icn]=>['start']['count']['sender']['receiver']['icn']['gscount']['date']
581 * ['GS'][$gs_ct]=>['start']['count']['gsn']['icn']['sender']['date']['stcount']['type']
582 * ['ST'][$stky]=>['start']['count']['stn']['gsn']['icn']['type']['trace']['acct']
583 * ['ST'][$stky]['acct'][i]=>CLM01
584 * ['ST'][$stky]['bht03'][i]=>BHT03
585 * </pre>
587 * @return array array as shown above or empty on error
589 public function edih_x12_envelopes($file_text = '')
591 // produce an array of envelopes and positions
592 $env_ar = array();
593 $de = '';
594 if ($file_text) {
595 // presume need for file scan and delimiters
596 $vars = $this->edih_file_text($file_text, false, true, true);
597 $segment_ar = (isset($vars['segments']) ) ? $vars['segments'] : array();
598 $de = (isset($vars['delimiters']) ) ? $vars['delimiters']['e'] : '';
599 //$segment_ar = $this->edih_x12_segments($file_text);
600 if (empty($segment_ar) || !$de) {
601 $this->message[] = 'edih_x12_envelopes: invalid file text';
602 return $env_ar;
604 } elseif (count($this->segments)) {
605 $segment_ar = $this->segments;
606 if (isset($this->delimiters['e'])) {
607 $de = $this->delimiters['e'];
608 } else {
609 $de = (substr(reset($segment_ar), 0, 3) == 'ISA') ? substr(reset($segment_ar), 3, 1) : '';
611 } else {
612 $this->message[] = 'edih_x12_envelopes: no text or segments';
613 return $env_ar;
616 if (!$de) {
617 $this->message[] = 'edih_x12_envelopes: invalid delimiters';
618 return $env_ar;
622 // get the segment array bounds
623 $seg_first = (reset($segment_ar) !== false) ? key($segment_ar) : '1';
624 $seg_last = (end($segment_ar) !== false) ? key($segment_ar) : count($segment_ar) + $seg_first;
625 if (reset($segment_ar) === false) {
626 $this->message[] = 'edi_x12_envelopes: reset() error in segment array';
627 return $env_ar;
628 } else {
629 $seg_ct = $seg_last + 1;
632 // variables
633 $seg_txt = '';
634 $sn = '';
635 $st_type = '';
636 $st_ct = 0;
637 $isa_ct = 0;
638 $iea_ct = 0;
639 $gs_st_ct = 0;
640 $trnset_seg_ct = 0;
641 $st_segs_ct = 0;
642 $isa_segs_ct = 0;
643 $chk_trn = false;
644 $trncd = '2';
645 //$id278 = false;
646 $ta1_icn = '';
647 $seg_ar = array();
648 // the segment IDs we look for
649 $chk_segs = array('ISA', 'GS' . $de, 'TA1', 'ST' . $de, 'BHT', 'HL' . $de, 'TRN', 'CLP', 'CLM', 'SE' . $de, 'GE' . $de, 'IEA');
651 for ($i = $seg_first; $i < $seg_ct; $i++) {
652 // counters
653 $isa_segs_ct++;
654 $st_segs_ct++;
656 $seg_text = $segment_ar[$i];
657 $sn = substr($seg_text, 0, 4);
658 // skip over segments that are not envelope boundaries or identifiers
659 if (!in_array(substr($sn, 0, 3), $chk_segs)) {
660 continue;
663 // create the structure array
664 if (strncmp($sn, 'ISA' . $de, 4) == 0) {
665 $seg_ar = explode($de, $seg_text);
666 $icn = trim($seg_ar[13]);
668 $env_ar['ISA'][$icn]['start'] = strval($i - 1);
669 $env_ar['ISA'][$icn]['sender'] = trim($seg_ar[6]);
670 $env_ar['ISA'][$icn]['receiver'] = trim($seg_ar[8]);
671 $env_ar['ISA'][$icn]['icn'] = $icn;
672 $env_ar['ISA'][$icn]['date'] = trim($seg_ar[9]); // YYMMDD
673 $env_ar['ISA'][$icn]['version'] = trim($seg_ar[12]);
675 $isa_segs_ct = 1;
676 $isa_ct++;
677 continue;
681 if (strncmp($sn, 'GS' . $de, 3) == 0) {
682 $seg_ar = explode($de, $seg_text);
683 $gs_start = strval($i - 1);
684 $gsn = $seg_ar[6];
685 // GS06 could be used to id 997/999 response, if truly unique
686 // cannot index on $gsn due to concatenated ISA envelopes and non-unique
687 $gs_ct = isset($env_ar['GS']) ? count($env_ar['GS']) : 0;
689 $env_ar['GS'][$gs_ct]['start'] = $gs_start;
690 $env_ar['GS'][$gs_ct]['gsn'] = $gsn;
691 $env_ar['GS'][$gs_ct]['icn'] = $icn;
692 $env_ar['GS'][$gs_ct]['sender'] = trim($seg_ar[2]);
693 $env_ar['GS'][$gs_ct]['date'] = trim($seg_ar[4]);
694 $env_ar['GS'][$gs_ct]['srcid'] = '';
695 // to verify type of edi transaction
696 if (array_key_exists($seg_ar[1], $this->gstype_ar)) {
697 $gs_fid = $this->gstype_ar[$seg_ar[1]];
698 $env_ar['GS'][$gs_ct]['type'] = $seg_ar[1];
699 } else {
700 $gs_fid = 'NA';
701 $env_ar['GS'][$gs_ct]['type'] = 'NA';
702 $this->message[] = 'edih_x12_envelopes: Unknown GS type ' . text($seg_ar[1]);
705 continue;
708 // expect 999 TA1 before ST
709 if (strncmp($sn, 'TA1' . $de, 4) == 0) {
710 $seg_ar = explode($de, $seg_text);
711 if (isset($seg_ar[1]) && $seg_ar[1]) {
712 $ta1_icn = $seg_ar[1];
713 } else {
714 $this->message[] = 'edih_x12_envelopes: Error in TA1 segment response ICN';
717 //TA1*ISA13ICN*ISA09DATE*ISA10TIME*ACKCode*NoteCode~
718 continue;
722 if (strncmp($sn, 'ST' . $de, 3) == 0) {
723 $seg_ar = explode($de, $seg_text);
724 $stn = $seg_ar[2];
725 $st_type = $seg_ar[1];
726 $st_start = strval($i);
727 $st_segs_ct = 1;
728 $st_ct = isset($env_ar['ST']) ? count($env_ar['ST']) : 0;
730 $env_ar['ST'][$st_ct]['start'] = strval($i - 1);
731 $env_ar['ST'][$st_ct]['count'] = '';
732 $env_ar['ST'][$st_ct]['stn'] = $seg_ar[2];
733 $env_ar['ST'][$st_ct]['gsn'] = $gsn;
734 $env_ar['ST'][$st_ct]['icn'] = $icn;
735 $env_ar['ST'][$st_ct]['type'] = $seg_ar[1];
736 $env_ar['ST'][$st_ct]['trace'] = '0';
737 $env_ar['ST'][$st_ct]['acct'] = array();
738 $env_ar['ST'][$st_ct]['bht03'] = array();
739 // GS file id FA can be 999 or 997
740 if ($gs_fid != $st_type && strpos($st_type, '99') === false) {
741 $this->message[] = "edih_x12_envelopes: ISA " . text($icn) . ", GS " . text($gsn . " " . $gs_fid) . " ST " . text($stn . " " . $st_type) . " type mismatch" . PHP_EOL;
745 continue;
749 if (strpos('|270|271|276|277|278', $st_type)) {
751 if (strncmp($sn, 'BHT' . $de, 4) == 0) {
752 $seg_ar = explode($de, $seg_text);
753 if (isset($seg_ar[2])) {
754 $trncd = ($seg_ar[2] == '13') ? '1' : '2';
755 // 13 = request, otherwise assume response
756 } else {
757 $this->message[] = 'edih_x12_envelopes: missing BHT02 type element';
760 if (isset($seg_ar[3]) && $seg_ar[3]) {
761 $env_ar['ST'][$st_ct]['bht03'][] = $seg_ar[3];
762 } else {
763 $this->message[] = 'edih_x12_envelopes: missing BHT03 identifier';
767 if (strncmp($sn, 'HL' . $de, 3) == 0) {
768 $seg_ar = explode($de, $seg_text);
769 if (isset($seg_ar[3]) && $seg_ar[3]) {
770 $chk_trn = ( strpos('|22|23|PT', $seg_ar[3]) ) ? true : false;
771 } else {
772 $this->message[] = 'edih_x12_envelopes: missing HL03 level element';
775 continue;
778 if ($chk_trn && strncmp($sn, 'TRN' . $de, 4) == 0) {
779 $seg_ar = explode($de, $seg_text);
780 if (isset($seg_ar[1]) && $seg_ar[1] == $trncd) {
781 $env_ar['ST'][$st_ct]['acct'][] = (isset($seg_ar[2])) ? $seg_ar[2] : '';
782 $chk_trn = false;
783 } else {
784 $this->message[] = 'edih_x12_envelopes: missing TRN02 type identifier element';
787 continue;
792 if ($st_type == '835') {
793 if (strncmp($sn, 'TRN' . $de, 4) == 0) {
794 $seg_ar = explode($de, $seg_text);
795 if (!isset($seg_ar[2]) || !isset($seg_ar[3])) {
796 $this->message[] = 'error in 835 TRN segment ' . text($seg_text);
799 $env_ar['ST'][$st_ct]['trace'] = (isset($seg_ar[2])) ? $seg_ar[2] : "";
800 // to match OpenEMR billing parse file name
801 if (isset($seg_ar[4])) {
802 $env_ar['GS'][$gs_ct]['srcid'] = $seg_ar[4];
803 } else {
804 $env_ar['GS'][$gs_ct]['srcid'] = (isset($seg_ar[3])) ? $seg_ar[3] : "";
808 continue;
811 if (strncmp($sn, 'CLP' . $de, 4) == 0) {
812 $seg_ar = explode($de, $seg_text);
813 if (isset($seg_ar[1])) {
814 $env_ar['ST'][$st_ct]['acct'][] = $seg_ar[1];
815 } else {
816 $this->message[] = 'error in 835 CLP segment ' . text($seg_text);
819 continue;
824 if ($st_type == '837') {
825 if (strncmp($sn, 'BHT' . $de, 4) == 0) {
826 $seg_ar = explode($de, $seg_text);
827 if (isset($seg_ar[3]) && $seg_ar[3]) {
828 $env_ar['ST'][$st_ct]['bht'][] = $seg_ar[3];
829 } else {
830 $this->message[] = 'edih_x12_envelopes: missing BHT03 identifier';
835 if (strncmp($sn, 'CLM' . $de, 4) == 0) {
836 $seg_ar = explode($de, $seg_text);
837 if (isset($seg_ar[1])) {
838 $env_ar['ST'][$st_ct]['acct'][] = $seg_ar[1];
839 } else {
840 $this->message[] = 'error in 837 CLM segment ' . text($seg_text);
843 continue;
848 if (strncmp($sn, 'SE' . $de, 3) == 0) {
849 // make sure no lingering toggle
850 $id278 = false;
851 $chk_trn = false;
853 $seg_ar = explode($de, $seg_text);
854 $se_num = $seg_ar[2];
855 $env_ar['ST'][$st_ct]['count'] = strval($seg_ar[1]);
856 // 999 case: expect TA1 before ST, so capture batch icn here
857 if ($st_type == '999' || $st_type == '997') {
858 if (isset($ta1_icn) && strlen($ta1_icn)) {
859 $env_ar['ST'][$st_ct]['trace'] = $ta1_icn;
860 $ta1_icn = '';
864 // errors
865 if ($se_num != $stn) {
866 $this->message[] = 'edih_x12_envelopes: ST-SE number mismatch ' . text($stn) . ' ' . text($se_num) . ' in ISA ' . text($icn) . PHP_EOL;
869 if (intval($seg_ar[1]) != $st_segs_ct) {
870 $this->message[] = 'edih_x12_envelopes: ST-SE segment count mismatch ' . text($st_segs_ct) . ' ' . text($seg_ar[1]) . ' in ISA ' . text($icn) . PHP_EOL;
873 continue;
877 if (strncmp($sn, 'GE' . $de, 3) == 0) {
878 $seg_ar = explode($de, $seg_text);
879 $env_ar['GS'][$gs_ct]['count'] = $i - $gs_start - 1;
880 $env_ar['GS'][$gs_ct]['stcount'] = trim($seg_ar[1]); // ST count
881 $gs_st_ct += $seg_ar[1];
883 if ($seg_ar[2] != $env_ar['GS'][$gs_ct]['gsn']) {
884 $this->message[] = 'edih_x12_envelopes: GS-GE identifier mismatch' . PHP_EOL;
887 if ($gs_ct === 0 && ($seg_ar[1] != count($env_ar['ST']))) {
888 $this->message[] = 'edih_x12_envelopes: GS count of ST mismatch' . PHP_EOL;
889 } elseif ($gs_st_ct != count($env_ar['ST'])) {
890 $this->message[] = 'edih_x12_envelopes: GS count of ST mismatch' . PHP_EOL;
893 continue;
897 if (strncmp($sn, 'IEA' . $de, 4) == 0) {
898 $seg_ar = explode($de, $seg_text);
899 $env_ar['ISA'][$icn]['count'] = $isa_segs_ct;
900 $env_ar['ISA'][$icn]['gscount'] = $seg_ar[1];
901 $iea_ct++;
903 if (count($env_ar['GS']) != $seg_ar[1]) {
904 $this->message[] = 'edih_x12_envelopes: GS count mismatch in ISA ' . text($icn) . PHP_EOL;
905 $gsct = count($env_ar['GS']);
906 $this->message[] = 'GS group count: ' . text($gsct) . ' IEA01: ' . text($seg_ar[1]) . ' segment: ' . text($seg_text);
909 if ($env_ar['ISA'][$icn]['icn'] !== $seg_ar[2]) {
910 $this->message[] = 'edih_x12_envelopes: ISA-IEA identifier mismatch ISA ' . text($icn) . ' IEA ' . text($seg_ar[2]);
913 if ($iea_ct == $isa_ct) {
914 $trnset_seg_ct += $isa_segs_ct;
915 //if ( $i+1 != $trnset_seg_ct ) {
916 if ($i != $trnset_seg_ct) {
917 $this->message[] = 'edih_x12_envelopes: IEA segment count error ' . text($i) . ' : ' . text($trnset_seg_ct);
919 } else {
920 $this->message[] = 'edih_x12_envelopes: ISA-IEA count mismatch ISA ' . text($isa_ct) . ' IEA ' . text($iea_ct);
923 continue;
928 return $env_ar;
932 * Parse x12 file contents into array of segments.
934 * @uses edih_x12_delimiters()
935 * @uses edih_x12_scan()
937 * @param string $file_text
938 * @return array array['i'] = segment, or empty on error
940 public function edih_x12_segments($file_text = '')
942 $ar_seg = array();
943 // do verifications
944 if ($file_text) {
945 if (!$this->constructing) {
946 // need to validate file
947 $vars = $this->edih_file_text($file_text, false, true, false);
948 $f_str = $file_text;
949 $dt = ( isset($vars['delimiters']['t']) ) ? $vars['delimiters']['t'] : '';
950 } else {
951 $f_str = $file_text;
952 if (isset($this->delimiters['t'])) {
953 $dt = $this->delimiters['t'];
954 } else {
955 $delims = $this->edih_x12_delimiters(substr($f_str, 0, 126));
956 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
959 } elseif ($this->text) {
960 $f_str = $this->text;
961 if (isset($this->delimiters['t'])) {
962 $dt = $this->delimiters['t'];
963 } else {
964 $delims = $this->edih_x12_delimiters(substr($f_str, 0, 126));
965 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
967 } else {
968 $this->message[] = 'edih_x12_segments: no file text';
969 return $ar_seg;
972 // did we get the segment terminator?
973 if (!$dt) {
974 $this->message[] = 'edih_x12_segments: invalid delimiters';
975 return $ar_seg;
978 // OK, now initialize variables
979 $seg_pos = 0; // position where segment begins
980 $seg_end = 0;
981 $seg_ct = 0;
982 $moresegs = true;
983 // could test this against simple $segments = explode($dt, $f_str)
984 while ($moresegs) {
985 // extract each segment from the file text
986 $seg_end = strpos($f_str, $dt, $seg_pos);
987 $seg_text = substr($f_str, $seg_pos, $seg_end - $seg_pos);
988 $seg_pos = $seg_end + 1;
989 $moresegs = strpos($f_str, $dt, $seg_pos);
990 $seg_ct++;
991 // we trim in case there are line or carriage returns
992 $ar_seg[$seg_ct] = trim($seg_text);
996 return $ar_seg;
1001 * extract the segments representing a transaction for CLM01 pt-encounter number
1002 * note: there may be more than one in a file, all matching are returned
1003 * 27x transactions will have unique BHT03 that could be used as the claimid argument
1005 * return_array[i] => transaction segments array
1006 * return_array[i][j] => particular segment string
1008 * @param string $clm01 837 CLM01 or BHT03 from 277
1009 * @param string $stn ST number -- optional, limit search to that ST-SE envelope
1010 * @param string $filetext optional file contents
1011 * @return array multidimensional array of segments or empty on failure
1013 public function edih_x12_transaction($clm01, $stn = '', $filetext = '')
1016 $ret_ar = array();
1018 if (!$clm01) {
1019 $this->message[] = 'edih_x12_transaction: invalid argument';
1020 return $ret_ar;
1024 $de = '';
1025 $tp = '';
1026 $seg_ar = array();
1027 $env_ar = array();
1028 // select the data to search
1029 if ($filetext && !$this->constructing) {
1030 $vars = $this->edih_file_text($filetext, true, true, true);
1031 $tp = ( isset($vars['type']) ) ? $vars['type'] : $tp;
1032 $de = ( isset($vars['delimiters']['e']) ) ? $vars['delimiters']['e'] : $de;
1033 $seg_ar = ( isset($vars['segments']) ) ? $vars['segments'] : $seg_ar;
1034 //$env_ar = $vars['envelopes']; // probably faster without envelopes in this case
1035 } elseif (count($this->segments)) {
1036 // default created object
1037 $seg_ar = $this->segments;
1038 if (count($this->delimiters)) {
1039 $de = $this->delimiters['e'];
1040 } else {
1041 $de = (substr(reset($segment_ar), 0, 3) == 'ISA') ? substr(reset($segment_ar), 3, 1) : '';
1044 $tp = ($this->type) ? $this->type : $this->edih_x12_type();
1045 $env_ar = ( isset($this->envelopes['ST']) ) ? $this->envelopes : $env_ar;
1046 } elseif ($this->text) {
1047 // object with file text, but no processing
1048 $tp = $this->edih_x12_type();
1049 $seg_ar = ( $tp ) ? $this->edih_x12_segments() : $seg_ar;
1050 if (count($seg_ar)) {
1051 $de = substr(reset($seg_ar), 3, 1);
1053 } else {
1054 $this->message[] = 'edih_x12_transaction: invalid search data';
1055 return $ret_ar;
1058 if (!count($seg_ar)) {
1059 $this->message[] = 'edih_x12_transaction: invalid segments';
1060 return $ret_ar;
1063 if (!$de) {
1064 $this->message[] = 'edih_x12_transaction: invalid delimiters';
1065 return $ret_ar;
1068 //array('HB'=>'271', 'HS'=>'270', 'HR'=>'276', 'HI'=>'278',
1069 // 'HN'=>'277', 'HP'=>'835', 'FA'=>'999', 'HC'=>'837');
1070 if (substr($tp, 0, 5) == 'mixed') {
1071 $tp = substr($tp, -2);
1074 if (!strpos('|HB|271|HS|270|HR|276|HI|278|HN|277|HP|835|FA|999|HC|837', $tp)) {
1075 $this->message[] = 'edih_x12_transaction: wrong edi type for transaction search ' . text($tp);
1076 return $ret_ar;
1079 $idx = 0;
1080 $is_found = false;
1081 $slice = array();
1082 $srch_ar = array();
1083 $sl_idx = 0;
1084 // there may be several in same ST envelope with the same $clm01, esp. 835
1085 // we will get each set of relevant transaction segments in foreach() below
1086 if (count($env_ar)) {
1087 foreach ($env_ar['ST'] as $st) {
1088 if (strlen($stn) && $st['stn'] != $stn) {
1089 continue;
1092 if (isset($st['acct']) && count($st['acct'])) {
1093 $ky = array_search($clm01, $st['acct']);
1094 if ($ky !== false) {
1095 $srch_ar[$idx]['array'] = array_slice($seg_ar, $st['start'], $st['count'], true);
1096 $srch_ar[$idx]['start'] = $st['start'];
1097 $srch_ar[$idx]['type'] = $st['type'];
1098 $idx++;
1104 // if not identified in envelope search, use segments
1105 if (!count($srch_ar)) {
1106 $srch_ar[0]['array'] = $seg_ar;
1107 $srch_ar[0]['start'] = 0; // with array_slice() the index is absolute zero base
1108 $srch_ar[0]['type'] = $tp;
1111 // verify we have type
1112 if ($srch_ar[0]['type'] == 'NA' || !$srch_ar[0]['type']) {
1113 $this->edih_message('edih_x12_transaction(): invalid file type ' . text($srch_ar[0]['type']));
1114 return $ret_ar;
1117 // segments we check
1118 $test_id = array('TRN','CLM','CLP','ST' . $de,'BHT','REF','LX' . $de,'PLB','SE' . $de);
1120 foreach ($srch_ar as $srch) {
1121 $idx = $srch['start'] - 1; // align index to segments array offset
1122 $type = (string)$srch['type'];
1123 $is_found = false;
1124 $idval = '';
1125 $idlen = 1;
1127 foreach ($srch['array'] as $key => $seg) {
1128 $idx++;
1130 $test_str = substr($seg, 0, 3);
1131 if (!in_array($test_str, $test_id, true)) {
1132 continue;
1136 // the opening ST segment should be in each search array,
1137 // so type and search values can be determined here.
1138 if (strncmp($seg, 'ST' . $de, 3) == 0) {
1139 $stseg = explode($de, $seg);
1140 $type = ($type) ? $type : (string)$stseg[1];
1142 $idval = ( strpos('|HN|277|HB|271', $type) ) ? 'TRN' . $de . '2' . $de . $clm01 : '';
1143 $idval = ( strpos('|HR|276|HS|270', $type) ) ? 'TRN' . $de . '1' . $de . $clm01 : $idval;
1144 $idval = ( strpos('|HI|278', $type) ) ? 'REF' . $de . 'EJ' . $de . $clm01 : $idval;
1145 $idval = ( strpos('|HC|837', $type) ) ? 'CLM' . $de . $clm01 . $de : $idval;
1146 $idval = ( strpos('|HP|835', $type) ) ? 'CLP' . $de . $clm01 . $de : $idval;
1147 $idlen = strlen($idval);
1149 continue;
1152 //array('HB'=>'271', 'HS'=>'270', 'HR'=>'276', 'HI'=>'278',
1153 // 'HN'=>'277', 'HP'=>'835', 'FA'=>'999', 'HC'=>'837');
1154 // these types use the BHT segment to begin transactions
1155 if (strpos('|HI|278|HN|277|HR|276|HB|271|HS|270|HC|837', $type)) {
1157 if (strncmp($seg, 'BHT' . $de, 4) === 0) {
1158 $bht_seg = explode($de, $seg);
1159 $bht_pos = $idx;
1160 //$bht_pos = $key;
1161 if ($is_found && isset($slice[$sl_idx]['start'])) {
1162 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1163 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1164 $is_found = false;
1165 $sl_idx++;
1166 } elseif (strcmp($clm01, $bht_seg[3]) === 0) {
1167 // matched by BHT03 identifier
1168 $is_found = true;
1169 $slice[$sl_idx]['start'] = $bht_pos;
1172 continue;
1176 if (strncmp($seg, $idval, $idlen) === 0) {
1177 // matched by clm01 identifier (idval)
1178 $is_found = true;
1179 $slice[$sl_idx]['start'] = $bht_pos;
1180 continue;
1185 if ($type == 'HP' || $type == '835') {
1186 if (strncmp($seg, 'CLP' . $de, 4) === 0) {
1187 if (strncmp($seg, $idval, $idlen) === 0) {
1188 if ($is_found && isset($slice[$sl_idx]['start'])) {
1189 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1190 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1191 $sl_idx++;
1194 $is_found = true;
1195 $slice[$sl_idx]['start'] = $idx;
1196 //$slice[$sl_idx]['start'] = $key;
1197 } else {
1198 if ($is_found && isset($slice[$sl_idx]['start'])) {
1199 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1200 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1201 $is_found = false;
1202 $sl_idx++;
1206 continue;
1209 // LX segment is often used to group claim payment information
1210 // we do not capture TS3 or TS2 segments in the transaction
1211 if (strncmp($seg, 'LX' . $de, 3) === 0) {
1212 if ($is_found && isset($slice[$sl_idx]['start'])) {
1213 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1214 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1215 $is_found = false;
1216 $sl_idx++;
1219 continue;
1222 // PLB segment is part of summary/trailer in 835
1223 // not part of the preceeding transaction
1224 if (strncmp($seg, 'PLB' . $de, 4) === 0) {
1225 if ($is_found && isset($slice[$sl_idx]['start'])) {
1226 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1227 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1228 $is_found = false;
1229 $sl_idx++;
1232 continue;
1236 // SE will always mark end of transaction segments
1237 if (strncmp($seg, 'SE' . $de, 3) === 0) {
1238 if ($is_found && isset($slice[$sl_idx]['start'])) {
1239 $slice[$sl_idx]['count'] = $idx - $slice[$sl_idx]['start'];
1240 //$slice[$sl_idx]['count'] = $key - $slice[$sl_idx]['start'];
1241 $is_found = false;
1242 $sl_idx++;
1245 } // end foreach($srch['array'] as $seg)
1246 } // end foreach($srch_ar as $srch)
1248 if (count($slice)) {
1249 foreach ($slice as $sl) {
1250 $ret_ar[] = array_slice($seg_ar, $sl['start'], $sl['count'], true);
1255 return $ret_ar;
1260 * get the segment(s) with a particular ID, such as CLP, NM1, etc.
1261 * return is array
1262 * array[i] => matching segment string
1264 * @param string $segmentID such as NM1, CLP, STC, etc.
1265 * @param string $srchStr optional string contained in segment
1266 * @param array $seg_array optional supplied array of segments to search
1267 * @return array
1269 public function edih_get_segment($segmentID, $srchStr = '', $seg_array = '')
1272 $ret_ar = array();
1273 $seg_ar = array();
1274 $segid = ( strlen($segmentID) ) ? trim($segmentID) : '';
1276 $srch = ( strlen($srchStr) ) ? $srchStr : '';
1279 if (!$segid) {
1280 $this->message[] = 'edih_get_segment(): missing segment ID';
1281 return $ret_ar;
1285 $de = ( isset($this->delimiters['e']) ) ? $this->delimiters['e'] : '';
1286 $dt = ( isset($this->delimiters['t']) ) ? $this->delimiters['t'] : '';
1288 // segment array from edih_x12_transaction() is two dimension
1289 if (is_array($seg_array) && count($seg_array)) {
1290 if (isset($seg_array[0]) && is_array($seg_array[0])) {
1291 foreach ($seg_array as $ar) {
1292 $seg_ar = array_merge($seg_ar, $ar);
1294 } else {
1295 $seg_ar = $seg_array;
1297 } elseif (count($this->segments)) {
1298 $seg_ar = $this->segments;
1299 } elseif ($this->text) {
1300 if (!$de) {
1301 $delims = $this->edih_x12_delimiters(substr($this->text, 0, 126));
1302 $dt = ( isset($delims['t']) ) ? $delims['t'] : '';
1303 $de = ( isset($delims['e']) ) ? $delims['e'] : '';
1306 if (!$de || !$dt) {
1307 $this->message[] = 'edih_get_segment() : unable to get delimiters';
1308 return $ret_ar;
1312 $segsrch = ($segid == 'ISA') ? $segid . $de : $dt . $segid . $de;
1313 $seg_pos = 1;
1314 $see_pos = 2;
1315 while ($seg_pos) {
1316 $seg_pos = strpos($this->text, $segsrch, $seg_pos);
1317 $see_pos = strpos($this->text, $dt, $seg_pos + 1);
1318 if ($seg_pos) {
1319 $segstr = trim(substr($this->text, $seg_pos, $see_pos - $seg_pos), $dt);
1320 if ($srch) {
1321 if (strpos($segstr, $srch) !== false) {
1322 $ret_ar[] = $segstr;
1324 } else {
1325 $ret_ar[] = $segstr;
1328 $seg_pos = $see_pos + 1;
1334 if (count($seg_ar)) {
1335 $cmplen = strlen($segid . $de);
1336 foreach ($seg_ar as $key => $seg) {
1337 if (strncmp($seg, $segid . $de, $cmplen) === 0) {
1338 if ($srch) {
1339 if (strpos($seg, $srch) !== false) {
1340 $ret_ar[$key] = $seg;
1342 } else {
1343 $ret_ar[$key] = $seg;
1347 } else {
1348 $this->message[] = 'edih_get_segment() : no segments or text content available';
1352 return $ret_ar;
1357 * Get a slice of the segments array
1358 * Supply an array with one or more of the following keys and values:
1360 * ['trace'] => trace value from 835(TRN02) or 999(TA101) x12 type
1361 * ['ISA13'] => ISA13
1362 * ['GS06'] => GS06 (sconsider also 'ISA13')
1363 * ['ST02'] => ST02 (condider also 'ISA13' and 'GS06')
1364 * ['keys'] => true to preserve segment numbering from original file
1366 * The return value will be an array of one or more segments.
1367 * The 'search' parameter results in one or more segments containing
1368 * the search string. The
1369 * @param array note: all element values except 'keys' are strings
1370 * @return array
1372 function edih_x12_slice($arg_array, $file_text = '')
1375 $ret_ar = array();
1376 $f_str = '';
1377 // see what we have
1378 if (!is_array($arg_array) || !count($arg_array)) {
1379 // debug
1380 $this->message[] = 'edih_x12_slice() invalid array argument';
1381 return $ret_ar;
1385 if ($file_text) {
1386 // need to validate file edih_file_text($file_text, $type=false, $delimiters=false, $segments=false)
1387 $vars = $this->edih_file_text($file_text, true, true, false);
1388 if (is_array($vars) && count($vars)) {
1389 $f_str = $file_text;
1390 $dt = ( isset($vars['delimiters']['t']) ) ? $vars['delimiters']['t'] : '';
1391 $de = ( isset($vars['delimiters']['e']) ) ? $vars['delimiters']['e'] : '';
1392 $ft = ( isset($vars['type']) ) ? $vars['type'] : '';
1393 //$seg_ar = ( isset($vars['segments']) ) ? $vars['segments'] : '';
1394 //$env_ar = $this->edih_x12_envelopes($f_str);
1395 } else {
1396 $this->message[] = 'edih_x12_slice() error processing file text';
1397 // debug
1398 //echo $this->edih_message().PHP_EOL;
1399 return $ret_ar;
1401 } elseif (count($this->segments) && count($this->envelopes) && count($this->delimiters)) {
1402 $seg_ar = $this->segments;
1403 $env_ar = $this->envelopes;
1404 $dt = $this->delimiters['t'];
1405 $de = $this->delimiters['e'];
1406 $ft = $this->type;
1407 } else {
1408 $this->message[] = 'edih_x12_slice() object missing needed properties';
1409 // debug
1410 //echo $this->edih_message().PHP_EOL;
1411 return $ret_ar;
1414 // initialize search variables
1415 $trace = '';
1416 $stn = '';
1417 $gsn = '';
1418 $icn = '';
1419 $prskeys = false;
1421 foreach ($arg_array as $key => $val) {
1422 switch ((string)$key) {
1423 case 'trace':
1424 $trace = (string)$val;
1425 break;
1426 case 'ST02':
1427 $stn = (string)$val;
1428 break;
1429 case 'GS06':
1430 $gsn = (string)$val;
1431 break;
1432 case 'ISA13':
1433 $icn = (string)$val;
1434 break;
1435 case 'keys':
1436 $prskeys = (bool)$val;
1437 break;
1442 if ($trace && strpos('|HP|FA', $ft) === false) {
1443 $this->message[] = 'edih_x12_slice() incorrect type [' . text($ft) . '] for trace';
1444 return $ret_ar;
1448 if ($f_str) {
1449 $srchstr = '';
1450 if ($icn) {
1451 $icnpos = strpos($f_str, $de . $icn . $de);
1452 if ($icnpos === false) {
1453 // $icn not found
1454 $this->message[] = 'edih_x12_slice() did not find ISA13 ' . text($icn);
1455 // debug
1456 //echo $this->edih_message().PHP_EOL;
1457 return $ret_ar;
1458 } elseif ($icnpos < 106) {
1459 $isapos = 0;
1460 } else {
1461 $isapos = strrpos($f_str, $dt . 'ISA' . $de, ($icnpos - strlen($f_str))) + 1;
1464 $ieapos = strpos($f_str, $de . $icn . $dt, $isapos);
1465 $ieapos = strpos($f_str, $dt, $ieapos) + 1;
1466 $segidx = ($prskeys) ? substr_count($f_str, $dt, 0, $isapos + 2) + 1 : 0;
1468 $srchstr = substr($f_str, $isapos, $ieapos - $isapos);
1471 if ($gsn) {
1472 $srchstr = ($srchstr) ? $srchstr : $f_str;
1473 $gspos = strpos($srchstr, $de . $gsn . $de);
1474 if ($gspos === false) {
1475 // $gsn not found
1476 $this->message[] = 'edih_x12_slice() did not find GS06 ' . text($gsn);
1477 return $ret_ar;
1478 } else {
1479 $gspos = strrpos(substr($srchstr, 0, $gspos), $dt) + 1;
1482 $gepos = strpos($srchstr, $dt . 'GE' . $dt, $gspos);
1483 $gepos = strpos($srchstr, $dt, $gepos + 1) + 1;
1484 $segidx = ($prskeys) ? substr_count($f_str, $dt, 0, $gspos + 2) + 1 : 0;
1486 $srchstr = substr($srchstr, $gspos, $gepos - $gspos);
1489 if ($stn) {
1490 $srchstr = ($srchstr) ? $srchstr : $f_str;
1491 $sttp = $this->gstype_ar[$ft];
1492 $seg_st = $dt . 'ST' . $de . $sttp . $de . $stn ;
1493 $seg_se = $dt . 'SE' . $de;
1494 // $segpos = 1;
1495 $stpos = strpos($srchstr, $seg_st);
1496 if ($stpos === false) {
1497 // $stn not found
1498 $this->message[] = 'edih_x12_slice() did not find ST02 ' . text($stn);
1499 return $ret_ar;
1500 } else {
1501 $stpos = $stpos + 1;
1504 $sepos = strpos($srchstr, $seg_se, $stpos);
1505 $sepos = strpos($srchstr, $dt, $sepos + 1);
1506 $segidx = ($prskeys) ? substr_count($f_str, $dt, 0, $stpos + 2) + 1 : 0;
1508 $srchstr = substr($srchstr, $stpos, $sepos - $stpos);
1511 if ($trace) {
1513 $trpos = strpos($f_str, $de . $trace);
1514 if ($trpos === false) {
1515 // $icn not found
1516 $this->message[] = 'edih_x12_slice() did not find trace ' . text($trace);
1517 return $ret_ar;
1520 $sttp = $this->gstype_ar[$ft];
1521 $seg_st = $dt . 'ST' . $de . $sttp . $de;
1522 $stpos = strrpos($f_str, $seg_st, ($trpos - strlen($f_str)));
1523 $sepos = strpos($f_str, $dt . 'SE' . $de, $stpos);
1524 $sepos = strpos($f_str, $dt, $sepos + 1);
1526 $segidx = ($prskeys) ? substr_count($f_str, $dt, 0, $st_pos + 2) + 1 : 0;
1527 $srchstr = substr($f_str, $stpos + 1, $sepos - $stpos);
1530 // if we have a match, the $srchstr should have the desired segments
1531 if ($trace || $icn || $gsn || $stn) {
1532 if ($srchstr) {
1533 $seg_ar = explode($dt, $srchstr);
1534 // to keep segment numbers same as original file
1535 foreach ($seg_ar as $seg) {
1536 $ret_ar[$segidx] = $seg;
1537 $segidx++;
1540 return $ret_ar;
1541 } else {
1542 $this->message[] = 'edih_x12_slice() error creating substring';
1543 return $ret_ar;
1547 // file_text not supplied, check for object values
1548 } elseif (!($seg_ar && $env_ar && $dt && $de && $ft)) {
1549 // debug
1550 $this->message[] = 'edih_x12_slice() error is processing file';
1551 return $ret_ar;
1554 // file_text not supplied, use object values
1555 if ($trace) {
1556 foreach ($env_ar['ST'] as $st) {
1557 if ($st['trace'] == $trace) {
1558 // have to add one to the count to capture the SE segment so html_str has data
1559 // when called from edih_835_payment_html function in edih_835_html.php 4-25-17 SMW
1560 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'] + 1, $prskeys);
1561 break;
1564 } elseif ($icn && !($stn || $gsn)) {
1565 if (isset($env_ar['ISA'][$icn])) {
1566 $ret_ar = array_slice($seg_ar, $env_ar['ISA'][$icn]['start'], $env_ar['ISA'][$icn]['count'], $prskeys);
1568 } elseif ($gsn && !$stn) {
1569 foreach ($env_ar['GS'] as $gs) {
1570 if ($icn) {
1571 if (($gs['icn'] == $icn) && ($gs['gsn'] == $gsn)) {
1572 $ret_ar = array_slice($seg_ar, $gs['start'], $gs['count'], $prskeys);
1573 break;
1575 } else {
1576 if ($gs['gsn'] == $gsn) {
1577 $ret_ar = array_slice($seg_ar, $gs['start'], $gs['count'], $prskeys);
1578 break;
1582 } elseif ($stn) {
1583 // ;
1584 foreach ($env_ar['ST'] as $st) {
1586 if ($icn) {
1587 if ($gsn) {
1588 if ($st['icn'] == $icn && $st['gsn'] == $gsn && $st['stn'] == $stn) {
1589 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1590 break;
1592 } else {
1593 if ($st['icn'] == $icn && $st['stn'] == $stn) {
1594 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1595 break;
1598 } elseif ($gsn) {
1599 if ($st['gsn'] == $gsn && $st['stn'] == $stn) {
1600 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1601 break;
1603 } elseif ($st['stn'] == $stn) {
1605 $ret_ar = array_slice($seg_ar, $st['start'], $st['count'], $prskeys);
1606 break;
1609 } else {
1610 $this->message[] = 'edih_x12_slice() no file text or invalid array argument keys or values';
1614 if (!count($ret_ar)) {
1615 $this->message[] = 'edih_x12_slice() no match';
1618 return $ret_ar;
1621 // end class edih_x12_file