Hacker News new | past | comments | ask | show | jobs | submit login
Just open sourced my 10k LOC PHP & MySQL invoicing app (github.com/tlhunter)
129 points by tlhunter on June 18, 2012 | hide | past | favorite | 97 comments



What exactly did you dislike about CodeIgniter? For me CodeIgniter is probably what I'd use if I had to go back to making PHP applications so I'd like to hear any reasons you had against it.


CI was built in the PHP4 days, and for that reason it has a lot of cruft left over. For example, there is no autoloader. Also, classes are loaded as singletons, and are assigned to properties of the controller, which I put into the 'magic' category, and isn't compatible with IDE autocomplete features (I'm a VIM user, but it makes things easier for a lot of developers).

//CI Class Load Example:

$this->load->library('example'); $this->example->something();

//Raw PHP Class Load Example:

$this->example = new Example(); $this->example->something();

As you can see, the latter is a little more obvious about what is going on.


I'm not comp sci educated, I'm all self taught so maybe "Autoload" has a real meaning outside of this context, but CI does have an autoloader... something that can autoload libraries and models? application/config/autoload.php


PHP has the ability to run a function when attempting to load a class which doesn't yet exist, which can load a file from the filesystem and get the class into memory. The CI version of autoloading is a manually entered list of classes to be loaded (a little ironic actually :p).


Very good question. I am self taught as well, and have been using CI for about 2 years now. I can autoload libs and helpers. I would love to read his answer there.

Also another question would be which frameworks is he currently using and what are its advantages.


I answered his questions directly; I'm not doing too much PHP development these days, it is mostly Node stuff.



What PHP framework would you use if you had to today? Kohana? Yii? Something else?


I'm not the OP but almost any PHP project these days has me reaching for Symfony 2[0] or her little brother Silex[1] paired with Doctrine 2[2].

[0]. http://symfony.com/ [1]. http://silex.sensiolabs.org/ [2]. http://www.doctrine-project.org/


Symfony is nice, Zend 2 is going to fix everything from ZF1 and is worth keeping an eye on as well.

I've dabbled in Doctrine before, but the memory usage looked a little too high for the convenience it offered.


Out of curiosity what are you doing in PHP where memory usage is an issue? PHP's standard life cycle, while having numerous disadvantages, avoids a lot of problems with memory usage.

It's a rather different beat, but you might checkout Propel (http://propelorm.org) for a different take on a PHP ORM. It has a static build phase unlike Doctrine which turns some people off, but if you can get past that it's a rather nice ORM.


If you think that your project is going to grow into a substantial code base, I would NOT recommend Propel.

A lot about their active record implementation violates POLA with various gotchas and code generation gets tedious and unwieldy with models that contain many fields or relations.


Interesting, I've been using Propel for so long I've probably internalized all the gotchas. What in particular caught you off guard?


My favourite is probably:

Say you want to add a product to an existing order in the database...

$orderProduct = new OrderProduct(); $order = OrderPeer::retrieveByPK(1);

$order->addOrderProduct($orderProduct);

Now, if ANYWHERE after this point in the code, something calls $order->getOrderProducts() before you call $order->save() (like a validation routine or something) you will lose that new OrderProduct and it will not be persisted.

It's bit me more than once, especially as you start to spread code around that depends on traversing the relationships - can be a very tricky bug to spot.


Propel's implementation of related collections definitely could use some improvement. In your example, you wouldn't necessarily lose your new object, it just wouldn't be persisted through a call to the parent order to save. My general habit is to call save on a newly created object directly, and as soon as possible. Regardless, I definitely agree this is one (and there are some other points) where Propel definitely has rough edges.


With this project in particular, each time someone hits the main page there are 6 php scripts executed in parallel. I was hitting some limits with my VPS during those spikes.


Yes with some complicated work patterns, Doctrine becomes memory and cpu/time hungry. I've been struggling with doctrine issues with our customers event portal. Most of the time we have to turn back into array based hydration. Even then time/cpu intensiveness is still there.


I think it's pretty important to figure out what you expect from a framework.

If you want to make it easier for other people to come on, perhaps open up to an ecosystem of open source contribution, you want a framework with broad appeal. Yii or Drupal would be good choices.

If you want a solid foundation, that you personally will benefit from, you either want something that is well engineered (perhaps even slightly over engineered) or something really minimal, that doesn't get in the way. Depending on you own experience and/or taste.


Laravel[0] is good IMO. Seems to have a good community as well.

[0] http://laravel.com/


I started using Kohana after moving on from CI, but these days, I don't do as much PHP development. It's mostly Node.js development now.

