Notice that I used a PCRE preg function instead of a POSIX ereg one. The syntax for the two are somewhat different, that for the former being described in the manual.
The PCRE syntax is quite a bit more flexible (and the functions run faster), though if you whack a regexp written in it over the head enough you could force it into POSIX syntax. You wouldn't want to have to, though.
Looking at what I wrote, I realised I didn't need to group the contents of the <p> tag (in fact, I explictly say I'm not interested in it with the "?:"); I could have just said
"#<p.?>(.?)</p>#is"
Bit by bit:
'#' starts the regexp. Traditionally, '/' is used, but I'll be wanting to use that character in the regexp, and I'm too lazy to bother with escaping it.
'<p' Obviously starts the paragraph tag; maybe '<\s*?p' would be smarter, as there may be some whitespace between the '<' and the 'p' ('\s' is PCRE-speak for "whitespace character").
".*?>"
Match zero or more characters, and then a ">". Without the ? this would happily gobble the ">" character at the end of the <p> tag as well and just keep going, but we don't want to do that. The ? tells it to stop at the first ">" it finds.
Incidentally, if the regexp engine can't get a match if this part of the expressions stops at the first ">", it will try again by stopping it at the second ">".
"(.*?)<"
Again, match zero or more characters, this time as far as the first "<" it sees. This is the bit between the tags, which is what we're after, so I'll group it and store it.
The note I gave above is not so incidental now: The paragraph could contain other tags as well, so you'll want this part to match more and more "<" characters untill it sees one that is followed by:
"/p>"
Which is the end of the paragraph. If I'd used the traditional '/' characters to delimit the regexp, this bit would have had to read "\/p>".
"#is"
End of the regexp, followed by some flags that affect what matches what. 'i' for case-insenstive matching, 's' to indicate that "." should match newline characters as well.
preg_match_all() finds, as the name implies, all the matches of the supplied pattern in the given string, stashing them all into an array. I used $matches. $matches[0] contains an array which lists all strings found that match the entire regexp, while $matches[1] contains an array which lists all strings found that match the first "()"-grouped section.