Vendor/phpspreadsheet/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
private function writeSharedStringsTable()
    {
        // maximum size of record data (excluding record header)
        $continue_limit = 8224;

        // initialize array of record data blocks
        $recordDatas = [];

        // start SST record data block with total number of strings, total number of unique strings
        $recordData = pack('VV', $this->stringTotal, $this->stringUnique);

        // loop through all (unique) strings in shared strings table
        foreach (array_keys($this->stringTable) as $string) {
            // here $string is a BIFF8 encoded string

            // length = character count
            $headerinfo = unpack('vlength/Cencoding', $string);

            // currently, this is always 1 = uncompressed
            $encoding = $headerinfo['encoding'];

            // initialize finished writing current $string
            $finished = false;

            while ($finished === false) {
                // normally, there will be only one cycle, but if string cannot immediately be written as is
                // there will be need for more than one cylcle, if string longer than one record data block, there
                // may be need for even more cycles

                if (strlen($recordData) + strlen($string) <= $continue_limit) {
                    // then we can write the string (or remainder of string) without any problems
                    $recordData .= $string;

                    if (strlen($recordData) + strlen($string) == $continue_limit) {
                        // we close the record data block, and initialize a new one
                        $recordDatas[] = $recordData;
                        $recordData = '';
                    }

                    // we are finished writing this string
                    $finished = true;
                } else {
                    // special treatment writing the string (or remainder of the string)
                    // If the string is very long it may need to be written in more than one CONTINUE record.

                    // check how many bytes more there is room for in the current record
                    $space_remaining = $continue_limit - strlen($recordData);

                    // minimum space needed
                    // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character
                    // compressed:   2 byte string length length field + 1 byte option flags + 1 byte character
                    $min_space_needed = ($encoding == 1) ? 5 : 4;

                    // We have two cases
                    // 1. space remaining is less than minimum space needed
                    //        here we must waste the space remaining and move to next record data block
                    // 2. space remaining is greater than or equal to minimum space needed
                    //        here we write as much as we can in the current block, then move to next record data block

                    // 1. space remaining is less than minimum space needed
                    if ($space_remaining < $min_space_needed) {
                        // we close the block, store the block data
                        $recordDatas[] = $recordData;

                        // and start new record data block where we start writing the string
                        $recordData = '';

                    // 2. space remaining is greater than or equal to minimum space needed
                    } else {
                        // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below
                        $effective_space_remaining = $space_remaining;

                        // for uncompressed strings, sometimes effective space remaining is reduced by 1
                        if ($encoding == 1 && (strlen($string) - $space_remaining) % 2 == 1) {
                            --$effective_space_remaining;
                        }

                        // one block fininshed, store the block data
                        $recordData .= substr($string, 0, $effective_space_remaining);

                        $string = substr($string, $effective_space_remaining); // for next cycle in while loop
                        $recordDatas[] = $recordData;

                        // start new record data block with the repeated option flags
                        $recordData = pack('C', $encoding);
                    }
                }
            }
        }

        // Store the last record data block unless it is empty
        // if there was no need for any continue records, this will be the for SST record data block itself
        if (strlen($recordData) > 0) {
            $recordDatas[] = $recordData;
        }

        // combine into one chunk with all the blocks SST, CONTINUE,...
        $chunk = '';
        foreach ($recordDatas as $i => $recordData) {
            // first block should have the SST record header, remaing should have CONTINUE header
            $record = ($i == 0) ? 0x00FC : 0x003C;

            $header = pack('vv', $record, strlen($recordData));
            $data = $header . $recordData;

            $chunk .= $this->writeData($data);
        }

        return $chunk;
    }
private function writeSharedStringsTable()
    {
        // maximum size of record data (excluding record header)
        $continue_limit = 8224;

        // initialize array of record data blocks
        $recordDatas = [];

        // start SST record data block with total number of strings, total number of unique strings
        $recordData = pack('VV', $this->stringTotal, $this->stringUnique);

        // loop through all (unique) strings in shared strings table
        foreach (array_keys($this->stringTable) as $string) {
            // here $string is a BIFF8 encoded string

            // length = character count
            $headerinfo = unpack('vlength/Cencoding', $string);

            // currently, this is always 1 = uncompressed
            $encoding = $headerinfo['encoding'];

            // initialize finished writing current $string
            $finished = false;

            while ($finished === false) {
                // normally, there will be only one cycle, but if string cannot immediately be written as is
                // there will be need for more than one cylcle, if string longer than one record data block, there
                // may be need for even more cycles

                if (strlen($recordData) + strlen($string) <= $continue_limit) {
                    // then we can write the string (or remainder of string) without any problems
                    $recordData .= $string;

                    if (strlen($recordData) + strlen($string) == $continue_limit) {
                        // we close the record data block, and initialize a new one
                        $recordDatas[] = $recordData;
                        $recordData = '';
                    }

                    // we are finished writing this string
                    $finished = true;
                } else {
                    // special treatment writing the string (or remainder of the string)
                    // If the string is very long it may need to be written in more than one CONTINUE record.

                    // check how many bytes more there is room for in the current record
                    $space_remaining = $continue_limit - strlen($recordData);

                    // minimum space needed
                    // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character
                    // compressed:   2 byte string length length field + 1 byte option flags + 1 byte character
                    $min_space_needed = ($encoding == 1) ? 5 : 4;

                    // We have two cases
                    // 1. space remaining is less than minimum space needed
                    //        here we must waste the space remaining and move to next record data block
                    // 2. space remaining is greater than or equal to minimum space needed
                    //        here we write as much as we can in the current block, then move to next record data block

                    // 1. space remaining is less than minimum space needed
                    if ($space_remaining < $min_space_needed) {
                        // we close the block, store the block data
                        $recordDatas[] = $recordData;

                        // and start new record data block where we start writing the string
                        $recordData = '';

                    // 2. space remaining is greater than or equal to minimum space needed
                    } else {
                        // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below
                        $effective_space_remaining = $space_remaining;

                        // for uncompressed strings, sometimes effective space remaining is reduced by 1
                        if ($encoding == 1 && (strlen($string) - $space_remaining) % 2 == 1) {
                            --$effective_space_remaining;
                        }

                        // one block fininshed, store the block data
                        $recordData .= substr($string, 0, $effective_space_remaining);

                        $string = substr($string, $effective_space_remaining); // for next cycle in while loop
                        $recordDatas[] = $recordData;

                        // start new record data block with the repeated option flags
                        $recordData = pack('C', $encoding);
                    }
                }
            }
        }

        // Store the last record data block unless it is empty
        // if there was no need for any continue records, this will be the for SST record data block itself
        if (strlen($recordData) > 0) {
            $recordDatas[] = $recordData;
        }

        // combine into one chunk with all the blocks SST, CONTINUE,...
        $chunk = '';
        foreach ($recordDatas as $i => $recordData) {
            // first block should have the SST record header, remaing should have CONTINUE header
            $record = ($i == 0) ? 0x00FC : 0x003C;

            $header = pack('vv', $record, strlen($recordData));
            $data = $header . $recordData;

            $chunk .= $this->writeData($data);
        }

        return $chunk;
    }
