Getting books details by ISBN in PHP
Sometimes I love being a geek. Today, as I set out to inventory my collection of almost 500 books or so, I wanted to find a fast way to get the information on the book I wanted based on the books ISBN. This was because I was already getting tired of typing five books into the inventory (yes, I am that lazy) and I really did not want to keep typing out the authors name, the title and the subtitle for each book.
So I went to ISBNdb.com and entered an ISBN thinking I would be able to just copy and paste the book information from the output. That turned out to be overwhelmingly difficult since their output of the search is really convoluted. I knew what I wanted to get and they didn't offer that.
But they did offer an API and, after reading their docs, I realized that I could write a script that would take an ISBN and return to me the information I was looking for in a way that I could just copy and paste it. Remarkably, it was faster and easier than I thought it would be. I actually developed this little snippet, freely available for you here, in about 5 minutes. Hope it helps you in some way, if you are looking for such a thing.
NOTE: Before using this code make sure to sign up for your own API key. In order to do that you will need to register for an account with ISBNdb.com and then create a key. But as soon as you do you will have immediate access to their API. The API is simple, responding to the request with a simple XML output. Anyhow, without further ado, here is the little script I put together to fetch me the data I was looking for the way I wanted it.
<?php /** * Set this value to your own API key */ $apikey = '12345678'; /** * Initialize this var for use when forms are not posted */ $isbn = null; /** * Initialize the result set var */ $rs = null; /** * See if the form is posted */ if ($_SERVER['REQUEST_METHOD'] == 'POST') { /** * Get the ISBN from the form * * No, there is no validation on this. This * was for me and I knew I would not be at * all trying to trick my own script. USE * THIS AS IS AT YOUR OWN PERIL. */ $isbn = $_POST['isbn']; /** * Get the result as a SimpleXML object */ $rs = simplexml_load_file("http://isbndb.com/api/books.xml?access_key=$apikey&index1=isbn&value1=$isbn"); } ?> <html> <head><title>ISBN Check</title></head> <body> <form method="post" action="<?php echo basename(__FILE__) ?>"> <p> Enter an ISBN:<br /> <input type="text" size="40" name="isbn" id="isbn" value="<?php echo $isbn ?>" /> </p> <p> <input type="submit" name="submit" value="Search ISBN" /> </p> </form> <?php /** * Only output more stuff if there is a result */ if ($rs) : ?> <hr /> Results for ISBN: <?php echo $isbn ?>: <?php /** * Results for a book are in $rs->BookList->BookData * * You can see this using var_dump($rs); */ $book = $rs->BookList->BookData; ?> <p> <strong><?php echo $book->Title; ?></strong><br /> <?php echo !empty($book->TitleLong) ? $book->TitleLong . '<br />' : null; ?> <?php echo $book->AuthorsText; ?><br /> <?php echo $book->PublisherText; ?><br /> <?php echo $isbn; ?> </p> <?php endif; ?> </body> </html>
Typical output might look like (from one of the books I was checking on):
Results for ISBN: 0316116955:
The day the universe changed
James Burke
Boston : Little, Brown, c1985.
0316116955
I hope you enjoy this, if you happen to be looking for an ISBN search tool that you can run on your own.
A small cache routine in PHP
It has been a long time since I have posted anything PHP related so I decided that to end the week I would post something related to caching data to a file. File based cache's are probably not the sexiest thing around and more than likely not the best way to do things. But in my case I needed something light and small to house a small data set from a database server that was taking upwards of 35 seconds to return 20 rows of data.
I also only needed this cache in one place so in my original code I have it running a small procedural routine within a method. But I figured I would extend that process to be a little more flexible so I objectified it. Here is what I came up with:
NOTES:
- The backend data is coming from a database in my sample. It could legitimately come from any place that takes a long time to retrieve though.
- I am using a MySQL database backend in this example, driven by PDO
- The database handle is assumed to have been created and is retrieved from a registry object in the example
- The directory where the cache file will live will need to be writable by the web server
- There is very little error handling in this code. I will leave that up to you.
- Some of what is being done is tightly coupled internally and requires overwrites to change (path, filename, etc)
- I have provided precious few examples.
<?php /** * Small abstract class that handles the non-specifics of the cache routine */ abstract class SmallCache { /** * Directory path to the cache file * * @access protected * @var string */ protected $_cachePath; /** * Name of the cache file * * @access protected * @var string */ protected $_cacheFile; /** * Flag that checks to see if the file is writable * * @access protected * @var boolean */ protected $_cacheFileWritable = true; /** * Default expiration time of the cache * * @access protected * @var int */ protected $_cacheExpiry = 3600; /** * Right now * * @access protected * @var int */ protected $_currentTime; /** * Object constructor * * @access public * @param string $cacheFile Name of the file to write to * @param string $cachePath Path to the directory where the file will live * @param int $cacheExpiry Time in seconds the cache should stay alive */ public function __construct($cacheFile = null, $cachePath = null, $cacheExpiry = null) { $this->setPath($cachePath); $this->setFile($cacheFile); $this->setExpiry($cacheExpiry); $this->_currentTime = time(); } /** * Abstracted method that each child should implement */ abstract public function fetch(); /** * Simple sets a path if one is given * * NOTE: The path should end with a slash * * @access public * @param string $path The path to the directory where the cache files live */ public function setPath($path) { $this->_cachePath = $path ? rtrim($path, '\/') . DIRECTORY_SEPARATOR : realpath('.') . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR; } /** * Sets the cache file name * * @access public * @param string $file Name of the file to write and read the cache * @param boolean $useCachePath If true, will prepend the file name with the * cache path */ public function setFile($file, $useCachePath = false) { // If there is a file name given... if ($file) { // Prepend the path if it is being asked for if ($useCachePath) { $file = $this->_cachePath . $file; } // Set the name $this->_cacheFile = $file; } else { // Build a name $this->_cacheFile = $this->_cachePath . get_class($this) . '-cache-file.php'; } } /** * Sets the cache file expiration time duration * * @access public * @param int $expiry Time in seconds to keep the cache before dumping and * starting over */ public function setExpiry($expiry) { if ($expiry !== null) { $this->_cacheExpiry = intval($cacheExpiry); } } /** * Gets the current contents if there is one * * @access public * @return mixed Null if there is none, otherwise whatever is in the cache */ public function get() { // See if we need to make the temp file if (!file_exists($this->_cacheFile)) { // This will simply make an empty file for writing. Maybe. if (!touch($this->_cacheFile)) { $this->_cacheFileWritable = false; } // Send back something to be used by the child return null; } else { // Get the cached data return include_once $this->_cacheFile; } } /** * Writes data to the cache file * * @access public * @param string $data The contents of the cache file as a string */ public function put($data) { // Write it now if we can if ($this->_cacheFileWritable) { $fh = fopen($this->_cacheFile, 'w'); fwrite($fh, $data); fclose($fh); } } } // Simple sample implementation class SlowQueryCache extends SmallCache { public function fetch() { // Get the cache data $filedata = $this->get(); // Now see if we have a cache and if the cache is young enough, if not, make the cache and use the data now if ( ($filedata && $this->_currentTime - $filedata['timestamp'] > $this->_cacheExpiry) || !$filedata) { // Set the query - pretend it is a MySQL query $sql = 'call some_proc_that_might_take_long()'; /** * Get the result set * * In our case we are pretending to use PDO - substitute your own DB * handler code here * * We are also pretending to get our DB handle from a registry in * our library */ $db = Libregistry::getInstance()->get('objectdbmysql'); /** * Call the query * * I am leaving error handling up to you. */ $rs = $db->query($sql); // Fileinfo is what is going to go into the file $fileinfo = "<?php\n"; $fileinfo .= "\$data = array(\n\t'timestamp' => $this->_currentTime,\n\t'rows' => array(\n"; // File data is what would normally come from the cache $filedata['timestamp'] = $this->_currentTime; $filedata['rows'] = array(); // Clears out any old crap /** * Loop and build * * This will be entirely dependent on your data structure and how * you expect to access the data from within the cache */ while ($row = $rs->fetch()) { $fileinfo .= "\t\t'$row[id]' => array('name' => '$row[name]', 'age' => '$row[age]'),\n"; $filedata['rows'][$row['id']] = array('name' => $row['name'], 'age' => $row['age']); } $fileinfo .= "\t),\n);\nreturn \$data;"; // Write it now if we can $this->put($fileinfo); } // Send back the data we have return $filedata; } } // Tests $cache = new SlowQueryCache('cache/slowquery.php'); $data = $cache->fetch(); var_dump($data); ?>
Basically what this does is check to see if there is a cache file. If there is, it gets it and reads it and looks at the time written into the cache file to compare the current time minus the cache time against the expiry limit. If the cache is expired, the data is collected from the database, a result array is build and the result array is then written, as a string of PHP code to the cache file while the array data is returned.
If the cache has not yet expired then the original cache data is returned.
Keep in mind that this is still very tailored to my personal needs. But it should be easily enough transitioned to something more useful with a little tweaking. And yes, I know that this can be done in a better, more robust and sexier way. But this worked for me and I figured I'd share it in case it might work for you.
Coding clean should be a lifestyle
Dear rookie PHP coders/script kiddies/n00Bs: if you plan to use a variable or array index please remember to initialize them first or I will hunt you down and gut you like a pig. Kthnxbai.
I am in the middle of a redevelopment project right now that is pissing me off beyond imagination. The main reason is because the original developer of the codebase decided that initializing variables, checking existence of variables and checking existence of common array indexes was not necessary. And it got me thinking... coding clean should not just be a high priority, it should be a way of life for developers.
For those of you that may just be getting started in the big scary world of PHP development I want to offer a suggestion: before you even think of using a variable or array index either initialize it or check its existence.
What is initializing a variable or array index you ask? Simple. Declare it with a value. Like this:
<?php // Initialize a variable $myVar = null; // Initialize a variable as an array $myArray = array(); // Initialize an index $myArray['myIndex'] = null; ?>
That isn't that hard, is it? Now lets that this a step further and make sure that before we use something it is actually available to us, shall we?
<?php if (isset($myVar)) { // Do something based on $myVar being set } if (empty($myArray)) { // Do something based on $myArray being either not // set or set to a value equal to (boolean) false } ?>
Using empty() and isset() are two things you can do before using a variable. This allows you to not only check if a variable has been set but also allows you to check a specific empty value.
Something to take note of here is that isset() will still return false if you check a variable that has be assigned a value of 'null'. However, because it has been declared you can use the variable in conditionals and other expressions without worry of generating an undefined variable notice.
<?php $myVar = null; if (!$myVar) { // So something if $myVar evaluates to (boolean) false // And you don't have to worry about throwing errors now } ?>
If you plan on using a constant, which is not a variable, then you would use defined().
<?php define('ROBERTROCKS', true); if (defined('ROBERTROCKS') && ROBERTROCKS) { echo 'Man, Robert must rock'; } ?>
Moving into an area that is a tad more useful, there are times when your code is going to enter a loop or other construct (like a switch for example) and something done inside of that construct will set a variable that you will use after the construct closes. When this happens, make sure to initialize your var outside the construct so that if the construct never opens or is never entered you can still use the variable after the construct closes.
<?php /** * Make sure we have clean access to the variable * regardless of whether the construct is used */ $myVar = ''; while (loopRuns()) { $myVar .= getLoopVal(); } /** * Since we made this variable before entering * the construct we can be sure we can use it * without issue even if the loop never happens. */ echo $myVar; ?>
So to close this out let me point out a few things that you should never see in your code. Ever.
<?php /** * This is crap. Don't ever do this. If a request is made * to this page without a querystring then $_GET['id'] * will not be set and you will look like an ass for not * knowing how to code cleanly. */ $id = $_GET['id']; /** * This is another stupid. How do you know if the session * variable has been set? You don't, and if it isn't set you * will be notified. */ if ($_SESSION['hasVar']) $hasSessionVar = $_SESSION['hasVar']; /** * Last example of what not to do... never build a var * inside a construct for use outside the construct without * initializing it. */ switch ($myVar) { case 'crap': $newVar = 'This is crap'; break; case 'bull': $newVar = 'This is bull'; break; } echo $newVar; ?>
I hope this helps someone learn to code a little cleaner. And perhaps save you all sorts of curses and negativity by developers that might have to try to work on your code long after you stopped working on it.
PHP operators: double and single arrow
Someone recently asked a question on the Professional PHP Programmers Google Group that I thought was interesting. This question interested me for a few reasons:
- The PHP manual is a very good manual and usually covers things like this; and,
- I remember asking this very question when I first started developing.
Hi all, I’m new to the mailing list, and new to php, and new on programming.
Here I go:
I have a question but I was unable to find an answer for this on the web. Because I have no Idea how to ask this on a search engine.
What’s the meaning of -> and => on php?
As good as the PHP manual is, and as well documented as the operators in PHP are, the documentation on these two operators is lacking severely. Even naming is horribly lacking for these two operators, as a recent Twitter chat shows:
- RobertGonzalez: Ok PHP development peeps, what is the official name of the "=>" operator? I have been calling the array assignment operator.
- daNanner: RT @RobertGonzalez: Ok PHP development peeps, what is the official name of the "=>" operator? I have been calling the array assignmen ...
- RobertGonzalez: And while we are at it, what is the '->' operator officially called. I thought it was scope resolution, but it seems that :: is actually SR.
- isa247: @RobertGonzalez associative array operator thingamabob
- isa247: @RobertGonzalez the object operator whatchamacallit
- elazar: @RobertGonzalez That's a damned good question. WTF *is* the -> operator called? Doesn't seem to be in the PHP manual.
- montythestrange: @elazar @RobertGonzalez I usually go with "dereference operator", but when saying "$foo->bar()" I'd say "foo pointer bar"
- isa247: @RobertGonzalez ummm... but I could be wrong, I just asked my php guy and he told me 'an arrow pointing to the right' lol
- ideogon: @RobertGonzalez I believe that :: is "paamayim nekudatayim"? Trying to wing that from memory w/o Google. Hebrew for "double colon"?
- BinaryKitten: RT @elazar: @RobertGonzalez That's a damned good question. WTF *is* the -> operator called? Doesn't seem to be in the PHP manual.
- ideogon: @RobertGonzalez I was close! http://tinyurl.com/z87sg
- RobertGonzalez: @elazar For arrays, from the manual: "The parameters can be given an index with the => operator." Even they can't name it.
- RobertGonzalez: @elazar The object arrow, though, have no idea. Can't find that anywhere.
- RobertGonzalez: @montythestrange I have heard dereference used before. Maybe I can use that.
- RobertGonzalez: @isa247 Yeah, my coworker just called it the "equals sign with a greater than sign next to it" operator.
- RobertGonzalez: @ideogon Yes, the :: is named paamayim nekudatayim. But it is also called the scope resolution operator in the manual.
- montythestrange: @RobertGonzalez Although technically the dereference operator is something completely different in C++, since it actually has real pointers.
- RobertGonzalez: @ideogon http://tinyurl.com/5dksw4 For Paamayim nekudotayim/Scope Resolution.
- NetDiva: @robertgonzalez => as an operator is greater than/equal to. It's also used for to assign array values to keys. -> is the OOP arrow operator.
- NetDiva: @robertgonzalez Was that what you were asking?
- RobertGonzalez: @NetDiva I was asking for the official names of the "=>" and "->" operators in PHP.
- RobertGonzalez: @rasmus Dude, do you know the official names of the "=>" and "->" operators in PHP?
- RobertGonzalez: @NetDiva And I think greater than or equal to is >=. At least in PHP.
- rasmus: @RobertGonzalez Don't think we ever really named them. Something like 'associate' and 'element of' perhaps, but I am just making it up
- thynctank: @RobertGonzalez as it is in most languages
- thynctank: @RobertGonzalez isn't `->` the deference operator? Oh wait, that's C with actual pointers
- RobertGonzalez: @thynctank Yeah. Tried that, didn't seem to fit
- RobertGonzalez: @rasmus So can I name them then, since they don't have a name yet?
- barryaustin: @RobertGonzalez @elazar PHP token reference here: http://us.php.net/tokens
- elazar: @RobertGonzalez I'm going with @BarryAustin on this one: when in doubt, Use the Source.
-> object operator, => double arrow operator. - RobertGonzalez: @barryaustin So then double array "=>" and object operator "->"?
- RobertGonzalez: @elazar from @rasmus: Don't think we ever really named them. Something like 'associate' and 'element of' perhaps, but I am just making it up
- RobertGonzalez: @elazar But sounds logical. I have used the term object operator before, but always referred to double arrow as array assignment.
- elazar: @RobertGonzalez *shrug* They're named as tokens in the source, so apparently somebody did.
- jlleblanc: @RobertGonzalez @elazar @rasmus Although not an operator, I have to give @barryaustin credit for helping us name the 'bracketurd'
- leleu: @RobertGonzalez if you find those oper names, please RT them. Wiki says -> is "member by pointer", at least in C++
- RobertGonzalez: @elazar True. I am down. I'll use the token names.
- RobertGonzalez: @barryaustin Thanks for the linkage dude. Helped put me in the right direction.
- RobertGonzalez: RT @barryaustin: @RobertGonzalez @elazar PHP token reference here: http://us.php.net/tokens #unknownphparrowoperatornames
- sweatje: @RobertGonzalez I sometimes call => associative operator from associative array. -> I call the object operator.
- RobertGonzalez: @sweatje Thanks dude. I am thinking object operator seems to be agreeable.
- RobertGonzalez: Alrighty then. Thanks for everyone's help on my crazy PHP arrow naming fiasco of ought-nine. Goodnight for now.
More on the operators used in PHP can be found at http://us3.php.net/manual/en/language.operators.php. Unfortunately, these operators will not be found there. So to assist in explaining what these operators are and what they do, lets dig a little deeper in the world of cryptic symbols that remain unnamed in PHP. Shall we?
The double arrow operator, "=>", is used as an access mechanism for arrays. This means that what is on the left side of it will have a corresponding value of what is on the right side of it in array context. This can be used to set values of any acceptable type into a corresponding index of an array. The index can be associative (string based) or numeric. So if I have an array like:
<?php $myArray = array('Big', 'Small', 'Up', 'Down'); ?>
Then in effect the code is saying:
<?php $myArray = array( 0 => 'Big', 1 => 'Small', 2 => 'Up', 3 => 'Down' ); ?>
This is not a real surprise seeing as arrays in PHP are numerically indexed, zero-based arrays by default. But what if you wanted a hash of keys and values? Like when passing something as a form or in a querystring or reading results from a database? Well then, you would want to associate a key to a value in the array, or something like:
<?php $myArray = array( 'Robert' => 'Big', 'Bobby' => 'Smart', 'PHP' => 'Rocks' ); ?>
Now you have a hash of values assigned to indeces using the double arrow, or array value assignment, operator ("=>").
The object operator, "->", is used in object scope to access methods and properties of an object. It's meaning is to say that what is on the right of the operator is a member of the object instantiated into the variable on the left side of the operator. Instantiated is the key term here. We'll talk about that a little more in just a bit.
If you come from a background in object oriented programming then you know that every object has a way to access the methods and properties of the instantiated object. In most languages that is the dot operator ("."). In PHP the dot operator has completely different meaning (it is used to concatenate strings) so don't use that for object scope resolution. Instead, after instantiating an object, access its methods and properties using the object operator, like this:
<?php $obj = new MyObject(); // Create a new instance of MyObject into $obj $obj->thisProperty = 'Fred'; // Set a property in the $obj object called thisProperty $obj->getProperty(); // Call a method of the $obj object named getProperty ?>
Now, getting back to object basics, remember I said that instantiated was a key term? The reason for that term is because there is another operator related to scope resolution than can be used but it is neither an arrow nor can it be used on objects. It can only be used on classes (this is as of PHP 5 - since PHP4 has already been fed to the dogs we are only going to talk about PHP 5). Class scope resolution is identified by the Paamayim Nekudotayim, or double colon, operator ("::").
If you use a static class or static methods or properties within a class, you would not use the object operator ("->") but would instead use the scope resolution, or double colon, operator, like so:
<?php MyClass::$staticProperty = 'Fred'; // Sets a values into the static class MyClass::getStaticProperty(); // Gets a value from a static method ?>
Now that you have the power of operators at your fingertips, even if in a very basic sense, you can get busy with assigning and accessing goodies in arrays, objects and classes. And remember as you use these that nothing at all can replace the learning you would get from trying them out, especially when you try to use them the wrong way (error messages can be quite educational).
If I could leave you with one bit of advice it would be to get your hands dirty on some of them and see what the parser tells you when it chokes on one (or more) of them.
And have fun. Stepping in to object oriented programming can be a frustrating, painful experience for some. But it is definitely a rewarding one.
Never try to change anything using WordPress.com
A few weeks ago I learned of a cool little statistics package that you can install on your WordPress blog that does a really good job of managing your stats. So I installed it here on this blog to see what people are searching for, reading and generally interested in and tied it an API key from WordPress.com account that I have used for my Akismet (anti-spam) plugin.
A few days later I realized that the WordPress.com account I have been using for Akismet was tied to my old business profile name, Everah, and not my newer, more open identity of RobertGonzalez. So I decided to change the API key I am using for Akismet and Stats to the API key for my personal account rather than my business account. And that, my friends, is wherein my biggest pain in the butt for the last two weeks has resided.
It started with the fact that there is really not a clear set of instructions for changing your Stats API key. Unlike Akismet, where you can go to Plugins -> Akistmet Configuration from inside your admin panel and change your API key, Stats requires that you deactivate it as a plugin first, then reactivate it then go through all the rigmarole of setting it all back up again. After finding the instructions to do that - thanks to Google and a lot of pissed of WordPress.com Stats users - I deactivated the plugin and reactivated it again, setting it up this time with my personal API key. Which, in my case didn't, seem work.
So after playing around with this forever I decided that it was time to submit a help ticket to the WordPress crew. The context of the ticket was pretty simple, I wanted to swap API keys between my personal and business accounts on their end, which in my opinion, would have been the easiest thing to do. Their support response, looking back on it now, were actually pretty good, except they did not answer my questions and actually led to more questions on top of the ones I had. When you get to the end of it all you will see that their initial suggestion (or second one) is in fact what I ended up doing. But it was a nightmare going through all of this.
The following is the transcript of all of our conversations (skip this and continue on the other side of it):
from: Anthony - WordPress.com
to: robert@robert-gonzalez.com
date: Sat, Feb 7, 2009 at 7:15 AM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thHi,
You cannot move API keys around. If you wish for us to transfer a blog to a different account, we can do that: http://support.wordpress.com/moving-a-blog/#transferring-your-blog-to-another-user-or-accountBest,
Anthony
Automattic | WordPress.com
from: Robert Gonzalez
to: support@wordpress.com
date: Sat, Feb 7, 2009 at 10:05 AM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thWhat I really want is to use the API key and history from the "everah" account only have it under the "robertgonzalez" account. My self hosted blog has a pretty decent Akismet history from the API key associated with the everah account and I just installed the stats plugin. I really want to not have my self hosted blog associated with the "everah" account at all and would prefer to have everything run from the "robertgonzalez" API key.
Should I just abandon the history I have with the everah account and start all over with the robertgonzalez account?
from: Anthony - WordPress.com
to: robert@robert-gonzalez.com
date: Sat, Feb 7, 2009 at 10:51 AM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thHi,
I can add the robertgonzalez account to the self-hosted blog's access list from this end - so that it can access the blog stats. If this is what you would like, please verify the blog URL.Best,
Anthony
Automattic | WordPress.com
from: Robert Gonzalez
to: support@wordpress.com
date: Sat, Feb 7, 2009 at 7:38 PM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thDoes that mean if I switch the Akismet and WordPress.com Stats API key then the data that has been collected so far would transfer as well? What I really would like is to not lose the Akismet stats I have collected under the everah account. Both everah and robertgonzalez have the same email and same URL for the blog address (http://www.robert-gonzalez.com) which is my self-hosted blog.
from: Nick - WordPress.com
to: robert@robert-gonzalez.com
date: Wed, Feb 11, 2009 at 6:33 AM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thHi,
The stats aren't stored with the account, they are stored with the blog. So giving another user access doesn't do anything to the stats.
--
Nick
Automattic | WordPress.com
from: Robert Gonzalez
to: support@wordpress.com
date: Wed, Feb 11, 2009 at 7:20 AM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thI tried that, switching the API key from the everah API key to the robertgonzalez API key and my stat count dropped from thousands down to 8 for akismet. Both accounts have my self hosted blog url registered to it. That is why I thought that switching was going to cause stat trouble.
So if I change the API key then theoretically nothing should change as long as the blog is registered to the account?
Robert
from: Nick - WordPress.com
to: robert@robert-gonzalez.com
date: Wed, Feb 11, 2009 at 7:45 AM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thCorrect. You mention Akismet, but Akismet isn't blog stats, it's for blocking spam on your blog.
--
Nick
Automattic | WordPress.com
from: Robert Gonzalez
to: support@wordpress.com
date: Wed, Feb 11, 2009 at 8:56 AM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thRight, by my akismet numbers changed too when I changed the API key which led me to believe that the API key was holding the stats, not the blog URL. Also, when I switched the API keys my stats basically vanished and I was told that I didn't have access to the stats. I am guessing that had something to do with me being logged in to WordPress.com under everah while trying to hook into the robertgonzalez api key.
Really I just want everything to roll into one account: robertgonzalez. But I have history with the everah account that I want to make sure I don't lose. If I need to massage the stats and akismet stuff though I guess I can.
Also, when are the developers of the stats plugin going to offer the ability to change the API key like Akisment does? Right now you either have to change the API key in akismet or, short of that, edit the wordpress_api_key config value to change it for stats.
from: Sheri - WordPress.com
to: robert@robert-gonzalez.com
date: Wed, Feb 11, 2009 at 4:42 PM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thHi,
Akismet should work the same even if the numbers are different because it's collective. Are you saying that spam is getting blocked differently if you switch the API key?Also note that there is no way to look up Akismet stats on the wordpress.com system, so it really doesn't matter which wordpress.com account you connect Akismet to from our point of view.
--
Sheri
Automattic | WordPress.comhttp://support.wordpress.com/
http://en.forums.wordpress.com/
from: Robert Gonzalez
to: support@wordpress.com
date: Wed, Feb 11, 2009 at 5:03 PM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thWhat I am saying is right now, under Akismet stats, using the API key from the "everah" account, I see this:
4,396 spams caught, 228 legitimate comments, and an overall accuracy rate of 99.805%.When I change my API key in the akismet admin screen to the API key for the "robertgonzalez" account, that line becomse something like:
8 spams caught, 0 legitimate comments, and an overall accuracy rate of 100.00%.That is why I asked about the API keys. Also, when I switched the API key in akismet, the wordpress.com stats plugin seemed to go haywire. Like it didn't know who I was or something.
I just want to make sure that the few days of stat harvesting and the last few months of akismet data harvesting doesn't get whacked with a single change to the API key.
from: Sheri - WordPress.com
to: robert@robert-gonzalez.com
date: Wed, Feb 11, 2009 at 5:17 PM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thThe data cannot be transferred between wordpress.com accounts. If you want to keep the same Akismet numbers showing in your blog, then you must use the first API key. If you want to change it, then it should not effect the future accuracy of the spam blocking, you will just see different numbers. It's up to you if you whether to switch it or not.
When you change your API key for Akismet, it should not affect the stats. However, if you switch the API key for stats, make sure to select the "replace an existing blog" option in step 2.
--
Sheri
Automattic | WordPress.comhttp://support.wordpress.com/
http://en.forums.wordpress.com/
from: Robert Gonzalez
to: support@wordpress.com
date: Wed, Feb 11, 2009 at 9:39 PM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thHow do you unregister a blog from wordpress.com? I just tried switching API keys and I think since http://www.robert-gonzalez.com is registered to my "everah" account I cannot associate it with my "robertgonzalez" account. I keep getting this error when I input my API key:
WordPress.com Stats
Error from last API Key attempt:Missing API Key
The WordPress.com Stats Plugin is not working because it needs to be linked to a WordPress.com account.
Enter your WordPress.com API key to link this blog to your WordPress.com account. Be sure to use your own API key! Using any other key will lock you out of your stats. (Get your key here.)
This is after I enter the API key and hit the "Add new blog" button. What do I do now?
from: Nick - WordPress.com
to: robert@robert-gonzalez.com
date: Thu, Feb 12, 2009 at 6:14 PM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thIf you log into WordPress.com with the 'everah' account and then visit http://support.wordpress.com/contact/ to send us a request, we can change the owner of the WordPress.com stats for the domain. Make sure to let us know the domain name and the user you want to have access.
--
Nick
Automattic | WordPress.com
from: Robert Gonzalez
to: support@wordpress.com
date: Wed, Feb 18, 2009 at 10:53 AM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thI can't submit a contact to you using the link below because the only blog attached to that account is my self hosted blog and your ticket system will not accept messages from blogs that are not hosted on your site.
This is a monumental pain in the ass. I really wish you would fix this. Seriously, it shouldn't be this freaking hard to make a simple change.
from: Nick - WordPress.com
to: robert@robert-gonzalez.com
date: Thu, Feb 19, 2009 at 6:44 AM
subject [WordPress #252013]: General - That I really only need one. But I cannot merge thThere may be a bug in our system them. Because it should let you message us about a blog connected to our system through WordPress.com stats. Is the form giving you an error?
--
Nick
Automattic | WordPress.com
from: Robert Gonzalez
to: support@wordpress.com
date: Thu, Feb 19, 2009 at 9:40 AM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thYes. Here are the steps I took:
1. Log in to WordPress.com using the account "everah".
2. Go to http://support.wordpress.com/contact/
3. Fill out the form and leave the Blog URL field empty
4. SubmitError: Let us know the URL of your blog.
I then select the only Blog URL that is on my account, http://www.robert-gonzalez.com and submit...
Error: We only provide support for WordPress.com blogs here. Please go to the WordPress.org Support Forum for further help.
from: Robert Gonzalez
to: support@wordpress.com
date: Thu, Feb 19, 2009 at 9:45 AM
subject Re: [WordPress #252013]: General - That I really only need one. But I cannot merge thYou know, I was just reading the tips above the form and #3 says:
We only support WordPress blogs hosted here on WordPress.com. If your blog is running the self-hosted version of WordPress, you should seek support elsewhere.So dude, what the hell? Am I stuck in the position I am in?
Ultimately what I needed to do was remove this blog's URL from my business account, where it was primary, and move it to my personal account as the primary blog. But I had to have some way of doing that so I could request it through their system. So I had to get crafty, tricksy if you will, so I could circumvent the stupid trouble ticket form on their web site. Wanna know how I did it?
What I finally ended up doing was creating a new WordPress.com blog under my "everah" username so I could submit a help ticket to the crew. I then logged into that account and sent them a trouble ticket in which I told them to reference this long string of emails that we vollied to one another and asked that they just move this blog's url from one account to the other. Which is what they suggested in the first place.
They did it. And of course, now stats work properly, but Akismet is now down to nothing again, so I ended up going back to API key from my business for that. But at least stats are working. And at least I was able to get this situation mostly resolved. But if there is one thing I would suggest you never do, it is try to change anything on your own blog and then ask for help from the WordPress.com crew to make it happen smoother.
Building the PHP MS SQL Server extension from source on Ubuntu 8.10
Yesterday I wrote about how I got an enhanced Sybase CT extension for PHP built on Ubuntu 8.10 from source files and from Red Hat RPM archives. Today I wanted to write about installing the SQL Server extension from source on Ubuntu 8.10.
I have written about this once before but it was for a Red Hat environment. Seeing as we are converting all of our system at work to Ubuntu I had to do the same thing for the Ubuntu platform. And wouldn't you know it, Debian and Red Hat are vastly different from each other.
Aside from the stupidity that is the Ubuntu package manager's rendition of the SQL Server extension for PHP the fact remained for me that I had to be able to integrate database communication from LAMP to a SQL Server on Windows as well to a Sybase server on Unix. The Sybase bit was taken care of in yesterday's write up. But how do we get PHP to talk to SQL Server on Ubuntu 8.10?
Throw out the notion that you can use the php-sybase package from the package manager. It munges stuff up pretty badly and at the same time does things to the Sybase extension that make it unusable for my needs. Not to mention that if their is a Sybase extension already installed then it really pukes because the php-sybase extension that installs the SQL Server functions actually installs the mssql_* functions then maps the sybase_* functions to them, which makes PHP squawk since those functions are already defined.
So the only way to make this happen is to build the SQL Server extension from source.
Building and installing the mssql extension from source
The follow steps will guide you through how I got the mssql extension built and installed on Ubuntu 8.10:
- Build the FreeTDS library
FreeTDS is a free, open source library of clients that allow Linux machines to talk to SQL servers and Sybase servers. Download the latest stable version of the FreeTDS library (as of this writing it was 0.82) and unpack the source to a directory on your machine. Once the source is unpacked, change to the directory where the source is located and configure and make the library:
$ ./configure --prefix=/usr/local/freetds --enable-msdblib
$ make
$ sudo make installDO NOT MAKE CLEAN AT THIS POINT.
Before you do anything else you must make sure that you have copied over all the necessary files from FreeTDS. "Wait, didn't the installation routine do that?" you might ask. No, it doesn't, because of a change made on the part of the FreeTDS developers. There are two files that are needed in order to build the mssql extension. Those files are among the make files and must be copied over to the freetds install directory.
From inside the source directory of freetds, where you built it from, enter:
$ sudo cp include/tds.h /usr/local/freetds/include
$ sudo cp src/tds/.libs/libtds.a /usr/local/freetds/lib - Get the PHP source code
Change to a directory where you wouldn't mind having the entirety of the PHP source stashed. From the command line type:
$ apt-get source php5 - Copy the mssql extension files to a directory you can build from
My preference is to leave source alone and work from copies. So I always copy the files I need to a different location and build from the copies. To that end, I created a directory at ~/SourceCode/php-mssql and copied the following files from the original PHP source directory:
$ cp ext/mssql/config.m4 ~/SourceCode/php-mssql
$ cp ext/mssql/php_mssql.c ~/SourceCode/php-mssql
$ cp ext/mssql/php_mssql.h ~/SourceCode/php-mssql - Make and install the extension
Make sure you have the php5-dev package installed on your system so that you can build PHP extensions from source. From inside the directory where your mssql extension source code is, at the command line, enter:
$ phpize
$ ./configure --with-mssql=/usr/local/freetds
$ make
$ make installFind out where your extensions directory is on your machine and quickly check it to make sure there is a php_mssql.so file living in it. On my machine the extension directory is /usr/lib/php5/20060613+lfs/. Yours may be different.
- Configure PHP to use the new extension we just made
Now we need to tell PHP to use the new extension we just built. To do that we need to create an ini file for the extension and put it inside of the extensions directory where PHP can find it. On my machine PHP looks for ini files to parse in /etc/php5/conf.d/ so naturally that is where I am going to go to to tell PHP to use this extension.$ cd /etc/php5/conf.d/Now we need to create an ini file and put into a directive to load the extension. You can use whatever editor you like. I prefer to use vim:
$ sudo vim mssql.iniInside this file place the following two lines:
; Enable the mssql extension
extension=mssql.so - Configure your environment to load a much needed environment variable whenever the machine starts
Much like the Sybase extension we did yesterday the mssql extension needs an environment variable in order to function properly. Again, this one caused me fits for a long time in Ubuntu. To be sure you can use the SQL Server extension from both the CLI and the web server you will need to add an environment variable to both the /etc/profile startup script AND the web servers environment variable setting script.$ sudo vim /etc/profileAt the end of the file add:
export FREETDSCONF=/etc/freetds/freetds.confNow add this same entry into your web server’s environment variables. I am using apache and assuming you are to. If not, consult your web server’s documentation for how to do this:
$ sudo vim /etc/apache2/envvarsNow add these entries to the end of the file:
export FREETDSCONF=/etc/freetds/freetds.conf - Configure FreeTDS
In order for FreeTDS to communicate properly with the SQL server a DSN of sorts needs to be created. To make a DSN you need to edit the freetds.conf file:
$ sudo vim /etc/freetds/freetds.confGo to the end of the file and add the following lines (MAKE SURE TO INDENT ALL OF THE CONFIG VALUES FOR THE SERVERNAME):
;--- Custom MSSQL server name ---
; THIS CAN BE ANY NAMED SERVER REFERENCE YOU WANT TO CREATE
[SERVERNAME]
; This is the host name of the MSSQL server
host=HOSTNAME
; This is the port number to use
port=PORTNUMBER
; This is the TDS version to use for anything over Server 2000
tds version=8.0Now we need to add the freetds library to the load library stack.
$ sudo vi /etc/ld.so.confGo to the end of the file and add the following line:
/usr/local/freetds/lib - Restart your web server
Like everything that involves a change to the PHP environment or configuration on your machine, restart the web server. I am assuming this is being built upon an apache server. If not, you will need to know how to stop and start your web server or, at the very least, know how to reboot your machine:
$ sudo apache2ctl stop
$ sudo apache2ctl startRun a PHP info page or CLI call to see if it is loaded:
$ php -mYou should now see mssql.
Again, I hope this was helpful to you. If it was, please leave a comment and let me know. It something went haywire for you, leave me a comment, too. Systems being what they are, it is never unheard of for two almost identical systems to have vastly different experiences with building software from source.
Handling multiple Sybase result sets in PHP on Ubuntu 8.10
A long time ago I was tasked with creating a PHP application that would talk to our Sybase database that drives our entire enterprise at work. This is not normally a big thing to do as one can simply enable the Sybase extension for PHP and have a database connection generally within minutes. However there was one thing that was an absolute necessity in this application that made it significantly more difficult to get it working: some parts of the application would be reliant upon stored procedures that returned multiple result sets.
First off let me say that any time you have the option to build stored procedures for your database quieries, do it. They are faster, safer, compiled and centralized and they keep your code from being littered with query builders all over the place. That said, this is neither the time nor the place to talk about the merits of stored procedures in application development. Just know that where I work there are no direct queries ever allowed to touch our database servers.
Knowing that I had to work under the requirement of handling multiple Sybase result sets I set out to find out how to do it and what I found was alarming. Few database extensions in PHP handle multiple result sets. In fact, as I was looking around it seemed that only the MySQLi extension and the SQL Server extension handled multiple result sets. But after a little searching around I found something called the "PHP Sybase CT driver enhancements" project on Sourceforge that essentially provided prepared statement handling and multiple result set handling for the PHP Sybase Extension. However, after trying to install it it became evident that the extension, as it was written, was still not usable in the state it was offered up for download in.
Not to worry, I have modified the package for compiling within a PHP 5 environment and have made a few changes to it that should allow it to be used as is. The source code archive file can be downloaded (in tar.gz format) by clicking here.
Please read the notes on this source code before installing it!
Before installation
Before we get started we need to cover a few assumptions:
- You have a licensed copy of Sybase Adaptive Server
I have not tried this against any Sybase database server other than Adaptive server. - You have the Sybase client installed or at the very least have the installer available
For this exercise we are using a Red Hat RPM of the Sybase 12.5 client. Yes, we are installing on Ubuntu, which is essentially Debian and therefore does not use RPM files, but we will burn that bridge when we come to it. - You are comfortable building PHP extensions from source
There is no way around this. In order to get this to work you will need to be able to build from source. - You have the php-devel package installed on the machine you building this extension for
If you don't have it, a quicksudo apt-get install php5-devshould do the trick. This will be necessary to build the Sybase extension from source.
Installation instructions
Ok, these are the steps I took in order to build the enhanced Sybase extension for PHP on my Ubuntu 8.10 desktop machine starting with Sybase ASE client and common packages in Red Hat RPM format.
- Convert the Sybase 12.5 OpenClient and Sybase 12.5 Common RPM archives to DEB packages and install them
To do this I used the alien package converter tool and, while inside the directory where the RPMs were living, issued the following command:
$ alien -k sybase-common-12.5.0.1de-1.i386.rpm
$ alien -k sybase-openclient-12.5.0.1esd-1.i386.rpmThis created two new archives, in the directory I was in, in debian format named
- sybase-common-12.5.0.1DE-1_i386.deb
- sybase-openclient-12.5.0.1ESD-1_i386.deb
I then issued the following commands to install them:
$ sudo dpkg --install sybase-common-12.5.0.1DE-1_i386.deb
$ sudo dpkg --install sybase-openclient-12.5.0.1ESD-1_i386.debThis made a directory named sybase-12.5 in the /opt directory.
- Prepare your environment to build the extension
To make things easier (and a little more compatible with the Red Hat way of doing things) I created a link inside of /top called sybase and pointed it to the sybase-12.5 directory.
$ cd /opt
$ ln -s -T /opt/sybase-12.5 sybaseI then had to export some environment vars that are needed by the extension and the web server in order to handle communication with the Sybase server:
$ export SYBASE=/opt/sybase
$ export SYBASE_OCS=OCS-12_5
$ export PATH=/opt/sybase/OCS-12_5/bin:$PATH
$ export LD_LIBRARY_PATH=/opt/sybase/OCS-12_5/lib:/opt/sybase/OCS-12_5/lib3p:$LD_LIBRARY_PATH
$ export INCLUDE=/opt/sybase/OCS-12_5/include:$INCLUDE
$ export LIB=/opt/sybase/OCS-12_5/lib:$LIBYes, I know that each one of these commands could have been placed in a file and sourced, but for some reason source is not available to my installation of Ubuntu so it was in fact easier and faster for me to do it this way. Do this how you will, but remember the values because you will need these later.
- Build and install the php-sybase-ct extension from source against the Sybase client you just installed
Remember that package I told you about earlier? The one that I said you could download? If not, get it now and unpack it to a directory somewhere where you have permission to unpack stuff on your system. For me, it was ~/Temp.Change to the directory you unpacked the source code to and configure it for make and installation using the php-devel package:
$ cd ~/Temp
$ phpize
$ ./configure --with-sybase-ct=$SYBASE/$SYBASE_OCS
$ make
$ sudo make installFind out where your extensions directory is on your machine and quickly check it to make sure there is a php_sybase_ct.so file living in it. On my machine the extension directory is /usr/lib/php5/20060613+lfs/. Yours may be different.
At this point you can safely do a
sudo make cleanbut you might want to hold off on that until it is all working in case you need to rebuild at all. It does happen from time to time. Just sayin'. - Configure PHP to use the new extension
Now we need to tell PHP to use the new extension we just built. To do that we need to create an ini file for the extension and put it inside of the extensions directory where PHP can find it. On my machine PHP looks for ini files to parse in /etc/php5/conf.d/ so naturally that is where I am going to go to to tell PHP to use this extension.$ cd /etc/php5/conf.d/Now we need to create an ini file and put into a directive to load the extension. You can use whatever editor you like. I prefer to use vim:
$ sudo vim sybase_ct.iniInside this file place the following two lines:
; Enable the sybase extension
extension=sybase_ct.so - Configure your environment to load the correct environment variables whenever the machine starts
This one caused me fits for a long time in Ubuntu. In order to ensure that you can use the Sybase extension from both the CLI and the web server you will need to take all of the environment variables that exported prior to building the extension and place them in both the /etc/profile startup script AND the web servers environment variable setting script. This caused countless hours of frustration and anger for me and I hope I can save you some angst with this little snippet.Add the environment vars to /etc/profile. You can use any editor you like. I like vim:
$ sudo vim /etc/profileAt the end of the file add the environment vars:
export SYBASE=/opt/sybase
export SYBASE_OCS=OCS-12_5
export PATH=/opt/sybase/OCS-12_5/bin:$PATH
export LD_LIBRARY_PATH=/opt/sybase/OCS-12_5/lib:/opt/sybase/OCS-12_5/lib3p:$LD_LIBRARY_PATH
export INCLUDE=/opt/sybase/OCS-12_5/include:$INCLUDE
export LIB=/opt/sybase/OCS-12_5/lib:$LIBNow add these same entries into your web server's environment variables. I am using apache and assuming you are to. If not, consult your web server's documentation for how to do this:
$ sudo vim /etc/apache2/envvarsNow add these entries to the end of the file:
export SYBASE=/opt/sybase
export SYBASE_OCS=OCS-12_5
export PATH=/opt/sybase/OCS-12_5/bin:$PATH
export LD_LIBRARY_PATH=/opt/sybase/OCS-12_5/lib:/opt/sybase/OCS-12_5/lib3p:$LD_LIBRARY_PATH
export INCLUDE=/opt/sybase/OCS-12_5/include:$INCLUDE
export LIB=/opt/sybase/OCS-12_5/lib:$LIB - Restart your web server
Like everything that involves a change to the PHP environment or configuration on your machine, restart the web server. I am assuming this is being built upon an apache server. If not, you will need to know how to stop and start your web server or, at the very least, know how to reboot your machine:
$ sudo apache2ctl stop
$ sudo apache2ctl startRun a PHP info page or CLI call to see if it is loaded:
$ php -mYou should see sybase_ct. If not, something went wrong. If so, you are now golden.
At this point you should be able to run queries against your Sybase server AND handle multiple result sets using the sybase_next_result() function. There is no documentation for this function, but a quick read through of the mssql_next_result() function will tell pretty well how to use it.
Enjoy! And if this was at all helpful please leave a comment to let me know.
Notes on the PHP Sybase CT Enhanced extension
Some things to keep in mind when using this extension:
- This extension is coded in PHP 4 source.
- When I got my hands on it the code was still much in development phase. Not much has changed about that other than I removed debug output from it.
- It does not handle connection failure well when the server does not respond. So unwell in fact that internally it causes segmentation faults and results in blank web pages upon failure. I do not know how to fix that.
- If you know how to program in c or want to make it better, please feel free to do so. I have contacted the original authors of this extension and of the three only one has done any real work on it and none have worked on it in the last five years and really do not intend to. I was given permission to distribute it and modify it. But I really do not know what I am doing in c. Yet.
Coffee, WiFi and coding
Today I had the pleasure of working. I know, that sounds really weird, but it is true. It was a pleasure. The reason it was pleasurable is because I was able to work outside of the home in a fairly quiet place that served all the coffee I could drink for $1.50.
Thanks to Mission Coffee and their free WiFi internet connection I was able to spend about three and half hours working on a project that I have spent entirely too much time on already but seriously needs to get done. It was clean, quiet, conducive to programming work and, well, it had coffee. Lots of it. And it was only $1.50 for never ending cup.
It was a nice feeling to get out of the house today. Usually as I am at home working I get derailed by the screams of children fighting or someone needing something. Being out meant that the distractions that I am faced with at home were virtually non-existent (save for guy that sat behind me and talked to himself in a quasi-farci-spanish mix). I had an entire table to myself, off in a little corner, where I wasn't messed with by anyone. Mostly.
After about three and half hours I was asked to move because the wall I was sitting next to was getting artwork hung on it. As soon as I stood up I realized that it was time to get home. It just seemed right to be done at the time. And I have no regrets. The day was productive, the environment was nice and the coffee was great.
So if you are ever in Fremont and need a coffee joint to sit at and spend some time on the Internet, hit up Mission Coffee. You'll be glad you did.
