+ public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+ if (
+ ( $flags & self::WRITE_ALLOW_SEGMENTS ) != self::WRITE_ALLOW_SEGMENTS ||
+ is_infinite( $this->segmentationSize )
+ ) {
+ return $this->doSet( $key, $value, $exptime, $flags );
+ }
+
+ $serialized = $this->serialize( $value );
+ $segmentSize = $this->getSegmentationSize();
+ $maxTotalSize = $this->getSegmentedValueMaxSize();
+
+ $size = strlen( $serialized );
+ if ( $size <= $segmentSize ) {
+ // Since the work of serializing it was already done, just use it inline
+ return $this->doSet(
+ $key,
+ SerializedValueContainer::newUnified( $serialized ),
+ $exptime,
+ $flags
+ );
+ } elseif ( $size > $maxTotalSize ) {
+ $this->setLastError( "Key $key exceeded $maxTotalSize bytes." );
+
+ return false;
+ }
+
+ $chunksByKey = [];
+ $segmentHashes = [];
+ $count = intdiv( $size, $segmentSize ) + ( ( $size % $segmentSize ) ? 1 : 0 );
+ for ( $i = 0; $i < $count; ++$i ) {
+ $segment = substr( $serialized, $i * $segmentSize, $segmentSize );
+ $hash = sha1( $segment );
+ $chunkKey = $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $hash );
+ $chunksByKey[$chunkKey] = $segment;
+ $segmentHashes[] = $hash;
+ }
+
+ $ok = $this->setMulti( $chunksByKey, $exptime, $flags );
+ if ( $ok ) {
+ // Only when all segments are stored should the main key be changed
+ $ok = $this->doSet(
+ $key,
+ SerializedValueContainer::newSegmented( $segmentHashes ),
+ $exptime,
+ $flags
+ );
+ }
+
+ return $ok;
+ }
+
+ /**
+ * Set an item
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+ * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+ * @return bool Success
+ */
+ abstract protected function doSet( $key, $value, $exptime = 0, $flags = 0 );
+
+ /**
+ * Delete an item
+ *
+ * For large values written using WRITE_ALLOW_SEGMENTS, this only deletes the main
+ * segment list key unless WRITE_PRUNE_SEGMENTS is in the flags. While deleting the segment
+ * list key has the effect of functionally deleting the key, it leaves unused blobs in cache.
+ *
+ * @param string $key
+ * @return bool True if the item was deleted or not found, false on failure
+ * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+ */
+ public function delete( $key, $flags = 0 ) {
+ if ( ( $flags & self::WRITE_PRUNE_SEGMENTS ) != self::WRITE_PRUNE_SEGMENTS ) {
+ return $this->doDelete( $key, $flags );
+ }
+
+ $mainValue = $this->doGet( $key, self::READ_LATEST );
+ if ( !$this->doDelete( $key, $flags ) ) {
+ return false;
+ }
+
+ if ( !SerializedValueContainer::isSegmented( $mainValue ) ) {
+ return true; // no segments to delete
+ }
+
+ $orderedKeys = array_map(
+ function ( $segmentHash ) use ( $key ) {
+ return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
+ },
+ $mainValue->{SerializedValueContainer::SEGMENTED_HASHES}
+ );
+
+ return $this->deleteMulti( $orderedKeys, $flags );
+ }