/** * Extracts the serialized payload from an entry. This function is able * to handle an (string) entry itself or the entry can be addressed via an * index first. * @param $entry index pointing to the $entry'th entry in the segment * or if it is a string it needs to be the entry itself * @return serialized payload on success or an empty string upon failures * @throws ParamNotValidException * @throws PayloadExtractionException */ private function get_payload($entry = 0) { $el = null; // if an index was provided via $entry if (strncmp(gettype($entry), "integer", 7) == 0) { $el = $this->get($entry); if (count($el) != 1) { $this->log(__METHOD__ . ": %", array(new PayloadExtractionException($this->element . ", " . $el, 0))); throw new PayloadExtractionException($this->element . ", " . $el, 0); } $el = $el[$entry]; // get the entry string // if $entry is an valid entry itself } else { if (strncmp(gettype($entry), "string", 6) == 0) { // the layout must be valid if (!StringUtil::has_layout($this->element->get_shm_seg_layout(), $entry)) { $this->log(__METHOD__ . ": %", array(new PayloadExtractionException($this->element . ", " . $el, 1))); throw new PayloadExtractionException($this->element . ", " . $el, 1); } $el = $entry; // get the entry string } else { $this->log(__METHOD__ . ": %", array(new ParamNotValidException("entry(int|string)=" . gettype($entry)))); throw new ParamNotValidExcepton("entry(int|string)=" . gettype($entry)); } } // finally extract the payload if all conditions passed if (strncmp(gettype($el), "string", 6) == 0) { $olleft = StringUtil::get_offset_last($this->element->get_shm_seg_var_eleft(), $el); $olright = StringUtil::get_offset_last($this->element->get_shm_seg_var_eleft(), $el); return substr($el, $olleft + 1, $olright - ($olleft + 1)); } else { return ""; } }
/** * Write $values to the shared memory segment in the form * '[val1][...][valn-1][serialize(valn)];'. Each value need * to match the memory layout related which is currently set * for the segment. * @param $values array containing values * @param $key postion to write to. -1 means simply append. * @throws SegmentException * @return number of bytes written. */ public function write($values = array(), $key = -1) { $written_data = 0; if (!is_null($this->element) && strncmp(gettype($values), "array", 5) == 0) { // build string to put first $pstr = ""; $l = $this->element->get_shm_seg_var_eleft(); $r = $this->element->get_shm_seg_var_eright(); $d = $this->element->get_shm_seg_var_delimiter(); foreach (array_values($values) as $k => $v) { if ($k == count($values) - 1) { $pstr = $pstr . $l . serialize($v) . $r . $d; } else { $pstr = $pstr . $l . $v . $r; } } $this->log(__METHOD__ . ": pstr=%, key=%", array($pstr, $key)); // check it against the memory layout first $has_layout = false; if (!StringUtil::has_layout($this->element->get_shm_seg_layout(), $pstr)) { $this->log(__METHOD__ . ": %", array(new SegmentException("layout=" . $this->element->get_shm_seg_layout() . ", data=" . $pstr, 2))); throw new SegmentException("layout=" . $this->element->get_shm_seg_layout() . ", data=" . $pstr, 2); } else { $has_layout = true; } $woffset = -1; $has_space = false; // NOTE: concatenation of delimiter string needs to be done // for performance reasons when it is necessary $smr = new SharedMemoryReader($this->element); // get the lenght of the current data stored $seglen = $smr->getlen(); // segment type $segtype = $this->element->get_shm_seg_type(); // get an indexable segment version $xseg = $smr->get(); // unsetting the temporary element string $this->telement = ""; $this->log(__METHOD__ . ": slen=%, len=%, type=%, cws=%", array($seglen, strlen($smr->read()), gettype($smr->read()), preg_replace("/[\t ]+/", " ", preg_replace("/[\r\n]/", " ", var_export(count_chars($smr->read()), true))))); // get the offset for simply appending if ($key == -1) { // space required $required = StringUtil::get_offset_last($r . $d, $pstr) + strlen($r . $d); // offset for next entry to put //$woffset = $this->element->get_shm_seg_size() - $seglen; $woffset = $seglen; // check the space left/needed before writing // fifo segment if (strncmp($segtype, "fifo", 4) == 0) { // if the new fully entry does not fit into the segment we free some // space according to the segment strategy but only if the new entry // fits into the segment itself to avoid uneccessary flushing if the entry // is to big for the empty segment if (!($woffsef >= 0 && $woffset + $required <= $this->element->get_shm_seg_size()) && strlen($pstr) <= $this->element->get_shm_seg_size()) { // fifo, free oldest entries first $this->telement = $smr->read(); while (strlen($this->telement) > 0 && $woffset + $required > $this->element->get_shm_seg_size()) { // get the next entry to discard $discardoffset = StringUtil::get_offset_first($r . $d, $this->telement) + strlen($r . $d) - 1; // free $this->telement = substr($this->telement, $discardoffset); // recalc free space $woffset = $this->element->get_shm_seg_size() - strlen($this->telement); } $has_space = true; // otherwise there is something wrong with the space } else { $has_space = false; $this->log(__METHOD__ . ": %", array(new SegmentException("freeing failed", 0))); throw new SegmentException("freeing failed", 0); } // lifo segment } else { if (strncmp($segtype, "lifo", 4) == 0) { // stor segment } else { if (strncmp($segtype, "stor", 4) == 0) { // simply check if there is still place left to push elements if ($seglen + strlen($pstr) <= $this->element->get_shm_seg_size()) { $has_space = true; } else { $this->log(__METHOD__ . ": %", array(new SegmentException("no more space", 1))); throw new SegmentException("no more space", 1); } } } } // writing to a specific index is a bit more complex // get the offset for writing to an index } else { if (strncmp(gettype($key), "integer", 7) == 0 && $key > -1) { // if its a valid key if ($key <= count($xseg) - 1) { // calculate new minimum length for data stored $segnlen = $seglen - strlen($xseg[$key] . $this->element->get_shm_seg_var_eright() . $this->element->get_shm_seg_var_delimiter()) + strlen($pstr); // auto free space for fifo/lifo segment types if there is no more // space left // fifo segment if (strncmp($segtype, "fifo", 4) == 0) { // if the new segment data length is to big if ($segnlen > $this->element->get_shm_seg_size()) { $difflen = $segnlen - $this->element->get_shm_seg_size(); $tmp_key = 0; while ($difflen > 0) { $difflen -= strlen(trim($xseg[$tmp_key]) . $this->element->get_shm_seg_var_eright() . $this->element->get_shm_seg_var_delimiter()); unset($xseg[$tmp_key]); $tmp_key += 1; // increase it as unset doesn't modify indexes } } // after cleaning the fifo queue there should be enough space $has_space = true; // lifo segment } else { if (strncmp($segtype, "lifo", 4) == 0) { // if the new segment data length is to big if ($segnlen > $this->element->get_shm_seg_size()) { $difflen = $segnlen - $this->element->get_shm_seg_size(); $tmp_key = count($xseg) - 1; while ($difflen > 0) { $difflen -= strlen(trim($xseg[$tmp_key]) . $this->element->get_shm_seg_var_eright() . $this->element->get_shm_seg_var_delimiter()); unset($xseg[$tmp_key]); $tmp_key -= 1; // increase it as unset doesn't modify indexes } } // after cleaning the lifo queue there should be enough space $has_space = true; // if the segment type is 'stor' simply try to store it or throw an // exception if the length do not fit } else { if (strncmp($segtype, "stor", 4) == 0) { // if the modified length is less or equal the segment size // simply write it if ($segnlen <= $this->element->get_shm_seg_size()) { $has_space = true; } else { $has_space = false; $this->log(__METHOD__ . ": %", array(new SegmentException("no more space", 1))); throw new SegmentException("no more space", 1); } } } } } } else { $this->log(__METHOD__ . ": %", array(new ParamNotValidException(__METHOD__ . ": key(int)=" . var_export($key, true)))); throw new ParamNotValidException(__METHOD__ . ": key(int)=" . var_export($key, true)) . "\n"; } } // write it using shmop_write if ($has_layout && $has_space && $woffset > -1 && $this->element->get_shm_seg_id() > -1) { // TODO: WriterThread to allow multiple writes on different // but "correctly calced" locations/offsets $this->log(__METHOD__ . ": tel=%, tels=%, pstr=%, pstrs=%, els=%, " . "woff=%, woff+lpstr=%, segs-(woff+lpstr)=%", array($this->telement, strlen($this->telement), $pstr, strlen($pstr), $this->element->get_shm_seg_size(), $woffset, $woffset + strlen($pstr), $this->element->get_shm_seg_size() - ($woffset + strlen($pstr)))); // write modified prefix $written_prefix = shmop_write($this->element->get_shm_seg_id(), $this->telement, 0); // write additional entry from offset $written_data = shmop_write($this->element->get_shm_seg_id(), $pstr, $woffset); // write additional entry from offset // TODO: only write flush/whitespace pad for overlapping bytes at reduction $written_flush = shmop_write($this->element->get_shm_seg_id(), str_pad("", $this->element->get_shm_seg_size() - ($woffset + strlen($pstr)), " "), $woffset + strlen($pstr)); $this->log(__METHOD__ . ": wp=%, wd=%, wf=%", array($written_prefix, $written_data, $written_flush)); if ($written_prefix === false || $written_data === false || $written_flush === false) { $this->log(__METHOD__ . "%", array(new SegmentException($this->element, 4))); throw new SegmentException($this->element, 4); } } } // close if return $written_data; }