I did start rebuilding this in Kohana a few months ago with the intention of rebuilding it as a less panel-y looking interface, but ended up scrapping the project.


Consider FuelPHP (http://fuelphp.com/) - it has a decent support for PHP 5.3 and modern databases such as Redis & MongoDB.


Thanks for sharing! I wished more people would share code of projects they are no longer interested in. The educational value ('how did he/she built this') is worth a lot, regardless of the 'quality' of the code. Thank you!


I've open-sourced a number of projects I was intending to keep closed-source, but gave up on.

One of them was SMOKE[1] - simple Python/Flask site with login. The most interesting part is probably the SMF Forum signature rotator[2], which manually parses HTML[3] that is not intended to be machine-readable.

[1]https://github.com/TazeTSchnitzel/SMOKE

[2]https://github.com/TazeTSchnitzel/SMOKE/blob/master/smoke/sm...

[3]https://github.com/TazeTSchnitzel/SMOKE/blob/master/smoke/sm...


I have the exact same sentiment; I pretty much open source all of my old projects for people to learn from (I taught a small meetup for a while, it was useful to explain both the good and the bad). I add DEFUNCT to the description if they are really old or have a lot of bad practices. Here's the rest: https://github.com/tlhunter


That's an excellent idea. I should go add that to some of my horrible, horrible C code, lest someone think that 10 levels of indentation, not using c-style strings, non-portability, architecture assumptions, lack of error checking, and blocking sockets is a good idea...

Or that macros without bounds checking that cause segfaults unless debugging is a good idea...

What was I thinking?


Heh, I'm not well versed in C, but it sounds scary.

You can also throw comments in the code where the bad stuff is so that it has more visibility, as well as suggestions for how to better solve the problem. Sometimes people learn best from bad code!


Good idea.

It should sound scary, it's the worst code I've ever written.

I wrote it 2 years ago with the idea "working first, correct later", but I haven't finished the latter yet...


I've spent many an hour staring at somebody else's code. I love it, but so often when I'm in the middle of a project I totally forget about those time's I've seen code that made me go "wow, I should do that."

Am I doing something wrong? Notes or something?


i've been trying to put unused stuff on my github https://github.com/th0ma5w and i've received great comments, and people thanking me for showing general concepts and such, so that's been really great to hear, especially from crap i just had sitting around at dead ends.


I remember writing invoicing systems like this. Then I realized I probably shouldn't reinvent the wheel or break GAAP. :/


Shameless plug here kinda.....

One thing you could do is write a really good invoicing app that works with a decent GAAP accounting system, like LedgerSMB. If the system is ok, and the database underneath is solid, you can do the GAAP stuff without having to write a whole accounting package.


Yeah, there is a ton of competition. I started building this without having done proper research.


See, the thing is: creating an invoice is an 'accounting' event that needs to be thought out a bit. Like a double entry general ledger, receiving payments, proper audit trails, the inability to modify an invoice after 'posting' it, batches, periods, and other goodness.

Otherwise, you get this ugly term: embezzlement.

But kudos for opensourcing it and standing up to criticism from hecklers like me.


First there is an unspoken assumption in your post. Embezzlement can only happen when someone is handling someone else's money. In many sole proprietors, this is not the case. So for those, sure. But it's important to understand the limitations here.

But still within the core area, I think you have to accept that I would trust your books if they were kept in pencil more than I would trust books kept with software that didn't have proper controls.


I disagree, as a business should think first in the broad context of product/market fit, which is a very dynamic issue, and not all markets need the kind of depth in an accounting product you describe.


The only case where that my be true is the case where you have a sole proprietor where the owner is the only one who touches the books. As soon as you have a bookkeeper who is not the owner, or multiple owners you do need that kind of depth.


I'd bet the OP intended it for use by sole proprietors like himself, and that he never aimed to build a product for bookkeepers. There's plenty of a market for invoicing software that targets sole proprietors.

A product with as much depth would likely appeal to accountants or those that assist them directly.


I think competition was never your problem. A lack of passion for your industry/product over the long-term, is most always the high level reason people start and stop projects like this.


I spent all of my spare time for a solid nine months on this project, and you are right; I was passionate, but more about the project itself than the industry.

There was also a fear of letting this be my full time gig and not having a steady paycheck. My brick and mortar business had just closed its doors as my biggest client stopped paying people (he literally disappeared into the woods and had a class action lawsuit from many people he owed money).


I had the same happen to me, in the sense that I built a company around passion for the software project vs. the business. It really helped me see what motivations to give more and less weight to. I'd bet your next passion project is a big success.

Here's mine: http://redtagcrazy.com/blog/2011/03/28/rise-and-fall/


great to see this open sourced - sorry to hear you closed neoinvoice though

the web based invoicing market is extremely crowded and very hard to complete against the likes of freshbooks.com

though i went the opposite route to you and its working $ for me

created the open source app http://simpleinvoices.org first, got large user based, then offered premium hosting at http://smarterinvoices.com


I think at one point we had some LedgerSMB users using SimpleInvoices and importing data into LedgerSMB.

I think the difficulty with invoicing is that it is so heavily tied to everything else one might try to do in a business, from tracking inventory to CRM to financial accounting. This doesn't mean that one can't do this, but the interconnections seem to be key.


re ledgdersmb - yep - the ledgersmb crew did some great work to the SimpleInvoices code to make it work for them

re interconnections

* depending on the target market

* the people who need integration already do some form of accounting already - and a 'full stack' accounting solution like http://xero.com is more appropriate for this market

* simpleinvoices market is people with no accounting system or desire for debit/credit stuff so we have make no attempts to make quickbooks, etc. integration work


Just as a quick look, this thing is riddled w/ potential sql injections..a number of unchecked/unescaped uri controlled variables. In some cases there's validation of numerics w/ +=0, but in many cases (e.g. $sort_col) there's none.


you mean you shouldn't trust user input?


The arguments to controller methods (in CI) are passed through some regular expressions. CI goes as far as to destroy all GET variables (which I highly disagree with).


GET's are all removed (by default), but for uri segments you'll just get some character filters, and some anti-xss attempts (assuming you have that on). Nothing anywhere near sufficient to prevent sql injection. Again, didn't dig too deep, but I don't see any validation that would prevent me from doing some level of at least blindsql..


I would be excited to see an example exploit executed against the app, I've tried plenty of times without success.

Any pull request to fix a vulnerability will be happily accepted!


I think CI rejects anything in the URI which isn't alpha-numeric. Would that solve the issue?


Kind of, anything in permited_uri_chars is allowed. This includes spaces, slashes, commas, %, and a handful of others by default. As I said only skimmed quickly so maybe I'm missing it. Will take a deeper look in a bit once not on mobile.


This is the escaping mechanism I was using.


I live by the communist motto. Trust, but verify.


The routing script should be taking care of that stuff with regular expressions.

If I'm wrong, please do let me know!


Skimming the code, it looks like input is being escaped in the SQL queries. Can you link to examples?


Just skimming, and e.g.

in: controllers/invoice.php

$data['invoices'] = $this->invoice_model->select_multiple($this->session->userdata('company_id'), $page, $this->pref_user['per_page'], TRUE, $sort_col);

$sort_col appears to be just uri_segment 2 of list_items.

select_multiple() then calls:

$sql = "SELECT id, name, DATEDIFF(NOW(), duedate) AS past_due FROM invoice WHERE company_id = " . $this->db->escape($company_id) . " ORDER BY $sort_col";

$sort_col is left as-is. It is certainly more difficult, since '()' aren't permitted in the uri, and we're already in the ORDER BY clause, but I think it may still be doable to get some blindsql into there.


This is indeed a bug:

http://neoinvoice.local/invoice/list_items/1/asdf

Does cause an erroneous query. It should use a case statement to check against column names. CI will strip out any special characters though.


Ugh, yup. This code should either be taken down, or come with a HUGE warning that it needs to be audited for security vulnerabilities.


All projects which have been just open sourced are going to contain bugs. That is, until people like you find them and fix them and submit pull requests ;).

