I oppose:

- all text coloring, fonts sizes help to express the importance of text. 

- text from right to left  is only useful for Hebrew or Arabic writing:  not my world

- special characters help a lot if you don't have them on your keyboard   ¿isn't it ? my 2 ¢

-  what's bad with smileys?  crying

I do spell checking with Grammarly. the embedded only confused me.

Source could need an improvement to wrap the text in the window. The single line display is cumbersome.

Still another approach using your original "solution" in PHP following the idea of a Micro-Service.

Instead of a mimic of what PHP might do I use it directly for this purpose.
That way more sophisticated functionalities that can be used without recoding.

I extended your test to include doubled double quotes

USER>write  %dstr
ABC Company,"123 Main St, Ste 102","Anytown, DC",10001,234-567-8901,"hi ""rcc"" was here"

USER>set reply=$$^phpCSV(%dstr) write reply,!! zwrite reply
ABC Company       123 Main St, Ste 102    Anytown, DC 10001   234-567-8901    hi "
rcc" was here
 
reply="ABC Company"_$c(9)_"123 Main St, Ste 102"_$c(9)_"Anytown, DC"_$c(9)_"10001"_$c(9)_"234-567-8901"_$c(9)_"hi ""rcc"" was here"

*

and here the code:

phpCSV(str) {       ; use PHP for conversion
#define php "............\php.exe "    ; add location of php.exe
#define pipe "|CPIPE|1"
  set file="myTest.php"
  open file:"WN" use file
  write "<?php "
        ,!,"$str='"_str_"';"
        ,!,"$strtotab = implode('\t', str_getcsv($str, ','));"
        ,!,"print_r($strtotab);"
        ,!,"?>",!
  close file
  open $$$pipe:$$$php_file
  use $$$pipe read result
  close $$$pipe
  use 0 ; write result
  quit $replace(result,"\t",$c(9))
}

The Unix/Linux world often uses LF := $C(10) as line terminator
while in Win (and VMS) world  CRLF := $C(C13,10) is a default.
 So you depend on the source system providing the data.

Suggested approach: use LF as the (common) line terminator  and   just drop $C(13) or   &#x0D; from your input record by

$replace($translate(record,$c(13)),"&#x0D;","")

Before any other processing.

OK, this handles double quotes. But only INSIDE a quoted string

parseCSV(string,newsep,sep=",",quote="""",newquote) {    ;adjust for flexible quoting
    set res="",newsep=$g(newsep,$c(9)),newquote=$g(newquote,quote)
    for  {
        if $g(string)[sep 
            if $e(string)=quote {
                set string=$replace(string,"""""",$c(2)) ; exclude double quotes
                set part=$P(string,quote,2)
                    ,string=$replace($p(string,part_quote_sep,2,*),$c(2),"""""")
                    ,res=res_newquote_$replace(part,$c(2),"""""")_newquote_newsep }
            else  
                set part=$P(string,sep),string=$p(string,sep,2,*)
                    ,res=res_part_newsep 
        else  
               set res=res_$g(string) quit }
        }
    quit res

}

-

HTH 

I discourage since years the use of $ZU(...) functions as they aren't documented since 2010. 
I recently had to dig back to 2009 for just a weak hint what might happen.

It is even worse with all the internal stuff around %occ* and similar.
No docs. No guaranty of the life cycle. No (external) notice of eventual changes. Mostly as a deployed code.

If it is used inside a $system.* or part of a generated code that's OK. The responsibility is not at the user side.

Verifying those "specials" with every release change can be a very heavy exercise. 
(just experiencing this on a system locked down to on an older version unable to migrate)

not being verbose in %occ* world I had this solution also allowing to change quoting
 

parseCSV(string,newsep,sep=",",quote="""",newquote) {    ;adjust for flexible quoting
  set res="",newsep=$g(newsep,$c(9)),newquote=$g(newquote,quote)
  for  
     if $g(string)[sep 
       if $e(string)=quote {
          set part=$P(string,quote,2),string=$p(string,part_quote_sep,2,*)
                ,res=res_newquote_part_newquote_newsep }
     else  
         
set part=$P(string,sep),string=$p(string,sep,2,*)
               ,res=res_part_newsep 
    else  
          set res=res_$g(string) quit }
     }
  quit res
}

BTW.
It's an excellent test exercise for new COS programmers
I'll add it to my collection.

Thanks   yes
 

OK.  for some reason the most important part of the link was truncated.

https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25CSP.BinaryStream

I hope it doesn't hide again.

The basic mistake happens here the definition of Request 
https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25CSP.Request

And you are right. %CSP.Stream has no Read method because  ContentType tells you the true object . 
As in the example:

It could have been %CSP.CharacterStream as well.

Both extend over some steps %GlobalStreamAdaptor which have all the READ, WRITE, ....methods

Just reading docs and not checking inherited methods (e.g. in Studio) is mostly misleading.