/** * Get an element named $skey stored at $pos with value $val at this * position. * @param $index index addressing the element that should be gathered. * if the default values for the upcoming parameters are * overridden then additional checks for the element at * $index are performed. if the default index value is * not overridden then the upcoming parameters can be used * to perom a search. * @param $skey string key to lookup for an element in the shared memory * element * @param $pos positional parameter to look up $skey for * @param $val value to compare with the last serialized object of the * element adressable by $skey and $pos. if it is a string * then it needs to be the class string the object is an * serialized is an instance of * @param $full set to true if you want to get the complete element * found * @throws ParamNotValidException * @throws ReadAccessException * @return unserialized object if $full is set to false or the complete * string value for the element adressed by $skey, $pos and $val * stored in the shared memory segment pinned. In case no element * is found or an error occured an empty string is returned. */ public function get($index = -1, $skey = "", $pos = -1, $val = null, $full = true) { // TODO: only attach if all requirements present // attach to the shared memory segment first $this->attach(); // read ro,rw segments $found = ""; if ($this->get_shm_seg()->get_shm_seg_access_type() === 0 || $this->get_shm_seg()->get_shm_seg_access_type() === 2) { // try to read the element $shm_reader = new SharedMemoryReader($this->get_shm_seg()); $this->log(__METHOD__ . ": %", array($shm_reader)); if (strncmp(gettype($index), "integer", 7) == 0 && strncmp(gettype($skey), "string", 6) == 0 && strncmp(gettype($pos), "integer", 7) == 0 && strncmp(gettype($full), "boolean", 7) == 0) { $found = $shm_reader->read($index, $skey, $pos, $val, $full); } else { $this->log(__METHOD__ . ": %", array(new ParamNotValidException(__CLASS__ . "::get()" . ": index(int)=" . var_export($index, true) . ", skey(string)=" . var_export($skey, true) . ", pos(int)=" . var_export($pos, true) . ", val(any)=" . var_export($val, true) . ", full(bool)=" . var_export($full)))); throw new ParamNotValidExcepton(__CLASS__ . "::get()" . ": index(int)=" . var_export($index, true) . ", skey(string)=" . var_export($skey, true) . ", pos(int)=" . var_export($pos, true) . ", val(any)=" . var_export($val, true) . ", full(bool)=" . var_export($full)); } } else { $this->log(__METHOD__ . ": %", array(new ReadAccessException($this->get_shm_seg()))); throw new ReadAccessExcepton($this->get_shm_seg()); } // detach from the shared memory segment to allow other write ops $this->detach(); // return the element found return $found; }
/** * 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; }