CI strips out all funky characters, so while it is possible to cause an erroneous query, I'm not seeing a security issue here.


On top of that we are all hopefully always learning. New kinds of security attacks will come along and we will have to figure out how to address them.

I would encourage you to consider trying hard to build a team around your project. In my experience open source software is hard work and really only can thrive in a community. This doesn't form magically around the software. It takes time and effort to build. If you can get good security folks in your community you can learn a lot from them.


One recommendation I have is to try to get a security guru on your team. LedgerSMB would be nowhere near as secure if it weren't for the efforts of Seneca Cunningham early on in the project. My coding and sensitivity to these issues has greatly improved.


Is that mochaUI? Looks like it.

Tried using it recently and it actually was quite nice despite the lack of docs and other various hiccups.

At any rate, thanks for putting it out.


Yeah, it's MochaUI. About two years ago, the project was mostly un-maintained, I haven't looked at it recently though.


Okay, so coming from a user of CI 2.0 mind you and using integrated smarty templates with it, I would have to say after using even a micro-framework say silex, I could build a much better enterprise application with modern php5.3/5 features than i can with CI. The major problem is it feels really dirty to include/require statements with CI. Also to build re-usable code within the confines of their existing library system you have to build all your constructors with a single $param argument. The models are junk since they are really just a place to store organized function calls. Their database abstraction is an abomination. Generally the only thing good about CI is that you can break up your source into a marginally logical fashion which can it improves maintainability.

