Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 4 of 4
  1. #1
    Senior Coder
    Join Date
    May 2006
    Posts
    1,680
    Thanks
    28
    Thanked 4 Times in 4 Posts

    Question about fopen and fsockopen

    Hi,

    I have been reading up on fsockopen, fopen file() and file_get_contents() and am not sure if I understand it properly.

    When we use these commands to download a web page, I assume that in reality PHP is ultimately using the same procedures (code) to connect to the remote server get the page. The different commands allow us access to different aspects of the procedure, correct ?

    So this means that when we use the fopen() to open a file stream on a web page, PHP looks after the sockets for us. So PHP opens a socket on a default port by itself. After all a connection via a socket will be needed. So which port does PHP use for this - is it 80 ?

    Then when we use file_get_contents(), again PHP must need to use both a socket and a handle, so presumably it uses fopensock() code (as above), then it also uses the fopen() code and creates a default handle where the second parameter is set to "rb" and it uses the fread() code to grab the contents by setting the bytes parameter to the filesize.

    So the file_get_contents() is a nice short cut because PHP is handling the necessary steps for us, but behind the scenes the same code is being used to set up a connection on a port, create a file stream and read the file.

    Is that about right ?

    Would appreciate it if anyone who knows about how this works would correct me or fill me in on the bits I am missing

    Thanks

  • #2
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,994
    Thanks
    4
    Thanked 2,662 Times in 2,631 Posts
    I think this is good to post here; PHP is open source. Anyway, in php\ext\standard I found file.c.
    file_get_contents():
    Code:
    PHP_FUNCTION(file_get_contents)
    {
    	char *filename;
    	int filename_len;
    	char *contents;
    	zend_bool use_include_path = 0;
    	php_stream *stream;
    	int len, newlen;
    	long offset = -1;
    	long maxlen = PHP_STREAM_COPY_ALL;
    	zval *zcontext = NULL;
    	php_stream_context *context = NULL;
    
    	/* Parse arguments */
    	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|br!ll",
    				  &filename, &filename_len, &use_include_path, &zcontext, &offset, &maxlen) == FAILURE) {
    		return;
    	}
    
    	if (ZEND_NUM_ARGS() == 5 && maxlen < 0) {
    		php_error_docref(NULL TSRMLS_CC, E_WARNING, "length must be greater than or equal to zero");
    		RETURN_FALSE;
    	}
    
    	context = php_stream_context_from_zval(zcontext, 0);
    
    	stream = php_stream_open_wrapper_ex(filename, "rb", 
    				(use_include_path ? USE_PATH : 0) | ENFORCE_SAFE_MODE | REPORT_ERRORS,
    				NULL, context);
    	if (!stream) {
    		RETURN_FALSE;
    	}
    
    	if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) {
    		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek to position %ld in the stream", offset);
    		php_stream_close(stream);
    		RETURN_FALSE;
    	}
    
    	/* uses mmap if possible */
    	if ((len = php_stream_copy_to_mem(stream, &contents, maxlen, 0)) > 0) {
    		
    		if (PG(magic_quotes_runtime)) {
    			contents = php_addslashes(contents, len, &newlen, 1 TSRMLS_CC); /* 1 = free source string */
    			len = newlen;
    		}
    
    		RETVAL_STRINGL(contents, len, 0);
    	} else if (len == 0) {
    		RETVAL_EMPTY_STRING();
    	} else {
    		RETVAL_FALSE;
    	}
    
    	php_stream_close(stream);
    	
    }
    file():
    Code:
    #define PHP_FILE_BUF_SIZE	80
    
    PHP_FUNCTION(file)
    {
    	char *filename;
    	int filename_len;
    	char *slashed, *target_buf=NULL, *p, *s, *e;
    	register int i = 0;
    	int target_len, len;
    	char eol_marker = '\n';
    	long flags = 0;
    	zend_bool use_include_path;
    	zend_bool include_new_line;
    	zend_bool skip_blank_lines;
    	php_stream *stream;
    	zval *zcontext = NULL;
    	php_stream_context *context = NULL;
    
    	/* Parse arguments */
    	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lr!", &filename, &filename_len, &flags, &zcontext) == FAILURE) {
    		return;
    	}
    	if (flags < 0 || flags > (PHP_FILE_USE_INCLUDE_PATH | PHP_FILE_IGNORE_NEW_LINES | PHP_FILE_SKIP_EMPTY_LINES | PHP_FILE_NO_DEFAULT_CONTEXT)) {
    		php_error_docref(NULL TSRMLS_CC, E_WARNING, "'%ld' flag is not supported", flags);
    		RETURN_FALSE;
    	}
    	
    	use_include_path = flags & PHP_FILE_USE_INCLUDE_PATH;
    	include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES);
    	skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES;
    
    	context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
    
    	stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, context);
    	if (!stream) {
    		RETURN_FALSE;
    	}
    
    	/* Initialize return array */
    	array_init(return_value);
    
     	if ((target_len = php_stream_copy_to_mem(stream, &target_buf, PHP_STREAM_COPY_ALL, 0))) {
     		s = target_buf;
     		e = target_buf + target_len;
     	
     		if (!(p = php_stream_locate_eol(stream, target_buf, target_len TSRMLS_CC))) {
     			p = e;
     			goto parse_eol;
    		}
    
     		if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
    			eol_marker = '\r';
     		}	
    
    		/* for performance reasons the code is duplicated, so that the if (include_new_line) 
    		 * will not need to be done for every single line in the file.
    		 */
    		if (include_new_line) {	
    	 		do {
     				p++;
    parse_eol:
     				if (PG(magic_quotes_runtime)) {
     					/* s is in target_buf which is freed at the end of the function */
     					slashed = php_addslashes(s, (p-s), &len, 0 TSRMLS_CC);
     					add_index_stringl(return_value, i++, slashed, len, 0);
     				} else {
     					add_index_stringl(return_value, i++, estrndup(s, p-s), p-s, 0);
    				}
     				s = p;
    	 		} while ((p = memchr(p, eol_marker, (e-p))));
    	 	} else {
    	 		do {
     				if (skip_blank_lines && !(p-s)) {
     					s = ++p;
     					continue;
     				}
     				if (PG(magic_quotes_runtime)) {
     					/* s is in target_buf which is freed at the end of the function */
     					slashed = php_addslashes(s, (p-s), &len, 0 TSRMLS_CC);
     					add_index_stringl(return_value, i++, slashed, len, 0);
     				} else {
     					add_index_stringl(return_value, i++, estrndup(s, p-s), p-s, 0);
    				}
     				s = ++p;
    	 		} while ((p = memchr(p, eol_marker, (e-p))));
    	 	}
     		
     		/* handle any left overs of files without new lines */
     		if (s != e) {
     			p = e;
     			goto parse_eol;
    		}
    	}
    
     	if (target_buf) {
     		efree(target_buf);
     	}	
    	php_stream_close(stream);
    }
    So yes, file and file_get_contents are literally the same, except one returns a string while the other returns an array.

    fsockopen on the other hand (located in fsoc.c, this has a passthrough from the php function call):
    Code:
    static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
    {
    	char *host;
    	int host_len;
    	long port = -1;
    	zval *zerrno = NULL, *zerrstr = NULL;
    	double timeout = FG(default_socket_timeout);
    	unsigned long conv;
    	struct timeval tv;
    	char *hashkey = NULL;
    	php_stream *stream = NULL;
    	int err;
    	char *hostname = NULL;
    	long hostname_len;
    	char *errstr = NULL;
    
    	RETVAL_FALSE;
    	
    	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lzzd", &host, &host_len, &port, &zerrno, &zerrstr, &timeout) == FAILURE) {
    		RETURN_FALSE;
    	}
    
    	if (persistent) {
    		spprintf(&hashkey, 0, "pfsockopen__%s:%ld", host, port);
    	}
    
    	if (port > 0) {
    		hostname_len = spprintf(&hostname, 0, "%s:%ld", host, port);
    	} else {
    		hostname_len = host_len;
    		hostname = host;
    	}
    	
    	/* prepare the timeout value for use */
    	conv = (unsigned long) (timeout * 1000000.0);
    	tv.tv_sec = conv / 1000000;
    	tv.tv_usec = conv % 1000000;
    
    	if (zerrno)	{
    		zval_dtor(zerrno);
    		ZVAL_LONG(zerrno, 0);
    	}
    	if (zerrstr) {
    		zval_dtor(zerrstr);
    		ZVAL_STRING(zerrstr, "", 1);
    	}
    
    	stream = php_stream_xport_create(hostname, hostname_len, ENFORCE_SAFE_MODE | REPORT_ERRORS,
    			STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hashkey, &tv, NULL, &errstr, &err);
    
    	if (port > 0) {
    		efree(hostname);
    	}
    	if (stream == NULL) {
    		php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to %s:%ld (%s)", host, port, errstr == NULL ? "Unknown error" : errstr);
    	}
    
    	if (hashkey) {
    		efree(hashkey);
    	}
    	
    	if (stream == NULL)	{
    		if (zerrno) {
    			zval_dtor(zerrno);
    			ZVAL_LONG(zerrno, err);
    		}
    		if (zerrstr && errstr) {
    			/* no need to dup; we need to efree buf anyway */
    			zval_dtor(zerrstr);
    			ZVAL_STRING(zerrstr, errstr, 0);
    		}
    		else if (!zerrstr && errstr) {
    			efree(errstr);
    		} 
    
    		RETURN_FALSE;
    	}
    
    	if (errstr) {
    		efree(errstr);
    	}
    		
    	php_stream_to_zval(stream, return_value);
    }
    It is different, but this is just a little above my head when it comes to php extensions.

    On the other hand, the fopen function looks to be chained pretty much directly to the fopen in c:
    Code:
    static FILE *zend_fopen_wrapper(const char *filename, char **opened_path)
    {
    	if (opened_path) {
    		*opened_path = estrdup(filename);
    	}
    	return fopen(filename, "rb");
    }
    Mkay, that one took a lot to find.
    PHP Code:
    header('HTTP/1.1 420 Enhance Your Calm'); 
    Been gone for a few months, and haven't programmed in that long of a time. Meh, I'll wing it ;)

  • #3
    Senior Coder
    Join Date
    May 2006
    Posts
    1,680
    Thanks
    28
    Thanked 4 Times in 4 Posts
    Thanks for the answer but unfortunately I don't know how to
    write or understand C so I can not interpret it to answer my posted questions.

    What I would like to know is when we use fopen file() and file_get_contents()
    does PHP handle the socket functions for us and if so what port does it use ?

    Thanks

  • #4
    God Emperor Fou-Lu's Avatar
    Join Date
    Sep 2002
    Location
    Saskatoon, Saskatchewan
    Posts
    16,994
    Thanks
    4
    Thanked 2,662 Times in 2,631 Posts
    While they don't appear to chain the same functions, I can't tell you for certain. I managed to track down the file/file_get_contents as far as this:
    Code:
    PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char **path_for_open, int options TSRMLS_DC)
    {
    	HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
    At which point I'm lost. Good old global variables; I have no idea where stream_wrappers is defined from. Url_stream_wrappers_hash is just a hashtable, and while stream_wrappers is also a hashtable, I don't know where it comes from.

    Logically I would say that the final functions would be the same, if the context is url based. To me, that makes sense as well; PHP shares a lot between itself in order to optimize, so I can't see writing the exact same functionality twice. On the filesystem I'd suspect that they use different underlying functions. fsockopen I would suspect is always considered external even if using an internal address.

    Now, from a PHP perspective, they are different. fsockopen can be used to create streams (which you can handle with you're own class if you like, browse http://www.php.net/manual/en/wrappers.php), while file_get_contents, file and fopen accept a stream context. If I read these functions right, the stream in fopen is used for persistence so it knows to not bother reopening it.
    When it comes to streams, this c is a little over my head too, but in all honesty I can't see it being something that should be of any concern when it comes to writing PHP code. Just be aware that file_get_contents and file can run into major memory issues while fsockopen and fopen shouldn't suffer from the same problems (aside from you're handling of the data of course).
    PHP Code:
    header('HTTP/1.1 420 Enhance Your Calm'); 
    Been gone for a few months, and haven't programmed in that long of a time. Meh, I'll wing it ;)


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •