[@TODO PHP-SRC 5.3] fgets 및fread 함수 분석

11789 단어
  • > fread:


  • 핵심 함수:_php_stream_read 핵심 소스 코드 단서:./ext/standard/file.c ./main/php_streams.h ./main/streams/streams.c
    먼저 구조체를 보십시오.
    struct _php_stream  {
        php_stream_ops *ops;
        void *abstract;         /* convenience pointer for abstraction */
    
        php_stream_filter_chain readfilters, writefilters;
    
        php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */
        void *wrapperthis;      /* convenience pointer for a instance of a wrapper */
        zval *wrapperdata;      /* fgetwrapperdata retrieves this */
    
        int fgetss_state;       /* for fgetss to handle multiline tags */
        int is_persistent;
        char mode[16];          /* "rwb" etc. ala stdio */
        int rsrc_id;            /* used for auto-cleanup */
        int in_free;            /* to prevent recursion during free */
        /* so we know how to clean it up correctly.  This should be set to
         * PHP_STREAM_FCLOSE_XXX as appropriate */
        int fclose_stdiocast;
        FILE *stdiocast;    /* cache this, otherwise we might leak! */
    #if ZEND_DEBUG
        int __exposed;  /* non-zero if exposed as a zval somewhere */
    #endif
        char *orig_path;
    
        php_stream_context *context;
        int flags;  /* PHP_STREAM_FLAG_XXX */
    
        /* buffer */
        off_t position; /* of underlying stream */
        unsigned char *readbuf;
        size_t readbuflen;
        off_t readpos;
        off_t writepos;
    
        /* how much data to read when filling buffer */
        size_t chunk_size;
    
        int eof;  // ps kimi:  eof  end of file  
    
    #if ZEND_DEBUG
        char *open_filename;
        uint open_lineno;
    #endif
    }; /* php_stream */
    

    주요 소스 코드:


    ./ext/standard/file.c:
    /* {{{ proto string fread(resource fp, int length)
       Binary-safe file read */
    PHPAPI PHP_FUNCTION(fread)
    {
        zval *arg1;
        long len;
        php_stream *stream;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &arg1, &len) == FAILURE) {
            RETURN_FALSE;
        }
    
        PHP_STREAM_TO_ZVAL(stream, &arg1);
    
        if (len <= 0) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length parameter must be greater than 0");
            RETURN_FALSE;
        }
    
        Z_STRVAL_P(return_value) = emalloc(len + 1);
        Z_STRLEN_P(return_value) = php_stream_read(stream, Z_STRVAL_P(return_value), len);
    
        /* needed because recv/read/gzread doesnt put a null at the end*/
        Z_STRVAL_P(return_value)[Z_STRLEN_P(return_value)] = 0;
    
        if (PG(magic_quotes_runtime)) {
            Z_STRVAL_P(return_value) = php_addslashes(Z_STRVAL_P(return_value),
                    Z_STRLEN_P(return_value), &Z_STRLEN_P(return_value), 1 TSRMLS_CC);
        }
        Z_TYPE_P(return_value) = IS_STRING;
    }
    /* }}} */
    

    ./main/streams/streams.c:
    PHPAPI size_t _php_stream_read(php_stream *stream, char *buf, size_t size TSRMLS_DC)
    {
        size_t toread = 0, didread = 0;
    
        while (size > 0) {
    
            /* take from the read buffer first.
             * It is possible that a buffered stream was switched to non-buffered, so we
             * drain the remainder of the buffer before using the "raw" read mode for
             * the excess */
            if (stream->writepos > stream->readpos) {
    
                toread = stream->writepos - stream->readpos;
                if (toread > size) {
                    toread = size;
                }
    
                memcpy(buf, stream->readbuf + stream->readpos, toread);
                stream->readpos += toread;
                size -= toread;
                buf += toread;
                didread += toread;
            }
    
            /* ignore eof here; the underlying state might have changed */
            if (size == 0) {
                break;
            }
    
            if (!stream->readfilters.head && (stream->flags & PHP_STREAM_FLAG_NO_BUFFER || stream->chunk_size == 1)) {
                toread = stream->ops->read(stream, buf, size TSRMLS_CC);
            } else {
                php_stream_fill_read_buffer(stream, size TSRMLS_CC);
    
                toread = stream->writepos - stream->readpos;
                if (toread > size) {
                    toread = size;
                }
    
                if (toread > 0) {
                    memcpy(buf, stream->readbuf + stream->readpos, toread);
                    stream->readpos += toread;
                }
            }
            if (toread > 0) {
                didread += toread;
                buf += toread;
                size -= toread;
            } else {
                /* EOF, or temporary end of data (for non-blocking mode). */
                break;
            }
    
            /* just break anyway, to avoid greedy read */
            if (stream->wrapper != &php_plain_files_wrapper) {
                break;
            }
        }
    
        if (didread > 0) {
            stream->position += didread;
        }
    
        return didread;
    }
    


  • > fgets:


  • 핵심 함수:_php_stream_get_line 핵심 소스 코드 단서:./ext/standard/file.c ./main/php_streams.h ./main/streams/streams.c

    주요 소스 코드:


    ./ext/standard/file.c:
       Get a line from file pointer */
    PHPAPI PHP_FUNCTION(fgets)
    {
        zval *arg1;
        long len = 1024;
        char *buf = NULL;
        int argc = ZEND_NUM_ARGS();
        size_t line_len = 0;
        php_stream *stream;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &arg1, &len) == FAILURE) {
            RETURN_FALSE;
        }
    
        PHP_STREAM_TO_ZVAL(stream, &arg1);
    
        if (argc == 1) {
            /* ask streams to give us a buffer of an appropriate size */
            buf = php_stream_get_line(stream, NULL, 0, &line_len);
            if (buf == NULL) {
                goto exit_failed;
            }
        } else if (argc > 1) {
            if (len <= 0) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length parameter must be greater than 0");
                RETURN_FALSE;
            }
    
            buf = ecalloc(len + 1, sizeof(char));
            if (php_stream_get_line(stream, buf, len, &line_len) == NULL) {
                goto exit_failed;
            }
        }
    
        if (PG(magic_quotes_runtime)) {
            Z_STRVAL_P(return_value) = php_addslashes(buf, line_len, &Z_STRLEN_P(return_value), 1 TSRMLS_CC);
            Z_TYPE_P(return_value) = IS_STRING;
        } else {
            ZVAL_STRINGL(return_value, buf, line_len, 0);
            /* resize buffer if it's much larger than the result.
             * Only needed if the user requested a buffer size. */
            if (argc > 1 && Z_STRLEN_P(return_value) < len / 2) {
                Z_STRVAL_P(return_value) = erealloc(buf, line_len + 1);
            }
        }
        return;
    
    exit_failed:
        RETVAL_FALSE;
        if (buf) {
            efree(buf);
        }
    }
    /* }}} */
    

    및 함수:
    PHPAPI char *php_stream_locate_eol(php_stream *stream, char *buf, size_t buf_len TSRMLS_DC)
    {
        size_t avail;
        char *cr, *lf, *eol = NULL; // ps kimi:  eol  end of line
        char *readptr;
    
        if (!buf) {
            readptr = stream->readbuf + stream->readpos;
            avail = stream->writepos - stream->readpos;
        } else {
            readptr = buf;
            avail = buf_len;
        }
    
        /* Look for EOL */
        if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) {
            cr = memchr(readptr, '\r', avail);
            lf = memchr(readptr, '
    ', avail); if (cr && lf != cr + 1 && !(lf && lf < cr)) { /* mac */ stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL; stream->flags |= PHP_STREAM_FLAG_EOL_MAC; eol = cr; } else if ((cr && lf && cr == lf - 1) || (lf)) { /* dos or unix endings */ stream->flags ^= PHP_STREAM_FLAG_DETECT_EOL; eol = lf; } } else if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) { eol = memchr(readptr, '\r', avail); } else { /* unix (and dos) line endings */ eol = memchr(readptr, '
    ', avail); } return eol; }

    ./main/streams/streams.c:
    /* If buf == NULL, the buffer will be allocated automatically and will be of an
     * appropriate length to hold the line, regardless of the line length, memory
     * permitting */
    PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen,
            size_t *returned_len TSRMLS_DC)
    {
        size_t avail = 0;
        size_t current_buf_size = 0;
        size_t total_copied = 0;
        int grow_mode = 0;
        char *bufstart = buf;
    
        if (buf == NULL) {
            grow_mode = 1;
        } else if (maxlen == 0) {
            return NULL;
        }
    
        /*
         * If the underlying stream operations block when no new data is readable,
         * we need to take extra precautions.
         *
         * If there is buffered data available, we check for a EOL. If it exists,
         * we pass the data immediately back to the caller. This saves a call
         * to the read implementation and will not block where blocking
         * is not necessary at all.
         *
         * If the stream buffer contains more data than the caller requested,
         * we can also avoid that costly step and simply return that data.
         */
    
        for (;;) {
            avail = stream->writepos - stream->readpos;
    
            if (avail > 0) {
                size_t cpysz = 0;
                char *readptr;
                char *eol;
                int done = 0;
    
                readptr = stream->readbuf + stream->readpos;
                eol = php_stream_locate_eol(stream, NULL, 0 TSRMLS_CC);
    
                if (eol) {
                    cpysz = eol - readptr + 1;
                    done = 1;
                } else {
                    cpysz = avail;
                }
    
                if (grow_mode) {
                    /* allow room for a NUL. If this realloc is really a realloc
                     * (ie: second time around), we get an extra byte. In most
                     * cases, with the default chunk size of 8K, we will only
                     * incur that overhead once.  When people have lines longer
                     * than 8K, we waste 1 byte per additional 8K or so.
                     * That seems acceptable to me, to avoid making this code
                     * hard to follow */
                    bufstart = erealloc(bufstart, current_buf_size + cpysz + 1);
                    current_buf_size += cpysz + 1;
                    buf = bufstart + total_copied;
                } else {
                    if (cpysz >= maxlen - 1) {
                        cpysz = maxlen - 1;
                        done = 1;
                    }
                }
    
                memcpy(buf, readptr, cpysz);
    
                stream->position += cpysz;
                stream->readpos += cpysz;
                buf += cpysz;
                maxlen -= cpysz;
                total_copied += cpysz;
    
                if (done) {
                    break;
                }
            } else if (stream->eof) {
                break;
            } else {
                /* XXX: Should be fine to always read chunk_size */
                size_t toread;
    
                if (grow_mode) {
                    toread = stream->chunk_size;
                } else {
                    toread = maxlen - 1;
                    if (toread > stream->chunk_size) {
                        toread = stream->chunk_size;
                    }
                }
    
                php_stream_fill_read_buffer(stream, toread TSRMLS_CC);
    
                if (stream->writepos - stream->readpos == 0) {
                    break;
                }
            }
        }
    
        if (total_copied == 0) {
            if (grow_mode) {
                assert(bufstart == NULL);
            }
            return NULL;
        }
    
        buf[0] = '\0';
        if (returned_len) {
            *returned_len = total_copied;
        }
    
        return bufstart;
    }
    

    좋은 웹페이지 즐겨찾기