Now as for this application being written in Code Igniter? I don't really see it as a problem since it means it will be easier for lesser experienced dev to pick up and run with the ball. In time they will learn it's major deficiencies and they will move on. But i think there is room for improvement, in fact I think this would be a great project to port to another maybe more robust framework and is a good project for learning new framework translations.

I will say however that the author wanting to rewrite a product like this in nodejs is really only going to make something like this more inherently difficult to maintain since javascript code tends to get more complicated than your typical java / php / python app as it grows. Granted you do have js at your finger tips it is still largely un-tested on massive scale for large application development. It however has shown great promise for handling extreme traffic in terms of concurrency, but it handles it mostly by using async libraries which pile everything into queues of some sort some where. So your code is littered with async callbacks everywhere. Anyways.. Not only that, but the biggest problem of all is the inability to intelligently step through your code with some kind of IDE beyond using a browser-base debuger such as webkit inspector or the online IDE cloud 9.

I don't hate nodejs. I just think people are jumping on the band wagon way too quickly for server-side hosting. For websockets it's a great solution, I just don't like seeing it as "the only solution" for some peoples projects. I'd rather see a more hybrid approach.


I'm curious, I remember a posting a while back that you were trying to sell this app through Flippa? Any details of that experience you would mind sharing (nothing but tire kickers, not the right market, buyer fall thru, etc...)?


Long story short, not a single bid. A lot of people would ask some questions, but would seem to ignore the info on the page. I didn't bother paying extra to have them tweet the site on their twitter, since they tweet seemingly hundreds of times a day and nobody seems to listen.

I'm used to selling items on the Envato marketplace, which provides all of the 'advertising' for me. But it seems that with Flippa, there is so much noise and one has to do their own finding of buyers.


I didn't look through the PHP or MySQL code but the front-end has a clean look (from the video), which is more than is expected from most people who take it upon themselves to develop an invoicing system


Thanks! The public facing layout went through three major revisions and the final one is based on 960.gs. The layout of the app is governed by MochaUI, but it is a custom theme.


How many users did you have?


There was only about 120 users at its peak. I hesitated, was never ready to make it a real money-making business, and therefor didn't really advertise it.


I've a few questions if you have the time.

Did you never charge? Or were you charging? What were the reasons it was / you were not ready to make it a money making business? Any mistakes or lessons learned while building/running the software? Where did the 120 users come from?

As someone who is looking to create their own product soon I'd be really grateful for any response you can give.

Thanks

Alan


For a few months I had the ability for users to sign up for a paid account, but I never pushed it. After that, I just disabled the feature altogether.

The most advertising I did do was a 125x125 ad on a website used by web developers (DavidWalsh.name) for one month. I didn't get a single user through that ad though.

The biggest lesson learned is that an app like this really needs someone who is good at marketing. I'm just a programmer, but if I had found a marketer and pursued the development of the app, I'm pretty sure it could have been successful.

Another take away is to research competitors. I never looked them up since I actually built this mostly for myself at first, and just kept adding new features as it got bigger. It turned out the market was very crowded (This page has a list of 17 competitors: http://thomashunter.name/blog/shutting-down-and-open-sourcin...).

Also, "piss or get off the pot". I spent a lot of time developing this but I didn't devote enough of my time to it for it to be successful.


I wouldn't describe a market with 17 competitors as crowded. Maybe others would disagree. I'm building an invoicing app for a niche market so I'm probably biased.


Thank you very much for taking the time to reply!

Also I know you're getting a lot of stick in this thread for the code which I feel is unjustified, I just want to say I commend you for open sourcing it.


Thanks for the encouragement! I get flack from everything I open, must be a sign.


It's a sign that people are interested in,and paying attention to your code. Take it as a compliment!


What would you do different based on what you know now?


While I was at a startup recently, I did all of my frontend development in Backbone.js, this app would do a lot better in that framework.

Also, the CI framework isn't all that great, I would have certainly used a different framework. Perhaps even Node.js (which is what I'm doing a lot of my development in nowadays).

I also would have looked for a marketer and devoted more time to the project. The market for this kind of product is very crowded, but there is a lot of room for innovation.