private function writeSharedStringsTable ( ) { // maximum size of record data (excluding record header) $ continue_limit = 8224 ; // initialize array of record data blocks $ recordDatas = [ ] ; // start SST record data block with total number of strings, total number of unique strings $ recordData = pack ( 'VV' , $ this -> stringTotal , $ this -> stringUnique ) ; // loop through all (unique) strings in shared strings table foreach ( array_keys ( $ this -> stringTable ) as $ string ) { // here $string is a BIFF8 encoded string // length = character count $ headerinfo = unpack ( 'vlength/Cencoding' , $ string ) ; // currently, this is always 1 = uncompressed $ encoding = $ headerinfo [ 'encoding' ] ; // initialize finished writing current $string $ finished = false ; while ( $ finished === false ) { // normally, there will be only one cycle, but if string cannot immediately be written as is // there will be need for more than one cylcle, if string longer than one record data block, there // may be need for even more cycles if ( strlen ( $ recordData ) + strlen ( $ string ) <= $ continue_limit ) { // then we can write the string (or remainder of string) without any problems $ recordData .= $ string ; if ( strlen ( $ recordData ) + strlen ( $ string ) == $ continue_limit ) { // we close the record data block, and initialize a new one $ recordDatas [ ] = $ recordData ; $ recordData = '' ; } // we are finished writing this string $ finished = true ; } else { // special treatment writing the string (or remainder of the string) // If the string is very long it may need to be written in more than one CONTINUE record. // check how many bytes more there is room for in the current record $ space_remaining = $ continue_limit - strlen ( $ recordData ) ; // minimum space needed // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character // compressed:   2 byte string length length field + 1 byte option flags + 1 byte character $ min_space_needed = ( $ encoding == 1 ) ? 5 : 4 ; // We have two cases // 1. space remaining is less than minimum space needed //        here we must waste the space remaining and move to next record data block // 2. space remaining is greater than or equal to minimum space needed //        here we write as much as we can in the current block, then move to next record data block // 1. space remaining is less than minimum space needed if ( $ space_remaining < $ min_space_needed ) { // we close the block, store the block data $ recordDatas [ ] = $ recordData ; // and start new record data block where we start writing the string $ recordData = '' ; // 2. space remaining is greater than or equal to minimum space needed } else { // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below $ effective_space_remaining = $ space_remaining ; // for uncompressed strings, sometimes effective space remaining is reduced by 1 if ( $ encoding == 1 && ( strlen ( $ string ) - $ space_remaining ) % 2 == 1 ) { -- $ effective_space_remaining ; } // one block fininshed, store the block data $ recordData .= substr ( $ string , 0 , $ effective_space_remaining ) ; $ string = substr ( $ string , $ effective_space_remaining ) ; // for next cycle in while loop $ recordDatas [ ] = $ recordData ; // start new record data block with the repeated option flags $ recordData = pack ( 'C' , $ encoding ) ; } } } } // Store the last record data block unless it is empty // if there was no need for any continue records, this will be the for SST record data block itself if ( strlen ( $ recordData ) > 0 ) { $ recordDatas [ ] = $ recordData ; } // combine into one chunk with all the blocks SST, CONTINUE,... $ chunk = '' ; foreach ( $ recordDatas as $ i => $ recordData ) { // first block should have the SST record header, remaing should have CONTINUE header $ record = ( $ i == 0 ) ? 0x00FC : 0x003C ; $ header = pack ( 'vv' , $ record , strlen ( $ recordData ) ) ; $ data = $ header . $ recordData ; $ chunk .= $ this -> writeData ( $ data ) ; } return $ chunk ; }
Handling of the SST continue blocks is complicated by the need to include an
additional continuation byte depending on whether the string is split between
blocks or whether it starts at the beginning of the block. (There are also
additional complications that will arise later when/if Rich Strings are
supported).

The Excel documentation says that the SST record should be followed by an
EXTSST record. The EXTSST record is a hash table that is used to optimise
access to SST. However, despite the documentation it doesn't seem to be
required so we will ignore it.

@return string Binary data
Handling of the SST continue blocks is complicated by the need to include an additional continuation byte depending on whether the string is split between blocks or whether it starts at the beginning of the block . ( There are also additional complications that will arise later when / if Rich Strings are supported ) .