I'm curious why you would choose Node.js instead today. I've done a little Node.js work, and I would unequivocally use it again for anything that needs a fair amount of concurrency, or high volume, lightweight requests. Other than that though, the only advantage I see over other server side stacks is a slight mental bonus in using the same language on both the client and server? Are there any other major advantages you see over PHP/Python/Ruby,etc. stacks?


I don't think anyone's ever subscribed to a SaaS product because it used X technology over Y. I also think you're way too focused on the tech and the list of features, instead of selling a solution.

You can say all you want about invoicing being an over saturated market, but I launched a very successful project management product a few months ago. Focus on doing a few things very well, and you can do well in just about any "saturated" B2B space.


Spot on!


This looks like you put a lot of work into this. Thanks for open sourcing it.


I can't believe you hardcoded this for MySQL instead of using PHP's excellent and versatile PDOs, and I can't believe the 10k line count. Gosh. Is "de-engineering" a suitable term here? Thumbs up for open sourcing your work, though.


This is the sort of comment that keeps a lot of people from publishing their source code.

Make a pull request, sir.


I built it using CodeIgniter, and wasn't as proficient in PHP as I am now. CI is a beginner framework and doesn't teach a lot of good practices (e.g. it loads external classes as singletons and uses a class loading script developed during the PHP4 days).

Here is the suggested CI method for executing database queries: http://codeigniter.com/user_guide/database/examples.html

Nowadays, I prefer (PHP) frameworks with real autoloaders and better OOP usage (like Zend or Kohana).


Please can you point me to documentation that shows that you can easily switch vendor when using PDO? The PHP docs clearly state database abstraction isn't purpose of PDO. The purpose of PDO is to abstract access to a database.

http://www.php.net/manual/en/intro.pdo.php

In this sense, wouldn't it have been better to either recommend that he should have used a DBA library? Or even recommend that he used PDO in place of CodeIgniter's own DB access library?

The OP's experience aside, I think you're also missing that this was written on top of CodeIgniter. From my own experience and learning, CodeIgniter isn't really the best tool for writing larger applications. It's architecture isn't so great and is largely a remanent of the past. To end a rant about CodeIgniter short, it seems more natural to write namespaced procedures using classes rather than taking full advantage of objects and OO (again, probably due to it being largely a PHP4 framework).


Really disappointing to see someone's work get trashed. Rock on for open sourcing it.


"markhaus" has -15 karma. I think his goal is to accumulate negative karma. That or he is just a real jerk.

Plus, I really disagree with his comment.


No I'm serious here, renownedmedia. Don't disregard my rant with a silly downvote, but do instead indulge yourself in the PDO methods instead of painting yourself and your projects into corners: http://php.net/manual/en/book.pdo.php


What makes you think he was the one who downvoted you? You could easily have asked him about his technical decisions without being a jerk.

Besides, this was a personal project, probably created without the intention of eventually open sourcing it. If he only ever intended to use MySQL, and portability across RDBMS systems wasn't a goal, then it's fine as it is.


You hit the nail on the head. I never knew I would open source this, I didn't even plan on it getting as feature-rich as it had become.


You can't downvote comments that are a reply to your own item, the person that downvoted you is not the OP -- unless he used an alternative account. I would suspect someone else did, the tone of your comment isn't one that really fits with the HN environment even if your point is valid.


You probably got down voted because you seem like a dick, rather than a helpful, nice, encouraging person.


Having just converted an app to use Postgres from MySQL, I can tell you that the PDO drivers only get you so far. You're still ultimately going to be writing SQL, and then you have to get into the differences between MySQL's `key`="val" and Postgres' "key"='val' quoting differences.


It wasn't me who downvoted you, I promise! I love PDO; please read my other comment.


Your very first comment on HN got a down-vote, I haven't actually seen a -5 karma before. On the bright side you've nowhere to go but up.

Seriously though I agree with your point about PDO. Perhaps the down-vote was due to the comment about de-engineering and apparently making fun of 10k LOC?

I took a look at the app and it's got a huge amount of features. 10k is not really that much code for a "enterprise" app I suppose you could call it. The OP probably is just trying to express the amount of labor that went into the app. Unfortunately other developers may judge your LOC as either a positive or negative, so it's probably best to avoid mentioning it (unless you've written an app in an unbelievably low number of lines).


It was the first metric that came to mind, it was hard to describe in time since development was on and off for a while. I just wanted people to know it wasn't a small project.


It actually looks like a nice product. I can tell from your video that you feel about the same way I do of many of my projects. Even after a few months it's hard for me to look at my work and not see the flaws and missing features. But it's good to keep in mind that people who aren't building the app don't usually see those things.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: